| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| Expand Up | @@ -306,6 +306,12 @@ def render_rect_filled(self, output: Output, | |
| """ | ||
| output.rects.append((x, y, w, h)) | ||
|
|
||
| def get_axis_height(self, font: str, fontsize: float, dpi: float) -> float: | ||
| """ | ||
| Get the axis height for the given *font* and *fontsize*. | ||
| """ | ||
| raise NotImplementedError() | ||
|
|
||
| def get_xheight(self, font: str, fontsize: float, dpi: float) -> float: | ||
| """ | ||
| Get the xheight for the given *font* and *fontsize*. | ||
| Expand Down Expand Up | @@ -407,17 +413,19 @@ def _get_info(self, fontname: str, font_class: str, sym: str, fontsize: float, | |
| offset=offset | ||
| ) | ||
|
|
||
| def get_axis_height(self, fontname: str, fontsize: float, dpi: float) -> float: | ||
| # The fraction line (if present) must be aligned with the minus sign. Therefore, | ||
| # the height of the latter from the baseline is the axis height. | ||
| metrics = self.get_metrics( | ||
| fontname, mpl.rcParams['mathtext.default'], '\u2212', fontsize, dpi) | ||
| return (metrics.ymax + metrics.ymin) / 2 | ||
|
|
||
| def get_xheight(self, fontname: str, fontsize: float, dpi: float) -> float: | ||
| font = self._get_font(fontname) | ||
| font.set_size(fontsize, dpi) | ||
| pclt = font.get_sfnt_table('pclt') | ||
| if pclt is None: | ||
| # Some fonts don't store the xHeight, so we do a poor man's xHeight | ||
| metrics = self.get_metrics( | ||
| fontname, mpl.rcParams['mathtext.default'], 'x', fontsize, dpi) | ||
| return metrics.iceberg | ||
| x_height = (pclt['xHeight'] / 64) * (fontsize / 12) * (dpi / 100) | ||
| return x_height | ||
| # Some fonts report the wrong x-height, while some don't store it, so | ||
| # we do a poor man's x-height. | ||
| metrics = self.get_metrics( | ||
| fontname, mpl.rcParams['mathtext.default'], 'x', fontsize, dpi) | ||
| return metrics.iceberg | ||
|
|
||
| def get_underline_thickness(self, font: str, fontsize: float, dpi: float) -> float: | ||
| # This function used to grab underline thickness from the font | ||
| Expand Down Expand Up | @@ -895,7 +903,10 @@ class FontConstantsBase: | |
| # Percentage of x-height of additional horiz. space after sub/superscripts | ||
| script_space: T.ClassVar[float] = 0.05 | ||
|
|
||
| # Percentage of x-height that sub/superscripts drop below the baseline | ||
| # Percentage of x-height that superscripts drop below the top of large box | ||
| supdrop: T.ClassVar[float] = 0.4 | ||
|
|
||
| # Percentage of x-height that subscripts drop below the bottom of large box | ||
| subdrop: T.ClassVar[float] = 0.4 | ||
|
|
||
| # Percentage of x-height that superscripts are raised from the baseline | ||
| Expand All | @@ -921,40 +932,109 @@ class FontConstantsBase: | |
| # integrals | ||
| delta_integral: T.ClassVar[float] = 0.1 | ||
|
|
||
| # Percentage of x-height the numerator is shifted up in display style. | ||
| num1: T.ClassVar[float] = 1.4 | ||
|
|
||
| # Percentage of x-height the numerator is shifted up in text, script and | ||
| # scriptscript styles if there is a fraction line. | ||
| num2: T.ClassVar[float] = 1.5 | ||
|
|
||
| # Percentage of x-height the numerator is shifted up in text, script and | ||
| # scriptscript styles if there is no fraction line. | ||
| num3: T.ClassVar[float] = 1.3 | ||
|
|
||
| # Percentage of x-height the denominator is shifted down in display style. | ||
| denom1: T.ClassVar[float] = 1.3 | ||
|
|
||
| # Percentage of x-height the denominator is shifted down in text, script | ||
| # and scriptscript styles. | ||
| denom2: T.ClassVar[float] = 1.1 | ||
|
|
||
|
|
||
| class ComputerModernFontConstants(FontConstantsBase): | ||
| script_space = 0.075 | ||
| subdrop = 0.2 | ||
| sup1 = 0.45 | ||
| sub1 = 0.2 | ||
| sub2 = 0.3 | ||
| delta = 0.075 | ||
| # Previously, the x-height of Computer Modern was obtained from the font | ||
| # table. However, that x-height was greater than the the actual (rendered) | ||
| # x-height by a factor of 1.771484375 (at font size 12, DPI 100 and hinting | ||
| # type 32). Now that we're using the rendered x-height, some font constants | ||
| # have been increased by the same factor to compensate. | ||
| script_space = 0.132861328125 | ||
| delta = 0.132861328125 | ||
| delta_slanted = 0.3 | ||
| delta_integral = 0.3 | ||
| _x_height = 451470 | ||
| # These all come from the cmsy10.tfm metrics, divided by the design xheight from | ||
| # there, since we multiply these values by the scaled xheight later. | ||
| supdrop = 404864 / _x_height | ||
| subdrop = 52429 / _x_height | ||
| sup1 = 432949 / _x_height | ||
| sub1 = 157286 / _x_height | ||
| sub2 = 259226 / _x_height | ||
| num1 = 709370 / _x_height | ||
| num2 = 412858 / _x_height | ||
| num3 = 465286 / _x_height | ||
| denom1 = 719272 / _x_height | ||
| denom2 = 361592 / _x_height | ||
|
|
||
|
|
||
| class STIXFontConstants(FontConstantsBase): | ||
| script_space = 0.1 | ||
| sup1 = 0.8 | ||
| sub2 = 0.6 | ||
| delta = 0.05 | ||
| delta_slanted = 0.3 | ||
| delta_integral = 0.3 | ||
|
|
||
|
|
||
| class STIXSansFontConstants(FontConstantsBase): | ||
| # These values are extracted from the TeX table of STIXGeneral.ttf using FreeType, | ||
|
Comment thread
Copy link
Copy Markdown
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityThis should have said FontForge, not FreeType; I'll fix it in a followup.
Sorry, something went wrong.
All reactions
|
||
| # and then divided by design xheight, since we multiply these values by the scaled | ||
| # xheight later. | ||
| _x_height = 450 | ||
| supdrop = 386 / _x_height | ||
| subdrop = 50.0002 / _x_height | ||
| sup1 = 413 / _x_height | ||
| sub1 = 150 / _x_height | ||
| sub2 = 309 / _x_height | ||
| num1 = 747 / _x_height | ||
| num2 = 424 / _x_height | ||
| num3 = 474 / _x_height | ||
| denom1 = 756 / _x_height | ||
| denom2 = 375 / _x_height | ||
|
|
||
|
|
||
| class STIXSansFontConstants(STIXFontConstants): | ||
| script_space = 0.05 | ||
| sup1 = 0.8 | ||
| delta_slanted = 0.6 | ||
| delta_integral = 0.3 | ||
|
|
||
|
|
||
| class DejaVuSerifFontConstants(FontConstantsBase): | ||
| pass | ||
| # These values are extracted from the TeX table of DejaVuSerif.ttf using FreeType, | ||
| # and then divided by design xheight, since we multiply these values by the scaled | ||
| # xheight later. | ||
| _x_height = 1063 | ||
| supdrop = 790.527 / _x_height | ||
| subdrop = 102.4 / _x_height | ||
| sup1 = 845.824 / _x_height | ||
| sub1 = 307.199 / _x_height | ||
| sub2 = 632.832 / _x_height | ||
| num1 = 1529.86 / _x_height | ||
| num2 = 868.352 / _x_height | ||
| num3 = 970.752 / _x_height | ||
| denom1 = 1548.29 / _x_height | ||
| denom2 = 768 / _x_height | ||
|
|
||
|
|
||
| class DejaVuSansFontConstants(FontConstantsBase): | ||
| pass | ||
| # These values are extracted from the TeX table of DejaVuSans.ttf using FreeType, | ||
| # and then divided by design xheight, since we multiply these values by the scaled | ||
| # xheight later. | ||
| _x_height = 1120 | ||
| supdrop = 790.527 / _x_height | ||
| subdrop = 102.4 / _x_height | ||
| sup1 = 845.824 / _x_height | ||
| sub1 = 307.199 / _x_height | ||
| sub2 = 632.832 / _x_height | ||
| num1 = 1529.86 / _x_height | ||
| num2 = 868.352 / _x_height | ||
| num3 = 970.752 / _x_height | ||
| denom1 = 1548.29 / _x_height | ||
| denom2 = 768 / _x_height | ||
|
|
||
|
|
||
| # Maps font family names to the FontConstantBase subclass to use | ||
| Expand Down Expand Up | @@ -1015,6 +1095,15 @@ def shrink(self) -> None: | |
| def render(self, output: Output, x: float, y: float) -> None: | ||
| """Render this node.""" | ||
|
|
||
| def is_char_node(self) -> bool: | ||
| # TeX defines a `char_node` as one which represents a single character, | ||
| # but also states that a `char_node` will never appear in a `Vlist` | ||
| # (node134). Further, nuclei made of one `Char` and nuclei made of | ||
| # multiple `Char`s have their superscripts and subscripts shifted by | ||
| # the same amount. In order to make Mathtext behave similarly, just | ||
| # check whether this node is a `Vlist` or has any `Vlist` descendants. | ||
| return True | ||
|
|
||
|
|
||
| class Box(Node): | ||
| """A node with a physical location.""" | ||
| Expand Down Expand Up | @@ -1204,6 +1293,10 @@ def __init__(self, elements: T.Sequence[Node], w: float = 0.0, | |
| self.kern() | ||
| self.hpack(w=w, m=m) | ||
|
|
||
| def is_char_node(self) -> bool: | ||
| # See description in Node.is_char_node. | ||
| return all(map(lambda node: node.is_char_node(), self.children)) | ||
|
|
||
| def kern(self) -> None: | ||
| """ | ||
| Insert `Kern` nodes between `Char` nodes to set kerning. | ||
| Expand Down Expand Up | @@ -1295,6 +1388,10 @@ def __init__(self, elements: T.Sequence[Node], h: float = 0.0, | |
| super().__init__(elements) | ||
| self.vpack(h=h, m=m) | ||
|
|
||
| def is_char_node(self) -> bool: | ||
| # See description in Node.is_char_node. | ||
| return False | ||
|
|
||
| def vpack(self, h: float = 0.0, | ||
| m: T.Literal['additional', 'exactly'] = 'additional', | ||
| l: float = np.inf) -> None: | ||
| Expand Down Expand Up | @@ -1386,7 +1483,7 @@ def __init__(self, width: float, height: float, depth: float, state: ParserState | |
|
|
||
| def render(self, output: Output, # type: ignore[override] | ||
| x: float, y: float, w: float, h: float) -> None: | ||
| self.fontset.render_rect_filled(output, x, y, w, h) | ||
| self.fontset.render_rect_filled(output, x, y - h, w, h) | ||
|
|
||
|
|
||
| class Hrule(Rule): | ||
| Expand Down Expand Up | @@ -2111,6 +2208,7 @@ def csnames(group: str, names: Iterable[str]) -> Regex: | |
| | p.text | ||
| | p.boldsymbol | ||
| | p.substack | ||
| | p.auto_delim | ||
| ) | ||
|
|
||
| mdelim = r"\middle" - (p.delim("mdelim") | Error("Expected a delimiter")) | ||
| Expand Down Expand Up | @@ -2440,8 +2538,7 @@ def subsuper(self, s: str, loc: int, toks: ParseResults) -> T.Any: | |
| state = self.get_state() | ||
| rule_thickness = state.fontset.get_underline_thickness( | ||
| state.font, state.fontsize, state.dpi) | ||
| x_height = state.fontset.get_xheight( | ||
| state.font, state.fontsize, state.dpi) | ||
| x_height = state.fontset.get_xheight(state.font, state.fontsize, state.dpi) | ||
|
|
||
| if napostrophes: | ||
| if super is None: | ||
| Expand Down Expand Up | @@ -2530,9 +2627,19 @@ def subsuper(self, s: str, loc: int, toks: ParseResults) -> T.Any: | |
| else: | ||
| subkern = 0 | ||
|
|
||
| # Set the minimum shifts for the superscript and subscript (node756). | ||
| if nucleus.is_char_node(): | ||
| shift_up = 0.0 | ||
| shift_down = 0.0 | ||
| else: | ||
|
Comment thread
QuLogic marked this conversation as resolved.
|
||
| shrunk_x_height = state.fontset.get_xheight( | ||
| state.font, state.fontsize * SHRINK_FACTOR, state.dpi) | ||
| shift_up = nucleus.height - consts.supdrop * shrunk_x_height | ||
| shift_down = nucleus.depth + consts.subdrop * shrunk_x_height | ||
|
|
||
| x: List | ||
| if super is None: | ||
| # node757 | ||
| # Align subscript without superscript (node757). | ||
| # Note: One of super or sub must be a Node if we're in this function, but | ||
| # mypy can't know this, since it can't interpret pyparsing expressions, | ||
| # hence the cast. | ||
| Expand All | @@ -2541,29 +2648,37 @@ def subsuper(self, s: str, loc: int, toks: ParseResults) -> T.Any: | |
| if self.is_dropsub(last_char): | ||
| shift_down = lc_baseline + consts.subdrop * x_height | ||
| else: | ||
| shift_down = consts.sub1 * x_height | ||
| shift_down = max(shift_down, consts.sub1 * x_height, | ||
| x.height - x_height * 4 / 5) | ||
| x.shift_amount = shift_down | ||
| else: | ||
| # Align superscript (node758). | ||
| x = Hlist([Kern(superkern), super]) | ||
| x.shrink() | ||
| if self.is_dropsub(last_char): | ||
| shift_up = lc_height - consts.subdrop * x_height | ||
| else: | ||
| shift_up = consts.sup1 * x_height | ||
| shift_up = max(shift_up, consts.sup1 * x_height, x.depth + x_height / 4) | ||
| if sub is None: | ||
| x.shift_amount = -shift_up | ||
| else: # Both sub and superscript | ||
| else: | ||
| # Align subscript with superscript (node759). | ||
| y = Hlist([Kern(subkern), sub]) | ||
| y.shrink() | ||
| if self.is_dropsub(last_char): | ||
| shift_down = lc_baseline + consts.subdrop * x_height | ||
| else: | ||
| shift_down = consts.sub2 * x_height | ||
| # If sub and superscript collide, move super up | ||
| clr = (2 * rule_thickness - | ||
| shift_down = max(shift_down, consts.sub2 * x_height) | ||
| # If the subscript and superscript are too close to each other, | ||
| # move the subscript down. | ||
| clr = (4 * rule_thickness - | ||
| ((shift_up - x.depth) - (y.height - shift_down))) | ||
| if clr > 0.: | ||
| shift_up += clr | ||
| shift_down += clr | ||
| clr = x_height * 4 / 5 - shift_up + x.depth | ||
| if clr > 0: | ||
| shift_up += clr | ||
| shift_down -= clr | ||
| x = Vlist([ | ||
| x, | ||
| Kern((shift_up - x.depth) - (y.height - shift_down)), | ||
| Expand All | @@ -2586,32 +2701,67 @@ def _genfrac(self, ldelim: str, rdelim: str, rule: float | None, style: _MathSty | |
| state = self.get_state() | ||
| thickness = state.get_current_underline_thickness() | ||
|
|
||
| axis_height = state.fontset.get_axis_height( | ||
| state.font, state.fontsize, state.dpi) | ||
| consts = _get_font_constant_set(state) | ||
| x_height = state.fontset.get_xheight(state.font, state.fontsize, state.dpi) | ||
|
|
||
| for _ in range(style.value): | ||
| x_height *= SHRINK_FACTOR | ||
|
Comment thread
anntzer marked this conversation as resolved.
|
||
| num.shrink() | ||
| den.shrink() | ||
| cnum = HCentered([num]) | ||
| cden = HCentered([den]) | ||
| width = max(num.width, den.width) | ||
| cnum.hpack(width, 'exactly') | ||
| cden.hpack(width, 'exactly') | ||
| vlist = Vlist([ | ||
| cnum, # numerator | ||
| Vbox(0, 2 * thickness), # space | ||
| Hrule(state, rule), # rule | ||
| Vbox(0, 2 * thickness), # space | ||
| cden, # denominator | ||
| ]) | ||
|
|
||
| # Shift so the fraction line sits in the middle of the | ||
| # equals sign | ||
| metrics = state.fontset.get_metrics( | ||
| state.font, mpl.rcParams['mathtext.default'], | ||
| '=', state.fontsize, state.dpi) | ||
| shift = (cden.height - | ||
| ((metrics.ymax + metrics.ymin) / 2 - 3 * thickness)) | ||
| vlist.shift_amount = shift | ||
|
|
||
| result: list[Box | Char | str] = [Hlist([vlist, Hbox(2 * thickness)])] | ||
| # Align the fraction with a fraction line (node743, node744 and node746). | ||
| if rule: | ||
| if style is self._MathStyle.DISPLAYSTYLE: | ||
| num_shift_up = consts.num1 * x_height | ||
| den_shift_down = consts.denom1 * x_height | ||
| clr = 3 * rule # The minimum clearance. | ||
| else: | ||
| num_shift_up = consts.num2 * x_height | ||
| den_shift_down = consts.denom2 * x_height | ||
| clr = rule # The minimum clearance. | ||
| delta = rule / 2 | ||
|
Comment thread
anntzer marked this conversation as resolved.
|
||
| num_clr = max((num_shift_up - cnum.depth) - (axis_height + delta), clr) | ||
| den_clr = max((axis_height - delta) - (cden.height - den_shift_down), clr) | ||
| vlist = Vlist([cnum, # numerator | ||
| Vbox(0, num_clr), # space | ||
| Hrule(state, rule), # rule | ||
| Vbox(0, den_clr), # space | ||
| cden # denominator | ||
| ]) | ||
| vlist.shift_amount = cden.height + den_clr + delta - axis_height | ||
|
|
||
| # Align the fraction without a fraction line (node743, node744 and node745). | ||
| else: | ||
| if style is self._MathStyle.DISPLAYSTYLE: | ||
| num_shift_up = consts.num1 * x_height | ||
| den_shift_down = consts.denom1 * x_height | ||
| min_clr = 7 * thickness # The minimum clearance. | ||
| else: | ||
| num_shift_up = consts.num3 * x_height | ||
| den_shift_down = consts.denom2 * x_height | ||
| min_clr = 3 * thickness # The minimum clearance. | ||
| def_clr = (num_shift_up - cnum.depth) - (cden.height - den_shift_down) | ||
| clr = max(def_clr, min_clr) | ||
| vlist = Vlist([cnum, # numerator | ||
| Vbox(0, clr), # space | ||
| cden # denominator | ||
| ]) | ||
| vlist.shift_amount = den_shift_down | ||
| if def_clr < min_clr: | ||
| vlist.shift_amount += (min_clr - def_clr) / 2 | ||
|
|
||
| result: list[Box | Char | str] = [Hlist([ | ||
| Hbox(thickness), | ||
| vlist, | ||
| Hbox(thickness) | ||
| ])] | ||
| if ldelim or rdelim: | ||
| return self._auto_sized_delimiter(ldelim or ".", result, rdelim or ".") | ||
| return result | ||
| Expand Down | ||
| Back | FazBrowse Home | New Git URL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityWhat is the rationale for not reading the x-height from pclt anymore? (+ see comment re: tfm params for actually directly using hardcoding tfm params instead).
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityThe algorithm breaks for the bundled Computer Modern font, because the x-height obtained from pclt is much greater than the rendered x-height. The other fonts don't have a font table, if I recall correctly.
If, however, the correct parameters could be added to the files, the x-height (and many other parameters, including the axis height, as you pointed out below) could definitely be read therefrom.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityOK, so let's not bother with this too much for now and fix this later once when revisiting xheight/axis_height reading?
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.