Improve support for on screen length calculation

The computation of length on screen depend in particular of the computation of the size of an em. Many places of the code used to rely on the width of the M character, which is not really correct:
http://en.wikipedia.org/wiki/Em_%28typography%29

In digital typography, the best value to use is the point size of the font.

* Implement FontMetrics::em(), which returns the value in pixels of the EM unit.
 Convert code to use it.

* Introduce Length::inPixel(MetricsBase const &), which takes the textwidth and em information from the MetricsBase object. Convert code to use it.

* Fix several places where Length::inPixel is used without a proper em value.

* add mathed_font_em() helper function. It should eventually be removed like some other functions in MathSupport.

* Add dummy implementation of FontMetrics to tex2lyx for linking purposes.
This commit is contained in:
Jean-Marc Lasgouttes 2015-03-26 16:55:19 +01:00
parent 67e6c45f80
commit 66fa801e74
20 changed files with 83 additions and 35 deletions

View File

@ -3000,7 +3000,7 @@ void BufferView::checkCursorScrollOffset(PainterInfo & pi)
// Horizontal scroll offset of the cursor row in pixels // Horizontal scroll offset of the cursor row in pixels
int offset = d->horiz_scroll_offset_; int offset = d->horiz_scroll_offset_;
int const MARGIN = Length(2, Length::EM).inPixels(workWidth()); int const MARGIN = Length(2, Length::EM).inPixels(pi.base);
if (cur_x < offset + MARGIN) { if (cur_x < offset + MARGIN) {
// scroll right // scroll right
offset = cur_x - MARGIN; offset = cur_x - MARGIN;

View File

@ -17,6 +17,9 @@
#include "Length.h" #include "Length.h"
#include "LyXRC.h" #include "LyXRC.h"
#include "MetricsInfo.h"
#include "frontends/FontMetrics.h"
#include "support/docstream.h" #include "support/docstream.h"
@ -197,7 +200,7 @@ int Length::inPixels(int text_width, int em_width_base) const
? em_width_base ? em_width_base
: 10*(dpi/72.27)*zoom; : 10*(dpi/72.27)*zoom;
// A different estimate for em_width is // A different estimate for em_width is
// theFontMetrics(FontInfo(sane_font)).width('M') // theFontMetrics(FontInfo(sane_font)).em()
// but this estimate might not be more accurate as the screen font // but this estimate might not be more accurate as the screen font
// is different then the latex font. // is different then the latex font.
@ -288,6 +291,12 @@ int Length::inPixels(int text_width, int em_width_base) const
} }
int Length::inPixels(MetricsBase const & base) const
{
return inPixels(base.textwidth, theFontMetrics(base.font).em());
}
int Length::inBP() const int Length::inBP() const
{ {
// return any Length value as a one with // return any Length value as a one with

View File

@ -20,6 +20,8 @@
namespace lyx { namespace lyx {
class MetricsBase;
// Solaris/x86 version 9 and earlier define these // Solaris/x86 version 9 and earlier define these
#undef PC #undef PC
#undef SP #undef SP
@ -87,8 +89,18 @@ public:
std::string const asLatexString() const; std::string const asLatexString() const;
/// return string representation for HTML /// return string representation for HTML
std::string const asHTMLString() const; std::string const asHTMLString() const;
/// return the on-screen size of this length /** return the on-screen size of this length.
*
* If the second argument is not provided, then the unit EM will
* only be approximated. It is better if possible to use
* FontMetrics::em() to get this value.
*/
int inPixels(int text_width, int em_width = 0) const; int inPixels(int text_width, int em_width = 0) const;
/** return the on-screen size of this length
*
* This version of the function uses the right EM definition.
*/
int inPixels(MetricsBase const &) const;
/// return the value in Big Postscript points. /// return the value in Big Postscript points.
int inBP() const; int inBP() const;

View File

@ -1736,7 +1736,7 @@ int TextMetrics::leftMargin(int max_width,
} }
if (!par.params().leftIndent().zero()) if (!par.params().leftIndent().zero())
l_margin += par.params().leftIndent().inPixels(max_width); l_margin += par.params().leftIndent().inPixels(max_width, labelfont_metrics.em());
LyXAlignment align; LyXAlignment align;

View File

@ -63,6 +63,9 @@ public:
/// return default dimension of the font. /// return default dimension of the font.
/// \warning \c width is set to zero. /// \warning \c width is set to zero.
virtual Dimension const defaultDimension() const = 0; virtual Dimension const defaultDimension() const = 0;
/// return the em size
virtual int em() const = 0;
/// return the width of the char in the font /// return the width of the char in the font
virtual int width(char_type c) const = 0; virtual int width(char_type c) const = 0;
/// return the ascent of the char in the font /// return the ascent of the char in the font

View File

@ -72,6 +72,12 @@ int GuiFontMetrics::maxDescent() const
} }
int GuiFontMetrics::em() const
{
return QFontInfo(font_).pixelSize();
}
int GuiFontMetrics::lbearing(char_type c) const int GuiFontMetrics::lbearing(char_type c) const
{ {
if (!is_utf16(c)) if (!is_utf16(c))

View File

@ -35,6 +35,7 @@ public:
virtual int maxAscent() const; virtual int maxAscent() const;
virtual int maxDescent() const; virtual int maxDescent() const;
virtual Dimension const defaultDimension() const; virtual Dimension const defaultDimension() const;
virtual int em() const;
virtual int width(char_type c) const; virtual int width(char_type c) const;
virtual int ascent(char_type c) const; virtual int ascent(char_type c) const;
virtual int descent(char_type c) const; virtual int descent(char_type c) const;

View File

@ -171,7 +171,7 @@ void InsetBox::metrics(MetricsInfo & m, Dimension & dim) const
// back up textwidth. // back up textwidth.
int textwidth_backup = m.base.textwidth; int textwidth_backup = m.base.textwidth;
if (hasFixedWidth()) if (hasFixedWidth())
m.base.textwidth = params_.width.inPixels(m.base.textwidth); m.base.textwidth = params_.width.inPixels(m.base);
InsetCollapsable::metrics(m, dim); InsetCollapsable::metrics(m, dim);
// retore textwidth. // retore textwidth.
m.base.textwidth = textwidth_backup; m.base.textwidth = textwidth_backup;

View File

@ -111,7 +111,7 @@ void InsetLine::metrics(MetricsInfo & mi, Dimension & dim) const
int const max_width = mi.base.textwidth; int const max_width = mi.base.textwidth;
Length const width(to_ascii(getParam("width"))); Length const width(to_ascii(getParam("width")));
dim.wid = width.inPixels(max_width, fm.width(char_type('M'))); dim.wid = width.inPixels(mi.base);
// assure that the line inset is not outside of the window // assure that the line inset is not outside of the window
// check that it doesn't exceed the outer boundary // check that it doesn't exceed the outer boundary
@ -123,11 +123,11 @@ void InsetLine::metrics(MetricsInfo & mi, Dimension & dim) const
dim.wid = max(minw, abs(dim.wid)); dim.wid = max(minw, abs(dim.wid));
Length height = Length(to_ascii(getParam("height"))); Length height = Length(to_ascii(getParam("height")));
height_ = height.inPixels(max_width, fm.width(char_type('M'))); height_ = height.inPixels(mi.base);
// get the length of the parameters in pixels // get the length of the parameters in pixels
Length offset = Length(to_ascii(getParam("offset"))); Length offset = Length(to_ascii(getParam("offset")));
offset_ = offset.inPixels(max_width, fm.width(char_type('M'))); offset_ = offset.inPixels(mi.base);
dim.asc = max(fm.maxAscent(), offset_ + height_); dim.asc = max(fm.maxAscent(), offset_ + height_);
dim.des = max(fm.maxDescent(), - offset_); dim.des = max(fm.maxDescent(), - offset_);

View File

@ -209,19 +209,20 @@ void InsetSpace::metrics(MetricsInfo & mi, Dimension & dim) const
frontend::FontMetrics const & fm = theFontMetrics(mi.base.font); frontend::FontMetrics const & fm = theFontMetrics(mi.base.font);
dim.asc = fm.maxAscent(); dim.asc = fm.maxAscent();
dim.des = fm.maxDescent(); dim.des = fm.maxDescent();
int const em = fm.em();
switch (params_.kind) { switch (params_.kind) {
case InsetSpaceParams::THIN: case InsetSpaceParams::THIN:
case InsetSpaceParams::NEGTHIN: case InsetSpaceParams::NEGTHIN:
dim.wid = fm.width(char_type('M')) / 6; dim.wid = em / 6;
break; break;
case InsetSpaceParams::MEDIUM: case InsetSpaceParams::MEDIUM:
case InsetSpaceParams::NEGMEDIUM: case InsetSpaceParams::NEGMEDIUM:
dim.wid = fm.width(char_type('M')) / 4; dim.wid = em / 4;
break; break;
case InsetSpaceParams::THICK: case InsetSpaceParams::THICK:
case InsetSpaceParams::NEGTHICK: case InsetSpaceParams::NEGTHICK:
dim.wid = fm.width(char_type('M')) / 2; dim.wid = em / 2;
break; break;
case InsetSpaceParams::PROTECTED: case InsetSpaceParams::PROTECTED:
case InsetSpaceParams::VISIBLE: case InsetSpaceParams::VISIBLE:
@ -229,20 +230,19 @@ void InsetSpace::metrics(MetricsInfo & mi, Dimension & dim) const
dim.wid = fm.width(char_type(' ')); dim.wid = fm.width(char_type(' '));
break; break;
case InsetSpaceParams::QUAD: case InsetSpaceParams::QUAD:
dim.wid = fm.width(char_type('M')); dim.wid = em;
break; break;
case InsetSpaceParams::QQUAD: case InsetSpaceParams::QQUAD:
dim.wid = 2 * fm.width(char_type('M')); dim.wid = 2 * em;
break; break;
case InsetSpaceParams::ENSPACE: case InsetSpaceParams::ENSPACE:
case InsetSpaceParams::ENSKIP: case InsetSpaceParams::ENSKIP:
dim.wid = int(0.5 * fm.width(char_type('M'))); dim.wid = int(0.5 * em);
break; break;
case InsetSpaceParams::CUSTOM: case InsetSpaceParams::CUSTOM:
case InsetSpaceParams::CUSTOM_PROTECTED: { case InsetSpaceParams::CUSTOM_PROTECTED: {
int const w = int const w =
params_.length.len().inPixels(mi.base.textwidth, params_.length.len().inPixels(mi.base);
fm.width(char_type('M')));
int const minw = (w < 0) ? 3 * arrow_size : 4; int const minw = (w < 0) ? 3 * arrow_size : 4;
dim.wid = max(minw, abs(w)); dim.wid = max(minw, abs(w));
break; break;

View File

@ -47,7 +47,7 @@ namespace {
int logoWidth(FontInfo const & font, InsetSpecialChar::Kind kind) { int logoWidth(FontInfo const & font, InsetSpecialChar::Kind kind) {
frontend::FontMetrics const & fm = theFontMetrics(font); frontend::FontMetrics const & fm = theFontMetrics(font);
int const em = fm.width('M'); int const em = fm.em();
int width = 0; int width = 0;
// See drawlogo() below to understand what this does. // See drawlogo() below to understand what this does.
switch (kind) { switch (kind) {
@ -137,9 +137,7 @@ namespace {
void drawLogo(PainterInfo & pi, InsetSpecialChar::Kind kind, int & x, int & y) { void drawLogo(PainterInfo & pi, InsetSpecialChar::Kind kind, int & x, int & y) {
FontInfo const & font = pi.base.font; FontInfo const & font = pi.base.font;
// FIXME: this definition of em is bogus, but there is a need int const em = theFontMetrics(font).em();
// for a big refactoring of the code around this issue anyway.
int const em = theFontMetrics(font).width('M');
switch (kind) { switch (kind) {
case InsetSpecialChar::PHRASE_LYX: case InsetSpecialChar::PHRASE_LYX:
/** Reference macro: /** Reference macro:

View File

@ -3591,7 +3591,7 @@ void InsetTabular::metrics(MetricsInfo & mi, Dimension & dim) const
MetricsInfo m = mi; MetricsInfo m = mi;
Length const p_width = tabular.getPWidth(cell); Length const p_width = tabular.getPWidth(cell);
if (!p_width.zero()) if (!p_width.zero())
m.base.textwidth = p_width.inPixels(mi.base.textwidth); m.base.textwidth = p_width.inPixels(mi.base);
tabular.cellInset(cell)->metrics(m, dim); tabular.cellInset(cell)->metrics(m, dim);
if (!p_width.zero()) if (!p_width.zero())
dim.wid = m.base.textwidth; dim.wid = m.base.textwidth;
@ -3652,11 +3652,11 @@ void InsetTabular::metrics(MetricsInfo & mi, Dimension & dim) const
} }
int const top_space = tabular.row_info[r].top_space_default ? int const top_space = tabular.row_info[r].top_space_default ?
default_line_space : default_line_space :
tabular.row_info[r].top_space.inPixels(mi.base.textwidth); tabular.row_info[r].top_space.inPixels(mi.base);
tabular.setRowAscent(r, maxasc + ADD_TO_HEIGHT + top_space); tabular.setRowAscent(r, maxasc + ADD_TO_HEIGHT + top_space);
int const bottom_space = tabular.row_info[r].bottom_space_default ? int const bottom_space = tabular.row_info[r].bottom_space_default ?
default_line_space : default_line_space :
tabular.row_info[r].bottom_space.inPixels(mi.base.textwidth); tabular.row_info[r].bottom_space.inPixels(mi.base);
tabular.setRowDescent(r, maxdes + ADD_TO_HEIGHT + bottom_space); tabular.setRowDescent(r, maxdes + ADD_TO_HEIGHT + bottom_space);
} }

View File

@ -75,7 +75,7 @@ void InsetMathChar::metrics(MetricsInfo & mi, Dimension & dim) const
dim = fm.dimension(char_); dim = fm.dimension(char_);
kerning_ = fm.rbearing(char_) - dim.wid; kerning_ = fm.rbearing(char_) - dim.wid;
} }
int const em = mathed_char_width(mi.base.font, 'M'); int const em = mathed_font_em(mi.base.font);
if (isBinaryOp(char_)) if (isBinaryOp(char_))
dim.wid += static_cast<int>(0.5*em+0.5); dim.wid += static_cast<int>(0.5*em+0.5);
else if (char_ == '\'') else if (char_ == '\'')
@ -93,7 +93,7 @@ void InsetMathChar::metrics(MetricsInfo & mi, Dimension & dim) const
void InsetMathChar::draw(PainterInfo & pi, int x, int y) const void InsetMathChar::draw(PainterInfo & pi, int x, int y) const
{ {
//lyxerr << "drawing '" << char_ << "' font: " << pi.base.fontname << endl; //lyxerr << "drawing '" << char_ << "' font: " << pi.base.fontname << endl;
int const em = mathed_char_width(pi.base.font, 'M'); int const em = mathed_font_em(pi.base.font);
if (isBinaryOp(char_)) if (isBinaryOp(char_))
x += static_cast<int>(0.25*em+0.5); x += static_cast<int>(0.25*em+0.5);
else if (char_ == '\'') else if (char_ == '\'')

View File

@ -28,7 +28,6 @@
#include "FuncRequest.h" #include "FuncRequest.h"
#include "frontends/Clipboard.h" #include "frontends/Clipboard.h"
#include "frontends/FontMetrics.h"
#include "frontends/Painter.h" #include "frontends/Painter.h"
#include "support/debug.h" #include "support/debug.h"
@ -98,9 +97,7 @@ InsetMathGrid::RowInfo::RowInfo()
int InsetMathGrid::RowInfo::skipPixels(MetricsInfo const & mi) const int InsetMathGrid::RowInfo::skipPixels(MetricsInfo const & mi) const
{ {
frontend::FontMetrics const & fm = theFontMetrics(mi.base.font); return crskip_.inPixels(mi.base);
return crskip_.inPixels(mi.base.textwidth,
fm.width(char_type('M')));
} }

View File

@ -47,7 +47,7 @@ void InsetMathKern::metrics(MetricsInfo & mi, Dimension & dim) const
{ {
dim.asc = 0; dim.asc = 0;
dim.des = 0; dim.des = 0;
dim.wid = wid_.inPixels(0, mathed_char_width(mi.base.font, 'M')); dim.wid = wid_.inPixels(mi.base);
} }

View File

@ -123,9 +123,7 @@ void InsetMathSpace::metrics(MetricsInfo & mi, Dimension & dim) const
dim.asc = 4; dim.asc = 4;
dim.des = 0; dim.des = 0;
if (space_info[space_].custom) if (space_info[space_].custom)
dim.wid = abs(length_.inPixels( dim.wid = abs(length_.inPixels(mi.base));
mi.base.textwidth,
mathed_char_width(mi.base.font, 'M')));
else else
dim.wid = space_info[space_].width; dim.wid = space_info[space_].width;
} }

View File

@ -67,7 +67,7 @@ void InsetMathSymbol::metrics(MetricsInfo & mi, Dimension & dim) const
sym_->extra == "mathalpha" && sym_->extra == "mathalpha" &&
mi.base.fontname == "mathit"; mi.base.fontname == "mathit";
std::string const font = italic_upcase_greek ? "cmm" : sym_->inset; std::string const font = italic_upcase_greek ? "cmm" : sym_->inset;
int const em = mathed_char_width(mi.base.font, 'M'); int const em = mathed_font_em(mi.base.font);
FontSetChanger dummy(mi.base, from_ascii(font)); FontSetChanger dummy(mi.base, from_ascii(font));
mathed_string_dim(mi.base.font, sym_->draw, dim); mathed_string_dim(mi.base.font, sym_->draw, dim);
docstring::const_reverse_iterator rit = sym_->draw.rbegin(); docstring::const_reverse_iterator rit = sym_->draw.rbegin();
@ -104,7 +104,7 @@ void InsetMathSymbol::draw(PainterInfo & pi, int x, int y) const
sym_->extra == "mathalpha" && sym_->extra == "mathalpha" &&
pi.base.fontname == "mathit"; pi.base.fontname == "mathit";
std::string const font = italic_upcase_greek ? "cmm" : sym_->inset; std::string const font = italic_upcase_greek ? "cmm" : sym_->inset;
int const em = mathed_char_width(pi.base.font, 'M'); int const em = mathed_font_em(pi.base.font);
if (isRelOp()) if (isRelOp())
x += static_cast<int>(0.25*em+0.5); x += static_cast<int>(0.25*em+0.5);
else else

View File

@ -501,6 +501,12 @@ deco_struct const * search_deco(docstring const & name)
} // namespace anon } // namespace anon
int mathed_font_em(FontInfo const & font)
{
return theFontMetrics(font).em();
}
int mathed_char_width(FontInfo const & font, char_type c) int mathed_char_width(FontInfo const & font, char_type c)
{ {
return theFontMetrics(font).width(c); return theFontMetrics(font).width(c);

View File

@ -27,6 +27,8 @@ class MathAtom;
class InsetMath; class InsetMath;
int mathed_font_em(FontInfo const &);
int mathed_char_width(FontInfo const &, char_type c); int mathed_char_width(FontInfo const &, char_type c);
int mathed_char_kerning(FontInfo const &, char_type c); int mathed_char_kerning(FontInfo const &, char_type c);

View File

@ -121,6 +121,22 @@ string alignmentToCSS(LyXAlignment)
return string(); return string();
} }
//
// Dummy FontMetrics (needed by Length)
//
class FontMetrics {
int em() const { return 0; };
};
class FontInfo;
FontMetrics const & theFontMetrics(FontInfo const &) {
static FontMetrics dummy;
return dummy;
}
// //
// Keep the linker happy on Windows // Keep the linker happy on Windows
// //