From: <md...@us...> - 2008-10-24 17:12:29
|
Revision: 6322 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6322&view=rev Author: mdboom Date: 2008-10-24 17:12:24 +0000 (Fri, 24 Oct 2008) Log Message: ----------- Add mathtext API docs. Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/mathtext.py Added Paths: ----------- trunk/matplotlib/doc/api/mathtext_api.rst Added: trunk/matplotlib/doc/api/mathtext_api.rst =================================================================== --- trunk/matplotlib/doc/api/mathtext_api.rst (rev 0) +++ trunk/matplotlib/doc/api/mathtext_api.rst 2008-10-24 17:12:24 UTC (rev 6322) @@ -0,0 +1,14 @@ +******************* +matplotlib mathtext +******************* + +.. inheritance-diagram:: matplotlib.mathtext + :parts: 1 + +:mod:`matplotlib.mathtext` +============================= + +.. automodule:: matplotlib.mathtext + :members: + :undoc-members: + :show-inheritance: Modified: trunk/matplotlib/lib/matplotlib/mathtext.py =================================================================== --- trunk/matplotlib/lib/matplotlib/mathtext.py 2008-10-24 15:53:59 UTC (rev 6321) +++ trunk/matplotlib/lib/matplotlib/mathtext.py 2008-10-24 17:12:24 UTC (rev 6322) @@ -1,175 +1,21 @@ r""" +:mod:`~matplotlib.mathtext` is a module for parsing a subset of the +TeX math syntax and drawing them to a matplotlib backend. -OVERVIEW +For a tutorial of its usage see :ref:`mathtext-tutorial`. This +document is primarily concerned with implementation details. - mathtext is a module for parsing TeX expressions and drawing them - into a matplotlib.ft2font image buffer. You can draw from this - buffer into your backend. +The module uses pyparsing_ to parse the TeX expression. - A large set of the TeX symbols are provided (see below). - Subscripting and superscripting are supported, as well as the - over/under style of subscripting with \sum, \int, etc. +.. _pyparsing: http://pyparsing.wikispaces.com/ - The module uses pyparsing to parse the TeX expression, an so can - handle fairly complex TeX expressions Eg, the following renders - correctly +The Bakoma distribution of the TeX Computer Modern fonts, and STIX +fonts are supported. There is experimental support for using +arbitrary fonts, but results may vary without proper tweaking and +metrics for those fonts. - s = r'$\mathcal{R}\prod_{i=\alpha\mathcal{B}}^\infty a_i\sin(2 \pi f x_i)$' - - Different fonts may be selected: - \mathcal Calligraphic fonts - \mathrm Roman (upright) font - \mathit Italic font - \mathtt Typewriter (monospaced) font, similar to Courier - - Additionally, if using the STIX fonts: - \mathbb Blackboard (double-struck) font - \mathcircled Circled characters - \mathfrak Fraktur (Gothic-style) font - \mathscr Script (cursive) font - \mathsf Sans-serif font - - The following accents are provided: \hat, \breve, \grave, \bar, - \acute, \tilde, \vec, \dot, \ddot. All of them have the same - syntax, eg to make an overbar you do \bar{o} or to make an o umlaut - you do \ddot{o}. The shortcuts are also provided, eg: \"o \'e \`e - \~n \.x \^y - - The spacing elements \ , \/ and \hspace{num} are provided. \/ - inserts a small space, and \hspace{num} inserts a fraction of the - current fontsize. Eg, if num=0.5 and the fontsize is 12.0, - hspace{0.5} inserts 6 points of space - - - - If you find TeX expressions that don't parse or render properly, - please email me, but please check KNOWN ISSUES below first. - -REQUIREMENTS - - mathtext requires matplotlib.ft2font. Set BUILD_FT2FONT=True in - setup.py. See BACKENDS below for a summary of availability by - backend. - -LICENSING: - - The computer modern fonts this package uses are part of the BaKoMa - fonts, which are (now) free for commercial and noncommercial use and - redistribution; see license/LICENSE_BAKOMA in the matplotlib src - distribution for redistribution requirements. - -USAGE: - - See http://matplotlib.sf.net/users/mathtext.html for a tutorial - introduction. - - Any text element (xlabel, ylabel, title, text, etc) can use TeX - markup, as in - - xlabel(r'$\Delta_i$') - ^ - use raw strings - - Math and non-math can be interpresed in the same string. E.g., - - r'My label $x_i$'. - - A large set of the TeX symbols are provided. Subscripting and - superscripting are supported, as well as the over/under style of - subscripting with \sum, \int, etc. - - - Allowed TeX symbols: - - $ \% \AA \AE \BbbC \BbbN \BbbP \BbbQ \BbbR \BbbZ \Bumpeq \Cap \Colon - \Cup \Delta \Doteq \Downarrow \Equiv \Finv \Gamma \H \Im \L \Lambda - \Ldsh \Leftarrow \Leftrightarrow \Lleftarrow \Lsh \Nearrow \Nwarrow - \O \OE \Omega \P \Phi \Pi \Psi \Rdsh \Re \Rightarrow \Rrightarrow - \Rsh \S \Searrow \Sigma \Subset \Supset \Swarrow \Theta \Uparrow - \Updownarrow \Upsilon \Vdash \Vert \Vvdash \Xi \_ \__sqrt__ \ac - \acute \acwopencirclearrow \adots \ae \aleph \alpha \angle \approx - \approxeq \approxident \arceq \ast \asymp \backcong \backprime - \backsim \backsimeq \backslash \bar \barleftarrow \barwedge \because - \beta \beth \between \bigcap \bigcirc \bigcup \bigodot \bigoplus - \bigotimes \bigstar \bigtriangledown \bigtriangleup \biguplus - \bigvee \bigwedge \blacksquare \blacktriangle \blacktriangledown - \blacktriangleleft \blacktriangleright \bot \bowtie \boxbar \boxdot - \boxminus \boxplus \boxtimes \breve \bullet \bumpeq \c \candra \cap - \carriagereturn \cdot \cdotp \cdots \check \checkmark \chi \circ - \circeq \circledR \circledS \circledast \circledcirc \circleddash - \circumflexaccent \clubsuit \clubsuitopen \colon \coloneq - \combiningacuteaccent \combiningbreve \combiningdiaeresis - \combiningdotabove \combininggraveaccent \combiningoverline - \combiningrightarrowabove \combiningtilde \complement \cong \coprod - \copyright \cup \cupdot \curlyeqprec \curlyeqsucc \curlyvee - \curlywedge \curvearrowleft \curvearrowright \cwopencirclearrow \d - \dag \daleth \danger \dashv \ddag \ddddot \dddot \ddot \ddots - \degree \delta \diamond \diamondsuit \digamma \div \divideontimes - \dot \doteq \dotminus \dotplus \dots \doublebarwedge ? \downarrow - \downdownarrows \downharpoonleft \downharpoonright \downzigzagarrow - \ell \emdash \emptyset \endash \enspace \epsilon \eqcirc \eqcolon - \eqdef \eqgtr \eqless \eqsim \equiv \eta \eth \exists \fallingdotseq - \flat \forall \frakC \frakZ \frown \gamma \geq \geqq \gg \ggg \gimel - \gneqq \gnsim \grave \greater \gtrdot \gtreqless \gtrless \gtrsim - \hat \heartsuit \hookleftarrow \hookrightarrow \i \iiint \iint - \imageof \imath \in \infty \int \intercal \invnot \iota \jmath \k - \kappa \kernelcontraction \l \lambda \lambdabar \lasp \lbrace - \lbrack \lceil \leftangle \leftarrow \leftarrowtail \leftbrace - \leftharpoonaccent \leftharpoondown \leftharpoonup \leftleftarrows - \leftparen \leftrightarrow \leftrightarrows \leftrightharpoons - \leftthreetimes \leq \leqq \less \lessdot \lesseqgtr \lessgtr - \lesssim \lfloor \ll \llcorner \lll \lneqq \lnsim \looparrowleft - \looparrowright \lq \lrcorner \ltimes \maltese \mapsdown \mapsfrom - \mapsto \mapsup \measeq \measuredangle \mho \mid \minus \models \mp - \mu \multimap \nLeftarrow \nLeftrightarrow \nRightarrow \nVDash - \nVdash \nabla \napprox \natural \ncong \ne \nearrow \neg \nequiv - \nexists \ngeq \ngtr \ni \nleftarrow \nleftrightarrow \nleq \nless - \nmid \not \notin \nparallel \nprec \nrightarrow \nsim \nsime - \nsubset \nsubseteq \nsucc \nsupset \nsupseteq \ntriangleleft - \ntrianglelefteq \ntriangleright \ntrianglerighteq \nu \nvDash - \nvdash \nwarrow \o \obar \ocirc \odot \oe \oiiint \oiint \oint - \omega \ominus \oplus \origof \oslash \otimes \overarc - \overleftarrow \overleftrightarrow \parallel \partial \phi \pi - \pitchfork \pm \prec \preccurlyeq \preceq \precnsim \precsim \prime - \prod \propto \prurel \psi \quad \questeq \rasp \rbrace \rbrack - \rceil \rfloor \rho \rightangle \rightarrow \rightarrowbar - \rightarrowtail \rightbrace \rightharpoonaccent \rightharpoondown - \rightharpoonup \rightleftarrows \rightleftharpoons \rightparen - \rightrightarrows \rightthreetimes \rightzigzagarrow \risingdotseq - \rq \rtimes \scrB \scrE \scrF \scrH \scrI \scrL \scrM \scrR \scre - \scrg \scro \scurel \searrow \sharp \sigma \sim \simeq \slash - \smallsetminus \smile \solbar \spadesuit \spadesuitopen - \sphericalangle \sqcap \sqcup \sqsubset \sqsubseteq \sqsupset - \sqsupseteq \ss \star \stareq \sterling \subset \subseteq \subsetneq - \succ \succcurlyeq \succeq \succnsim \succsim \sum \supset \supseteq - \supsetneq \swarrow \t \tau \textasciiacute \textasciicircum - \textasciigrave \textasciitilde \textexclamdown \textquestiondown - \textquotedblleft \textquotedblright \therefore \theta \thickspace - \thinspace \tilde \times \to \top \triangledown \triangleleft - \trianglelefteq \triangleq \triangleright \trianglerighteq - \turnednot \twoheaddownarrow \twoheadleftarrow \twoheadrightarrow - \twoheaduparrow \ulcorner \underbar \uparrow \updownarrow - \updownarrowbar \updownarrows \upharpoonleft \upharpoonright \uplus - \upsilon \upuparrows \urcorner \vDash \varepsilon \varkappa - \varnothing \varphi \varpi \varrho \varsigma \vartheta \vartriangle - \vartriangleleft \vartriangleright \vdash \vdots \vec \vee \veebar - \veeeq \vert \wedge \wedgeq \widehat \widetilde \wp \wr \xi \yen - \zeta \{ \| \} - -BACKENDS - - mathtext currently works with all backends. - -KNOWN ISSUES: - - - Certainly there are some... - -Author : John Hunter <jdh...@ac...> - Michael Droettboom <md...@st...> - (rewrite based on TeX box layout algorithms) -Copyright : John Hunter (2004,2005) -License : matplotlib license (PSF compatible) - +If you find TeX expressions that don't parse or render properly, +please email md...@st..., but please check KNOWN ISSUES below first. """ from __future__ import division import os @@ -214,10 +60,9 @@ def get_unicode_index(symbol): """get_unicode_index(symbol) -> integer -Return the integer index (from the Unicode table) of symbol. -symbol can be a single unicode character, a TeX command (i.e. r'\pi'), -or a Type1 symbol name (i.e. 'phi'). - +Return the integer index (from the Unicode table) of symbol. *symbol* +can be a single unicode character, a TeX command (i.e. r'\pi'), or a +Type1 symbol name (i.e. 'phi'). """ # From UTF #25: U+2212 minus sign is the preferred # representation of the unary and binary minus sign rather than @@ -239,32 +84,62 @@ class MathtextBackend(object): + """ + The base class for the mathtext backend-specific code. The + purpose of :class:`MathtextBackend` subclasses is to interface + between mathtext and a specific matplotlib graphics backend. + + Subclasses need to override the following: + + - :meth:`render_glyph` + - :meth:`render_filled_rect` + - :meth:`get_results` + + And optionally, if you need to use a Freetype hinting style: + + - :meth:`get_hinting_type` + """ def __init__(self): self.fonts_object = None def set_canvas_size(self, w, h, d): - 'Dimension the drawing canvas; may be a noop' + 'Dimension the drawing canvas' self.width = w self.height = h self.depth = d def render_glyph(self, ox, oy, info): + """ + Draw a glyph described by *info* to the reference point (*ox*, + *oy*). + """ raise NotImplementedError() def render_filled_rect(self, x1, y1, x2, y2): + """ + Draw a filled black rectangle from (*x1*, *y1*) to (*x2*, *y2*). + """ raise NotImplementedError() def get_results(self, box): - """Return a backend specific tuple of things to return to the - backend after all processing is done.""" + """ + Return a backend-specific tuple to return to the backend after + all processing is done. + """ raise NotImplementedError() def get_hinting_type(self): + """ + Get the Freetype hinting type to use with this particular + backend. + """ return LOAD_NO_HINTING class MathtextBackendBbox(MathtextBackend): - """A backend whose only purpose is to get a precise bounding box. - Only required for the Agg backend.""" + """ + A backend whose only purpose is to get a precise bounding box. + Only required for the Agg backend. + """ def __init__(self, real_backend): MathtextBackend.__init__(self) @@ -310,6 +185,10 @@ self.real_backend.oy = self.bbox[1] class MathtextBackendAggRender(MathtextBackend): + """ + Render glyphs and rectangles to an FTImage buffer, which is later + transferred to the Agg image by the Agg backend. + """ def __init__(self): self.ox = 0 self.oy = 0 @@ -353,9 +232,17 @@ return self.image, self.depth def MathtextBackendBitmap(): + """ + A backend to generate standalone mathtext images. No additional + matplotlib backend is required. + """ return MathtextBackendBbox(MathtextBackendBitmapRender()) class MathtextBackendPs(MathtextBackend): + """ + Store information to write a mathtext rendering to the PostScript + backend. + """ def __init__(self): self.pswriter = StringIO() self.lastfont = None @@ -393,6 +280,10 @@ self.fonts_object.get_used_characters()) class MathtextBackendPdf(MathtextBackend): + """ + Store information to write a mathtext rendering to the PDF + backend. + """ def __init__(self): self.glyphs = [] self.rects = [] @@ -417,6 +308,10 @@ self.fonts_object.get_used_characters()) class MathtextBackendSvg(MathtextBackend): + """ + Store information to write a mathtext rendering to the SVG + backend. + """ def __init__(self): self.svg_glyphs = [] self.svg_rects = [] @@ -442,6 +337,11 @@ self.fonts_object.get_used_characters()) class MathtextBackendCairo(MathtextBackend): + """ + Store information to write a mathtext rendering to the Cairo + backend. + """ + def __init__(self): self.glyphs = [] self.rects = [] @@ -466,7 +366,7 @@ class Fonts(object): """ - An abstract base class for fonts that want to render mathtext + An abstract base class for a system of fonts to use for mathtext. The class must be able to take symbol keys and font file names and return the character metrics. It also delegates to a backend class @@ -474,11 +374,15 @@ """ def __init__(self, default_font_prop, mathtext_backend): - """default_font_prop: A FontProperties object to use for the - default non-math font, or the base font for Unicode font - rendering. - mathtext_backend: A subclass of MathTextBackend used to - delegate the actual rendering.""" + """ + *default_font_prop*: A + :class:`~matplotlib.font_manager.FontProperties` object to use + for the default non-math font, or the base font for Unicode + (generic) font rendering. + + *mathtext_backend*: A subclass of :class:`MathTextBackend` + used to delegate the actual rendering. + """ self.default_font_prop = default_font_prop self.mathtext_backend = mathtext_backend # Make these classes doubly-linked @@ -486,51 +390,86 @@ self.used_characters = {} def destroy(self): - """Fix any cyclical references before the object is about - to be destroyed.""" + """ + Fix any cyclical references before the object is about + to be destroyed. + """ self.used_characters = None def get_kern(self, font1, fontclass1, sym1, fontsize1, font2, fontclass2, sym2, fontsize2, dpi): """ - Get the kerning distance for font between sym1 and sym2. + Get the kerning distance for font between *sym1* and *sym2*. - fontX: one of the TeX font names, tt, it, rm, cal, sf, bf or - default (non-math) - symX: a symbol in raw TeX form. e.g. '1', 'x' or '\sigma' - fontsizeX: the fontsize in points - dpi: the current dots-per-inch + *fontX*: one of the TeX font names:: - sym is a single symbol(alphanum, punct) or a special symbol - like \sigma. + tt, it, rm, cal, sf, bf or default (non-math) + *fontclassX*: TODO + + *symX*: a symbol in raw TeX form. e.g. '1', 'x' or '\sigma' + + *fontsizeX*: the fontsize in points + + *dpi*: the current dots-per-inch """ return 0. def get_metrics(self, font, font_class, sym, fontsize, dpi): """ - font: one of the TeX font names, tt, it, rm, cal, sf, bf or - default (non-math) - sym: a symbol in raw TeX form. e.g. '1', 'x' or '\sigma' - fontsize: font size in points - dpi: current dots-per-inch + *font*: one of the TeX font names:: - advance - height - width - xmin, xmax, ymin, ymax - the ink rectangle of the glyph - iceberg - the distance from the baseline to the top of the glyph. - horiBearingY in Truetype parlance, height in TeX parlance + tt, it, rm, cal, sf, bf or default (non-math) + + *font_class*: TODO + + *sym*: a symbol in raw TeX form. e.g. '1', 'x' or '\sigma' + + *fontsize*: font size in points + + *dpi*: current dots-per-inch + + Returns an object with the following attributes: + + - *advance*: The advance distance (in points) of the glyph. + + - *height*: The height of the glyph in points. + + - *width*: The width of the glyph in points. + + - *xmin*, *xmax*, *ymin*, *ymax* - the ink rectangle of the glyph + + - *iceberg* - the distance from the baseline to the top of + the glyph. This corresponds to TeX's definition of + "height". """ info = self._get_info(font, font_class, sym, fontsize, dpi) return info.metrics def set_canvas_size(self, w, h, d): - 'Dimension the drawing canvas; may be a noop' + """ + Set the size of the buffer used to render the math expression. + Only really necessary for the bitmap backends. + """ self.width, self.height, self.depth = ceil(w), ceil(h), ceil(d) self.mathtext_backend.set_canvas_size(self.width, self.height, self.depth) def render_glyph(self, ox, oy, facename, font_class, sym, fontsize, dpi): + """ + Draw a glyph at + + - *ox*, *oy*: position + + - *facename*: One of the TeX face names + + - *font_class*: + + - *sym*: TeX symbol name or single character + + - *fontsize*: fontsize in points + + - *dpi*: The dpi to draw at. + """ info = self._get_info(facename, font_class, sym, fontsize, dpi) realpath, stat_key = get_realpath_and_stat(info.font.fname) used_characters = self.used_characters.setdefault( @@ -539,31 +478,52 @@ self.mathtext_backend.render_glyph(ox, oy, info) def render_rect_filled(self, x1, y1, x2, y2): + """ + Draw a filled rectangle from (*x1*, *y1*) to (*x2*, *y2*). + """ self.mathtext_backend.render_rect_filled(x1, y1, x2, y2) def get_xheight(self, font, fontsize, dpi): + """ + Get the xheight for the given *font* and *fontsize*. + """ raise NotImplementedError() def get_underline_thickness(self, font, fontsize, dpi): + """ + Get the line thickness that matches the given font. Used as a + base unit for drawing lines such as in a fraction or radical. + """ raise NotImplementedError() def get_used_characters(self): + """ + Get the set of characters that were used in the math + expression. Used by backends that need to subset fonts so + they know which glyphs to include. + """ return self.used_characters def get_results(self, box): + """ + Get the data needed by the backend to render the math + expression. The return value is backend-specific. + """ return self.mathtext_backend.get_results(box) def get_sized_alternatives_for_symbol(self, fontname, sym): """ Override if your font provides multiple sizes of the same - symbol. + symbol. Should return a list of symbols matching *sym* in + various sizes. The expression renderer will select the most + appropriate size for a given situation from this list. """ return [(fontname, sym)] class TruetypeFonts(Fonts): """ A generic base class for all font setups that use Truetype fonts - (through ft2font) + (through FT2Font). """ class CachedFont: def __init__(self, font): @@ -589,8 +549,6 @@ Fonts.destroy(self) def _get_font(self, font): - """Looks up a CachedFont with its charmap and inverse charmap. - font may be a TeX font name (cal, rm, it etc.), or postscript name.""" if font in self.fontmap: basename = self.fontmap[font] else: @@ -611,7 +569,6 @@ return 0. def _get_info(self, fontname, font_class, sym, fontsize, dpi): - 'load the cmfont, metrics and glyph with caching' key = fontname, font_class, sym, fontsize, dpi bunch = self.glyphd.get(key) if bunch is not None: @@ -665,8 +622,9 @@ return xHeight def get_underline_thickness(self, font, fontsize, dpi): - # This function used to grab underline thickness from the font, - # but that information is just too un-reliable, so it is now hardcoded. + # This function used to grab underline thickness from the font + # metrics, but that information is just too un-reliable, so it + # is now hardcoded. return ((0.75 / 12.0) * fontsize * dpi) / 72.0 def get_kern(self, font1, fontclass1, sym1, fontsize1, @@ -681,7 +639,10 @@ class BakomaFonts(TruetypeFonts): """ - Use the Bakoma true type fonts for rendering + Use the Bakoma TrueType fonts for rendering. + + Symbols are strewn about a number of font files, each of which has + its own proprietary 8-bit encoding. """ _fontmap = { 'cal' : 'cmsy10', 'rm' : 'cmr10', @@ -788,15 +749,19 @@ _size_alternatives[alias] = _size_alternatives[target] def get_sized_alternatives_for_symbol(self, fontname, sym): - alternatives = self._size_alternatives.get(sym) - if alternatives: - return alternatives - return [(fontname, sym)] + return self._size_alternatives.get(sym, [(fontname, sym)]) class UnicodeFonts(TruetypeFonts): - """An abstract base class for handling Unicode fonts. """ + An abstract base class for handling Unicode fonts. + While some reasonably complete Unicode fonts (such as DejaVu) may + work in some situations, the only Unicode font I'm aware of with a + complete set of math symbols is STIX. + + This class will "fallback" on the Bakoma fonts when a required + symbol can not be found in the font. + """ fontmap = {} use_cmex = True @@ -900,7 +865,15 @@ class StixFonts(UnicodeFonts): """ - A font handling class for the STIX fonts + A font handling class for the STIX fonts. + + In addition to what UnicodeFonts provides, this class: + + - supports "virtual fonts" which are complete alpha numeric + character sets with different font styles at special Unicode + code points, such as "Blackboard". + + - handles sized alternative characters for the STIXSizeX fonts. """ _fontmap = { 'rm' : 'STIXGeneral', 'it' : 'STIXGeneral:italic', @@ -994,7 +967,7 @@ class StixSansFonts(StixFonts): """ - A font handling class for the STIX fonts (using sans-serif + A font handling class for the STIX fonts (that uses sans-serif characters by default). """ _sans = True @@ -1187,9 +1160,9 @@ pass class Node(object): - """A node in the TeX box model - node133 """ + A node in the TeX box model + """ def __init__(self): self.size = 0 @@ -1203,21 +1176,26 @@ return 0.0 def shrink(self): - """Shrinks one level smaller. There are only three levels of sizes, - after which things will no longer get smaller.""" + """ + Shrinks one level smaller. There are only three levels of + sizes, after which things will no longer get smaller. + """ self.size += 1 def grow(self): - """Grows one level larger. There is no limit to how big something - can get.""" + """ + Grows one level larger. There is no limit to how big + something can get. + """ self.size -= 1 def render(self, x, y): pass class Box(Node): - """Represents any node with a physical location. - node135""" + """ + Represents any node with a physical location. + """ def __init__(self, width, height, depth): Node.__init__(self) self.width = width @@ -1255,15 +1233,16 @@ Box.__init__(self, width, 0., 0.) class Char(Node): - """Represents a single character. Unlike TeX, the font - information and metrics are stored with each Char to make it - easier to lookup the font metrics when needed. Note that TeX - boxes have a width, height, and depth, unlike Type1 and Truetype - which use a full bounding box and an advance in the x-direction. - The metrics must be converted to the TeX way, and the advance (if - different from width) must be converted into a Kern node when the - Char is added to its parent Hlist. - node134""" + """ + Represents a single character. Unlike TeX, the font information + and metrics are stored with each :class:`Char` to make it easier + to lookup the font metrics when needed. Note that TeX boxes have + a width, height, and depth, unlike Type1 and Truetype which use a + full bounding box and an advance in the x-direction. The metrics + must be converted to the TeX way, and the advance (if different + from width) must be converted into a :class:`Kern` node when the + :class:`Char` is added to its parent :class:`Hlist`. + """ def __init__(self, c, state): Node.__init__(self) self.c = c @@ -1294,9 +1273,11 @@ return self._metrics.slanted def get_kerning(self, next): - """Return the amount of kerning between this and the given + """ + Return the amount of kerning between this and the given character. Called when characters are strung together into - Hlists to create Kern nodes.""" + :class:`Hlist` to create :class:`Kern` nodes. + """ advance = self._metrics.advance - self.width kern = 0. if isinstance(next, Char): @@ -1307,7 +1288,9 @@ return advance + kern def render(self, x, y): - """Render the character to the canvas""" + """ + Render the character to the canvas + """ self.font_output.render_glyph( x, y, self.font, self.font_class, self.c, self.fontsize, self.dpi) @@ -1328,9 +1311,11 @@ self.depth *= GROW_FACTOR class Accent(Char): - """The font metrics need to be dealt with differently for accents, + """ + The font metrics need to be dealt with differently for accents, since they are already offset correctly from the baseline in - TrueType fonts.""" + TrueType fonts. + """ def _update_metrics(self): metrics = self._metrics = self.font_output.get_metrics( self.font, self.font_class, self.c, self.fontsize, self.dpi) @@ -1347,14 +1332,17 @@ self._update_metrics() def render(self, x, y): - """Render the character to the canvas""" + """ + Render the character to the canvas. + """ self.font_output.render_glyph( x - self._metrics.xmin, y + self._metrics.ymin, self.font, self.font_class, self.c, self.fontsize, self.dpi) class List(Box): - """A list of nodes (either horizontal or vertical). - node135""" + """ + A list of nodes (either horizontal or vertical). + """ def __init__(self, elements): Box.__init__(self, 0., 0., 0.) self.shift_amount = 0. # An arbitrary offset @@ -1372,8 +1360,10 @@ ' '.join([repr(x) for x in self.children])) def _determine_order(self, totals): - """A helper function to determine the highest order of glue - used by the members of this list. Used by vpack and hpack.""" + """ + A helper function to determine the highest order of glue + used by the members of this list. Used by vpack and hpack. + """ o = 0 for i in range(len(totals) - 1, 0, -1): if totals[i] != 0.0: @@ -1411,8 +1401,9 @@ self.glue_set *= GROW_FACTOR class Hlist(List): - """A horizontal list of boxes. - node135""" + """ + A horizontal list of boxes. + """ def __init__(self, elements, w=0., m='additional', do_kern=True): List.__init__(self, elements) if do_kern: @@ -1420,10 +1411,13 @@ self.hpack() def kern(self): - """Insert Kern nodes between Chars to set kerning. The - Chars themselves determine the amount of kerning they need - (in get_kerning), and this function just creates the linked - list in the correct way.""" + """ + Insert :class:`Kern` nodes between :class:`Char` nodes to set + kerning. The :class:`Char` nodes themselves determine the + amount of kerning they need (in :meth:`~Char.get_kerning`), + and this function just creates the linked list in the correct + way. + """ new_children = [] num_children = len(self.children) if num_children: @@ -1455,20 +1449,24 @@ # return 0.0 def hpack(self, w=0., m='additional'): - """The main duty of hpack is to compute the dimensions of the - resulting boxes, and to adjust the glue if one of those dimensions is - pre-specified. The computed sizes normally enclose all of the material - inside the new box; but some items may stick out if negative glue is - used, if the box is overfull, or if a \vbox includes other boxes that - have been shifted left. + """ + The main duty of :meth:`hpack` is to compute the dimensions of + the resulting boxes, and to adjust the glue if one of those + dimensions is pre-specified. The computed sizes normally + enclose all of the material inside the new box; but some items + may stick out if negative glue is used, if the box is + overfull, or if a ``\\vbox`` includes other boxes that have + been shifted left. - w: specifies a width - m: is either 'exactly' or 'additional'. + - *w*: specifies a width - Thus, hpack(w, 'exactly') produces a box whose width is exactly w, while - hpack (w, 'additional') yields a box whose width is the natural width - plus w. The default values produce a box with the natural width. - node644, node649""" + - *m*: is either 'exactly' or 'additional'. + + Thus, ``hpack(w, 'exactly')`` produces a box whose width is + exactly *w*, while ``hpack(w, 'additional')`` yields a box + whose width is the natural width plus *w*. The default values + produce a box with the natural width. + """ # I don't know why these get reset in TeX. Shift_amount is pretty # much useless if we do. #self.shift_amount = 0. @@ -1514,25 +1512,28 @@ self._set_glue(x, -1, total_shrink, "Underfull") class Vlist(List): - """A vertical list of boxes. - node137""" + """ + A vertical list of boxes. + """ def __init__(self, elements, h=0., m='additional'): List.__init__(self, elements) self.vpack() def vpack(self, h=0., m='additional', l=float(inf)): - """The main duty of vpack is to compute the dimensions of the - resulting boxes, and to adjust the glue if one of those dimensions is - pre-specified. + """ + The main duty of :meth:`vpack` is to compute the dimensions of + the resulting boxes, and to adjust the glue if one of those + dimensions is pre-specified. - h: specifies a height - m: is either 'exactly' or 'additional'. - l: a maximum height + - *h*: specifies a height + - *m*: is either 'exactly' or 'additional'. + - *l*: a maximum height - Thus, vpack(h, 'exactly') produces a box whose width is exactly w, while - vpack(w, 'additional') yields a box whose width is the natural width - plus w. The default values produce a box with the natural width. - node644, node668""" + Thus, ``vpack(h, 'exactly')`` produces a box whose height is + exactly *h*, while ``vpack(h, 'additional')`` yields a box + whose height is the natural height plus *h*. The default + values produce a box with the natural width. + """ # I don't know why these get reset in TeX. Shift_amount is pretty # much useless if we do. # self.shift_amount = 0. @@ -1585,13 +1586,15 @@ self._set_glue(x, -1, total_shrink, "Underfull") class Rule(Box): - """A Rule node stands for a solid black rectangle; it has width, - depth, and height fields just as in an Hlist. However, if any of these - dimensions is inf, the actual value will be determined by running the - rule up to the boundary of the innermost enclosing box. This is called - a "running dimension." The width is never running in an Hlist; the - height and depth are never running in a Vlist. - node138""" + """ + A :class:`Rule` node stands for a solid black rectangle; it has + *width*, *depth*, and *height* fields just as in an + :class:`Hlist`. However, if any of these dimensions is inf, the + actual value will be determined by running the rule up to the + boundary of the innermost enclosing box. This is called a "running + dimension." The width is never running in an :class:`Hlist`; the + height and depth are never running in a :class:`Vlist`. + """ def __init__(self, width, height, depth, state): Box.__init__(self, width, height, depth) self.font_output = state.font_output @@ -1600,7 +1603,9 @@ self.font_output.render_rect_filled(x, y, x + w, y + h) class Hrule(Rule): - """Convenience class to create a horizontal rule.""" + """ + Convenience class to create a horizontal rule. + """ def __init__(self, state): thickness = state.font_output.get_underline_thickness( state.font, state.fontsize, state.dpi) @@ -1608,18 +1613,21 @@ Rule.__init__(self, inf, height, depth, state) class Vrule(Rule): - """Convenience class to create a vertical rule.""" + """ + Convenience class to create a vertical rule. + """ def __init__(self, state): thickness = state.font_output.get_underline_thickness( state.font, state.fontsize, state.dpi) Rule.__init__(self, thickness, inf, inf, state) class Glue(Node): - """Most of the information in this object is stored in the underlying - GlueSpec class, which is shared between multiple glue objects. (This + """ + Most of the information in this object is stored in the underlying + :class:`GlueSpec` class, which is shared between multiple glue objects. (This is a memory optimization which probably doesn't matter anymore, but it's easier to stick to what TeX does.) - node149, node152""" + """ def __init__(self, glue_type, copy=False): Node.__init__(self) self.glue_subtype = 'normal' @@ -1647,7 +1655,9 @@ self.glue_spec.width *= GROW_FACTOR class GlueSpec(object): - """node150, node151""" + """ + See :class:`Glue`. + """ def __init__(self, width=0., stretch=0., stretch_order=0, shrink=0., shrink_order=0): self.width = width self.stretch = stretch @@ -1709,26 +1719,32 @@ Glue.__init__(self, 'ss') class HCentered(Hlist): - """A convenience class to create an Hlist whose contents are centered - within its enclosing box.""" + """ + A convenience class to create an :class:`Hlist` whose contents are + centered within its enclosing box. + """ def __init__(self, elements): Hlist.__init__(self, [SsGlue()] + elements + [SsGlue()], do_kern=False) class VCentered(Hlist): - """A convenience class to create an Vlist whose contents are centered - within its enclosing box.""" + """ + A convenience class to create a :class:`Vlist` whose contents are + centered within its enclosing box. + """ def __init__(self, elements): Vlist.__init__(self, [SsGlue()] + elements + [SsGlue()]) class Kern(Node): - """A Kern node has a width field to specify a (normally negative) - amount of spacing. This spacing correction appears in horizontal lists - between letters like A and V when the font designer said that it looks - better to move them closer together or further apart. A kern node can - also appear in a vertical list, when its 'width' denotes additional - spacing in the vertical direction. - node155""" + """ + A :class:`Kern` node has a width field to specify a (normally + negative) amount of spacing. This spacing correction appears in + horizontal lists between letters like A and V when the font + designer said that it looks better to move them closer together or + further apart. A kern node can also appear in a vertical list, + when its *width* denotes additional spacing in the vertical + direction. + """ def __init__(self, width): Node.__init__(self) self.width = width @@ -1746,11 +1762,13 @@ self.width *= GROW_FACTOR class SubSuperCluster(Hlist): - """This class is a sort of hack to get around that fact that this - code doesn't parse to an mlist and then an hlist, but goes directly - to hlists. This lets us store enough information in the hlist itself, - namely the nucleas, sub- and super-script, such that if another script - follows that needs to be attached, it can be reconfigured on the fly.""" + """ + :class:`SubSuperCluster` is a sort of hack to get around that fact + that this code do a two-pass parse like TeX. This lets us store + enough information in the hlist itself, namely the nucleus, sub- + and super-script, such that if another script follows that needs + to be attached, it can be reconfigured on the fly. + """ def __init__(self): self.nucleus = None self.sub = None @@ -1758,11 +1776,13 @@ Hlist.__init__(self, []) class AutoHeightChar(Hlist): - """A class that will create a character as close to the given height - and depth as possible. When using a font with multiple height versions - of some characters (such as the BaKoMa fonts), the correct glyph will - be selected, otherwise this will always just return a scaled version - of the glyph.""" + """ + :class:`AutoHeightChar` will create a character as close to the + given height and depth as possible. When using a font with + multiple height versions of some characters (such as the BaKoMa + fonts), the correct glyph will be selected, otherwise this will + always just return a scaled version of the glyph. + """ def __init__(self, c, height, depth, state, always=False): alternatives = state.font_output.get_sized_alternatives_for_symbol( state.font, c) @@ -1784,11 +1804,13 @@ self.shift_amount = shift class AutoWidthChar(Hlist): - """A class that will create a character as close to the given width - as possible. When using a font with multiple width versions - of some characters (such as the BaKoMa fonts), the correct glyph will - be selected, otherwise this will always just return a scaled version - of the glyph.""" + """ + :class:`AutoWidthChar` will create a character as close to the + given width as possible. When using a font with multiple width + versions of some characters (such as the BaKoMa fonts), the + correct glyph will be selected, otherwise this will always just + return a scaled version of the glyph. + """ def __init__(self, c, width, state, always=False, char_class=Char): alternatives = state.font_output.get_sized_alternatives_for_symbol( state.font, c) @@ -1808,13 +1830,15 @@ self.width = char.width class Ship(object): - """Once the boxes have been set up, this sends them to output. - Since boxes can be inside of boxes inside of boxes, the main - work of Ship is done by two mutually recursive routines, hlist_out - and vlist_out , which traverse the Hlists and Vlists inside of - horizontal and vertical boxes. The global variables used in TeX to - store state as it processes have become member variables here. - node592.""" + """ + Once the boxes have been set up, this sends them to output. Since + boxes can be inside of boxes inside of boxes, the main work of + :class:`Ship` is done by two mutually recursive routines, + :meth:`hlist_out` and :meth:`vlist_out`, which traverse the + :class:`Hlist` nodes and :class:`Vlist` nodes inside of horizontal + and vertical boxes. The global variables used in TeX to store + state as it processes have become member variables here. + """ def __call__(self, ox, oy, box): self.max_push = 0 # Deepest nesting of push commands so far self.cur_s = 0 @@ -1959,6 +1983,9 @@ # PARSER def Error(msg): + """ + Helper class to raise parser errors. + """ def raise_error(s, loc, toks): raise ParseFatalException(msg + "\n" + s) @@ -1967,6 +1994,14 @@ return empty class Parser(object): + """ + This is the pyparsing-based parser for math expressions. It + actually parses full strings *containing* math expressions, in + that raw text may also appear outside of pairs of ``$``. + + The grammar is based directly on that in TeX, though it cuts a few + corners. + """ _binary_operators = set(r''' + * \pm \sqcap \rhd @@ -2198,11 +2233,20 @@ self.clear() def clear(self): + """ + Clear any state before parsing. + """ self._expr = None self._state_stack = None self._em_width_cache = {} def parse(self, s, fonts_object, fontsize, dpi): + """ + Parse expression *s* using the given *fonts_object* for + output, at the given *fontsize* and *dpi*. + + Returns the parse tree of :class:`Node` instances. + """ self._state_stack = [self.State(fonts_object, 'default', 'rm', fontsize, dpi)] try: self._expression.parseString(s) @@ -2219,6 +2263,12 @@ # is pushed and popped accordingly. The current state always # exists in the top element of the stack. class State(object): + """ + Stores the state of the parser. + + States are pushed and popped from a stack as necessary, and + the "current" state is always at the top of the stack. + """ def __init__(self, font_output, font, font_class, fontsize, dpi): self.font_output = font_output self._font = font @@ -2243,12 +2293,22 @@ font = property(_get_font, _set_font) def get_state(self): + """ + Get the current :class:`State` of the parser. + """ return self._state_stack[-1] def pop_state(self): + """ + Pop a :class:`State` off of the stack. + """ self._state_stack.pop() def push_state(self): + """ + Push a new :class:`State` onto the stack which is just a copy + of the current state. + """ self._state_stack.append(self.get_state().copy()) def finish(self, s, loc, toks): @@ -2674,14 +2734,6 @@ # MAIN class MathTextParser(object): - """ - Parse the math expression s, return the (bbox, fonts) tuple needed - to render it. - - fontsize must be in points - - return is width, height, fonts - """ _parser = None _backend_mapping = { @@ -2701,10 +2753,23 @@ } def __init__(self, output): + """ + Create a MathTextParser for the given backend *output*. + """ self._output = output.lower() self._cache = maxdict(50) def parse(self, s, dpi = 72, prop = None): + """ + Parse the given math expression *s* at the given *dpi*. If + *prop* is provided, it is a + :class:`~matplotlib.font_manager.FontProperties` object + specifying the "default" font to use in the math expression, + used for all non-math text. + + The results are cached, so multiple calls to :meth:`parse` + with the same expression should be fast. + """ if prop is None: prop = FontProperties() cacheKey = (s, dpi, hash(prop)) @@ -2712,7 +2777,7 @@ if result is not None: return result - if self._output == 'PS' and rcParams['ps.useafm']: + if self._output == 'ps' and rcParams['ps.useafm']: font_output = StandardPsFonts(prop) else: backend = self._backend_mapping[self._output]() @@ -2748,6 +2813,15 @@ def to_mask(self, texstr, dpi=120, fontsize=14): """ + *texstr* + A valid mathtext string, eg r'IQ: $\sigma_i=15$' + + *dpi* + The dots-per-inch to render the text + + *fontsize* + The font size in points + Returns a tuple (*array*, *depth*) - *array* is an NxM uint8 alpha ubyte mask array of @@ -2755,15 +2829,6 @@ - depth is the offset of the baseline from the bottom of the image in pixels. - - ''texstr'' - A valid mathtext string, eg r'IQ: $\sigma_i=15$' - - ''dpi'' - The dots-per-inch to render the text - - ''fontsize'' - The font size in points """ assert(self._output=="bitmap") prop = FontProperties(size=fontsize) @@ -2774,27 +2839,25 @@ def to_rgba(self, texstr, color='black', dpi=120, fontsize=14): """ - Returns a tuple (*array*, *depth*) + *texstr* + A valid mathtext string, eg r'IQ: $\sigma_i=15$' - - *array* is an NxMx4 RGBA array of ubyte rasterized tex. + *color* + Any matplotlib color argument - - depth is the offset of the baseline from the bottom of the - image in pixels. + *dpi* + The dots-per-inch to render the text - Returns a tuple (array, depth), where depth is the offset of - the baseline from the bottom of the image. + *fontsize* + The font size in points - ''texstr'' - A valid mathtext string, eg r'IQ: $\sigma_i=15$' + Returns a tuple (*array*, *depth*) - ''color'' - A valid matplotlib color argument + - *array* is an NxM uint8 alpha ubyte mask array of + rasterized tex. - ''dpi'' - The dots-per-inch to render the text - - ''fontsize'' - The font size in points + - depth is the offset of the baseline from the bottom of the + image in pixels. """ x, depth = self.to_mask(texstr, dpi=dpi, fontsize=fontsize) @@ -2813,22 +2876,24 @@ Returns the offset of the baseline from the bottom of the image in pixels. - ''filename'' + *filename* A writable filename or fileobject - ''texstr'' + *texstr* A valid mathtext string, eg r'IQ: $\sigma_i=15$' - ''color'' + *color* A valid matplotlib color argument - ''dpi'' + *dpi* The dots-per-inch to render the text - ''fontsize'' + *fontsize* The font size in points - """ + Returns the offset of the baseline from the bottom of the + image in pixels. + """ rgba, depth = self.to_rgba(texstr, color=color, dpi=dpi, fontsize=fontsize) numrows, numcols, tmp = rgba.shape @@ -2840,16 +2905,15 @@ Returns the offset of the baseline from the bottom of the image in pixels. - ''texstr'' + *texstr* A valid mathtext string, eg r'IQ: $\sigma_i=15$' - ''dpi'' + *dpi* The dots-per-inch to render the text - ''fontsize'' + *fontsize* The font size in points - - """ + """ assert(self._output=="bitmap") prop = FontProperties(size=fontsize) ftimage, depth = self.parse(texstr, dpi=dpi, prop=prop) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |