From: <md...@us...> - 2007-07-30 16:41:56
|
Revision: 3631 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3631&view=rev Author: mdboom Date: 2007-07-30 09:41:53 -0700 (Mon, 30 Jul 2007) Log Message: ----------- Major refactor to separate out backend-specific stuff from fontset-specific stuff. What used to be the Fonts class heirarchy is now both a Fonts heirarchy and MathtextBackend heirarchy. This should be further improved in the future when the backends themselves are refactored to be more consistent. Now uses the multi-sized delimiters in the BaKoMa fonts. This prevents the square root symbol from getting overly wide as it gets tall. (As auto-sized parens, brackets and braces etc.) Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/_mathtext_data.py trunk/matplotlib/lib/matplotlib/mathtext.py Modified: trunk/matplotlib/lib/matplotlib/_mathtext_data.py =================================================================== --- trunk/matplotlib/lib/matplotlib/_mathtext_data.py 2007-07-30 07:03:47 UTC (rev 3630) +++ trunk/matplotlib/lib/matplotlib/_mathtext_data.py 2007-07-30 16:41:53 UTC (rev 3631) @@ -14,8 +14,9 @@ print charcode, glyphind """ -latex_to_bakoma = { +from sets import Set +latex_to_bakoma = { r'\oint' : ('cmex10', 45), r'\bigodot' : ('cmex10', 50), r'\bigoplus' : ('cmex10', 55), @@ -29,13 +30,7 @@ r'\bigwedge' : ('cmex10', 4), r'\bigvee' : ('cmex10', 37), r'\coprod' : ('cmex10', 42), - r'\Leftbracket' : ('cmex10', 29), - r'\Rightbracket' : ('cmex10', 61), - r'\Leftbrace' : ('cmex10', 43), - r'\Rightbrace' : ('cmex10', 16), r'\sqrt' : ('cmex10', 48), - r'\Sqrt' : ('cmex10', 21), - r'\SQRT' : ('cmex10', 53), r'\leftbrace' : ('cmex10', 92), r'{' : ('cmex10', 92), r'\{' : ('cmex10', 92), @@ -44,16 +39,6 @@ r'\}' : ('cmex10', 130), r'\leftangle' : ('cmex10', 97), r'\rightangle' : ('cmex10', 64), - r'\Leftparen' : ('cmex10', 112), - r'\Rightparen' : ('cmex10', 81), - r'\LEFTparen' : ('cmex10', 119), - r'\RIGHTparen' : ('cmex10', 87), - r'\LEFTbracket' : ('cmex10', 125), - r'\RIGHTbracket' : ('cmex10', 93), - r'\LEFTbrace' : ('cmex10', 70), - r'\RIGHTbrace' : ('cmex10', 107), - r'\LEFTangle' : ('cmex10', 76), - r'\RIGHTangle' : ('cmex10', 113), r'\omega' : ('cmmi10', 29), r'\varepsilon' : ('cmmi10', 20), @@ -144,15 +129,15 @@ # these are mathml names, I think. I'm just using them for the # tex methods noted - r'\circumflexaccent' : ('cmr10', 124), # for \hat - r'\combiningbreve' : ('cmr10', 81), # for \breve - r'\combiningoverline' : ('cmr10', 131), # for \bar - r'\combininggraveaccent' : ('cmr10', 114), # for \grave - r'\combiningacuteaccent' : ('cmr10', 63), # for \accute - r'\combiningdiaeresis' : ('cmr10', 91), # for \ddot - r'\combiningtilde' : ('cmr10', 75), # for \tilde + r'\circumflexaccent' : ('cmr10', 124), # for \hat + r'\combiningbreve' : ('cmr10', 81), # for \breve + r'\combiningoverline' : ('cmr10', 131), # for \bar + r'\combininggraveaccent' : ('cmr10', 114), # for \grave + r'\combiningacuteaccent' : ('cmr10', 63), # for \accute + r'\combiningdiaeresis' : ('cmr10', 91), # for \ddot + r'\combiningtilde' : ('cmr10', 75), # for \tilde r'\combiningrightarrowabove' : ('cmmi10', 110), # for \vec - r'\combiningdotabove' : ('cmr10', 26), # for \dot + r'\combiningdotabove' : ('cmr10', 26), # for \dot r'\leftarrow' : ('cmsy10', 10), r'\uparrow' : ('cmsy10', 25), @@ -199,8 +184,6 @@ r'\rceil' : ('cmsy10', 81), r'\lbrace' : ('cmsy10', 92), r'\rbrace' : ('cmsy10', 105), - r'\langle' : ('cmsy10', 3), - r'\rangle' : ('cmsy10', 88), r'\mid' : ('cmsy10', 47), r'\vert' : ('cmsy10', 47), r'\Vert' : ('cmsy10', 44), @@ -258,417 +241,6 @@ r'\spadesuit' : ('cmsy10', 7), } -# the kerning dictionary in design space units -cmkern = { - 'cmr10' : - { - ('A', 't') : -0.027779, - ('A', 'C') : -0.027779, - ('A', 'O') : -0.027779, - ('A', 'G') : -0.027779, - ('A', 'U') : -0.027779, - ('A', 'Q') : -0.027779, - ('A', 'T') : -0.083334, - ('A', 'Y') : -0.083334, - ('A', 'V') : -0.111112, - ('A', 'W') : -0.111112, - ('D', 'X') : -0.027779, - ('D', 'W') : -0.027779, - ('D', 'A') : -0.027779, - ('D', 'V') : -0.027779, - ('D', 'Y') : -0.027779, - ('F', 'o') : -0.083334, - ('F', 'e') : -0.083334, - ('F', 'u') : -0.083334, - ('F', 'r') : -0.083334, - ('F', 'a') : -0.083334, - ('F', 'A') : -0.111112, - ('F', 'O') : -0.027779, - ('F', 'C') : -0.027779, - ('F', 'G') : -0.027779, - ('F', 'Q') : -0.027779, - ('I', 'I') : 0.027779, - ('K', 'O') : -0.027779, - ('K', 'C') : -0.027779, - ('K', 'G') : -0.027779, - ('K', 'Q') : -0.027779, - ('L', 'T') : -0.083334, - ('L', 'Y') : -0.083334, - ('L', 'V') : -0.111112, - ('L', 'W') : -0.111112, - ('O', 'X') : -0.027779, - ('O', 'W') : -0.027779, - ('O', 'A') : -0.027779, - ('O', 'V') : -0.027779, - ('O', 'Y') : -0.027779, - ('P', 'A') : -0.083334, - ('P', 'o') : -0.027779, - ('P', 'e') : -0.027779, - ('P', 'a') : -0.027779, - ('R', 't') : -0.027779, - ('R', 'C') : -0.027779, - ('R', 'O') : -0.027779, - ('R', 'G') : -0.027779, - ('R', 'U') : -0.027779, - ('R', 'Q') : -0.027779, - ('R', 'T') : -0.083334, - ('R', 'Y') : -0.083334, - ('R', 'V') : -0.111112, - ('R', 'W') : -0.111112, - ('T', 'y') : -0.027779, - ('T', 'e') : -0.083334, - ('T', 'o') : -0.083334, - ('T', 'r') : -0.083334, - ('T', 'a') : -0.083334, - ('T', 'A') : -0.083334, - ('T', 'u') : -0.083334, - ('V', 'o') : -0.083334, - ('V', 'e') : -0.083334, - ('V', 'u') : -0.083334, - ('V', 'r') : -0.083334, - ('V', 'a') : -0.083334, - ('V', 'A') : -0.111112, - ('V', 'O') : -0.027779, - ('V', 'C') : -0.027779, - ('V', 'G') : -0.027779, - ('V', 'Q') : -0.027779, - ('W', 'o') : -0.083334, - ('W', 'e') : -0.083334, - ('W', 'u') : -0.083334, - ('W', 'r') : -0.083334, - ('W', 'a') : -0.083334, - ('W', 'A') : -0.111112, - ('W', 'O') : -0.027779, - ('W', 'C') : -0.027779, - ('W', 'G') : -0.027779, - ('W', 'Q') : -0.027779, - ('X', 'O') : -0.027779, - ('X', 'C') : -0.027779, - ('X', 'G') : -0.027779, - ('X', 'Q') : -0.027779, - ('Y', 'e') : -0.083334, - ('Y', 'o') : -0.083334, - ('Y', 'r') : -0.083334, - ('Y', 'a') : -0.083334, - ('Y', 'A') : -0.083334, - ('Y', 'u') : -0.083334, - ('a', 'v') : -0.027779, - ('a', 'j') : 0.055555, - ('a', 'y') : -0.027779, - ('a', 'w') : -0.027779, - ('b', 'e') : 0.027779, - ('b', 'o') : 0.027779, - ('b', 'x') : -0.027779, - ('b', 'd') : 0.027779, - ('b', 'c') : 0.027779, - ('b', 'q') : 0.027779, - ('b', 'v') : -0.027779, - ('b', 'j') : 0.055555, - ('b', 'y') : -0.027779, - ('b', 'w') : -0.027779, - ('c', 'h') : -0.027779, - ('c', 'k') : -0.027779, - ('g', 'j') : 0.027779, - ('h', 't') : -0.027779, - ('h', 'u') : -0.027779, - ('h', 'b') : -0.027779, - ('h', 'y') : -0.027779, - ('h', 'v') : -0.027779, - ('h', 'w') : -0.027779, - ('k', 'a') : -0.055555, - ('k', 'e') : -0.027779, - ('k', 'a') : -0.027779, - ('k', 'o') : -0.027779, - ('k', 'c') : -0.027779, - ('m', 't') : -0.027779, - ('m', 'u') : -0.027779, - ('m', 'b') : -0.027779, - ('m', 'y') : -0.027779, - ('m', 'v') : -0.027779, - ('m', 'w') : -0.027779, - ('n', 't') : -0.027779, - ('n', 'u') : -0.027779, - ('n', 'b') : -0.027779, - ('n', 'y') : -0.027779, - ('n', 'v') : -0.027779, - ('n', 'w') : -0.027779, - ('o', 'e') : 0.027779, - ('o', 'o') : 0.027779, - ('o', 'x') : -0.027779, - ('o', 'd') : 0.027779, - ('o', 'c') : 0.027779, - ('o', 'q') : 0.027779, - ('o', 'v') : -0.027779, - ('o', 'j') : 0.055555, - ('o', 'y') : -0.027779, - ('o', 'w') : -0.027779, - ('p', 'e') : 0.027779, - ('p', 'o') : 0.027779, - ('p', 'x') : -0.027779, - ('p', 'd') : 0.027779, - ('p', 'c') : 0.027779, - ('p', 'q') : 0.027779, - ('p', 'v') : -0.027779, - ('p', 'j') : 0.055555, - ('p', 'y') : -0.027779, - ('p', 'w') : -0.027779, - ('t', 'y') : -0.027779, - ('t', 'w') : -0.027779, - ('u', 'w') : -0.027779, - ('v', 'a') : -0.055555, - ('v', 'e') : -0.027779, - ('v', 'a') : -0.027779, - ('v', 'o') : -0.027779, - ('v', 'c') : -0.027779, - ('w', 'e') : -0.027779, - ('w', 'a') : -0.027779, - ('w', 'o') : -0.027779, - ('w', 'c') : -0.027779, - ('y', 'o') : -0.027779, - ('y', 'e') : -0.027779, - ('y', 'a') : -0.027779, - }, - 'cmex10' : {}, - 'cmtt10' : {}, - 'cmsy10' : {}, - 'cmmi10' : - { - ('9', 'A') : -0.055555, - ('9', 'M') : -0.055555, - ('9', 'N') : -0.055555, - ('9', 'Y') : 0.055555, - ('9', 'Z') : -0.055555, - ('d', 'Y') : 0.055555, - ('d', 'Z') : -0.055555, - ('d', 'j') : -0.111112, - ('d', 'f') : -0.166667, - }, - } -ams_type1 = { - r'\Leftbracket' : ( 'cmex10', '02'), - r'\Rightbracket' : ( 'cmex10', '03'), - r'\Leftbrace' : ( 'cmex10', '08'), - r'\leftbrace' : ( 'cmex10', '08'), - '{' : ( 'cmex10', '08'), - r'\Rightbrace' : ( 'cmex10', '09'), - r'\rightbrace' : ( 'cmex10', '09'), - '}' : ( 'cmex10', '09'), - r'\Leftparen' : ( 'cmex10', '10'), - r'\Rightparen' : ( 'cmex10', '11'), - r'\LEFTparen' : ( 'cmex10', '20'), - r'\RIGHTparen' : ( 'cmex10', '21'), - r'\LEFTbracket' : ( 'cmex10', '22'), - r'\RIGHTbracket' : ( 'cmex10', '23'), - '6' : ( 'cmex10', '26'), - '(' : ( 'cmex10', '28'), - r'\LEFTbrace' : ( 'cmex10', '28'), - r'\leftparen' : ( 'cmex10', '28'), - ')' : ( 'cmex10', '29'), - r'\RIGHTbrace' : ( 'cmex10', '29'), - r'\rightparen' : ( 'cmex10', '29'), - r'\LEFTangle' : ( 'cmex10', '2A'), - '+' : ( 'cmex10', '2B'), - '0' : ( 'cmex10', '30'), - '1' : ( 'cmex10', '31'), - '2' : ( 'cmex10', '32'), - '3' : ( 'cmex10', '33'), - '4' : ( 'cmex10', '34'), - '5' : ( 'cmex10', '35'), - '7' : ( 'cmex10', '37'), - '8' : ( 'cmex10', '38'), - '9' : ( 'cmex10', '39'), - ':' : ( 'cmex10', '3A'), - ';' : ( 'cmex10', '3B'), - '=' : ( 'cmex10', '3D'), - r'\leftangle' : ( 'cmex10', '44'), - r'\rightangle' : ( 'cmex10', '45'), - r'\oint' : ( 'cmex10', '49'), - r'\bigodot' : ( 'cmex10', '4B'), - r'\bigoplus' : ( 'cmex10', '4D'), - r'\bigotimes' : ( 'cmex10', '4F'), - r'\sum' : ( 'cmex10', '58'), - r'\prod' : ( 'cmex10', '59'), - r'\int' : ( 'cmex10', '5A'), - '[' : ( 'cmex10', '5B'), - r'\bigcup' : ( 'cmex10', '5B'), - r'\leftbracket' : ( 'cmex10', '5B'), - r'\bigcap' : ( 'cmex10', '5C'), - r'\biguplus' : ( 'cmex10', '5D'), - r'\rightbracket' : ( 'cmex10', '5D'), - ']' : ( 'cmex10', '5D'), - r'\bigwedge' : ( 'cmex10', '5E'), - r'\bigvee' : ( 'cmex10', '5F'), - r'\coprod' : ( 'cmex10', '61'), - r'\Sqrt' : ( 'cmex10', '70'), - r'\sqrt' : ( 'cmex10', '70'), - r'\SQRT' : ( 'cmex10', '72'), - r'\Sigma' : ( 'cmmi10', '06'), - r'\Upsilon' : ( 'cmmi10', '07'), - r'\Phi' : ( 'cmmi10', '08'), - r'\Psi' : ( 'cmmi10', '09'), - r'\alpha' : ( 'cmmi10', '0B'), - r'\beta' : ( 'cmmi10', '0C'), - r'\gamma' : ( 'cmmi10', '0D'), - r'\delta' : ( 'cmmi10', '0E'), - r'\epsilon' : ( 'cmmi10', '0F'), - r'\zeta' : ( 'cmmi10', '10'), - r'\eta' : ( 'cmmi10', '11'), - r'\theta' : ( 'cmmi10', '12'), - r'\iota' : ( 'cmmi10', '13'), - r'\lambda' : ( 'cmmi10', '15'), - r'\mu' : ( 'cmmi10', '16'), - r'\nu' : ( 'cmmi10', '17'), - r'\xi' : ( 'cmmi10', '18'), - r'\pi' : ( 'cmmi10', '19'), - r'\kappa' : ( 'cmmi10', '19'), - r'\rho' : ( 'cmmi10', '1A'), - r'\sigma' : ( 'cmmi10', '1B'), - r'\tau' : ( 'cmmi10', '1C'), - r'\upsilon' : ( 'cmmi10', '1D'), - r'\phi' : ( 'cmmi10', '1E'), - r'\chi' : ( 'cmmi10', '1F'), - r'\psi' : ( 'cmmi10', '20'), - r'\omega' : ( 'cmmi10', '21'), - r'\varepsilon' : ( 'cmmi10', '22'), - r'\vartheta' : ( 'cmmi10', '23'), - r'\varrho' : ( 'cmmi10', '25'), - r'\varsigma' : ( 'cmmi10', '26'), - r'\varphi' : ( 'cmmi10', '27'), - r'\leftharpoonup' : ( 'cmmi10', '28'), - r'\leftharpoondown' : ( 'cmmi10', '29'), - r'\rightharpoonup' : ( 'cmmi10', '2A'), - r'\rightharpoondown' : ( 'cmmi10', '2B'), - r'\triangleright' : ( 'cmmi10', '2E'), - r'\triangleleft' : ( 'cmmi10', '2F'), - '.' : ( 'cmmi10', '3A'), - ',' : ( 'cmmi10', '3B'), - '<' : ( 'cmmi10', '3C'), - '/' : ( 'cmmi10', '3D'), - '>' : ( 'cmmi10', '3E'), - r'\flat' : ( 'cmmi10', '5B'), - r'\natural' : ( 'cmmi10', '5C'), - r'\sharp' : ( 'cmmi10', '5D'), - r'\smile' : ( 'cmmi10', '5E'), - r'\frown' : ( 'cmmi10', '5F'), - r'\ell' : ( 'cmmi10', '60'), - r'\imath' : ( 'cmmi10', '7B'), - r'\jmath' : ( 'cmmi10', '7C'), - r'\wp' : ( 'cmmi10', '7D'), - r'\Gamma' : ( 'cmr10', '00'), - r'\Delta' : ( 'cmr10', '01'), - r'\Theta' : ( 'cmr10', '02'), - r'\Lambda' : ( 'cmr10', '03'), - r'\Xi' : ( 'cmr10', '04'), - r'\Pi' : ( 'cmr10', '05'), - r'\Omega' : ( 'cmr10', '0A'), - '-' : ( 'cmsy10', '00'), - r'\cdot' : ( 'cmsy10', '01'), - r'\times' : ( 'cmsy10', '02'), - '*' : ( 'cmsy10', '03'), - r'\ast' : ( 'cmsy10', '03'), - r'\div' : ( 'cmsy10', '04'), - r'\diamond' : ( 'cmsy10', '05'), - r'\pm' : ( 'cmsy10', '06'), - r'\mp' : ( 'cmsy10', '07'), - r'\oplus' : ( 'cmsy10', '08'), - r'\ominus' : ( 'cmsy10', '09'), - r'\otimes' : ( 'cmsy10', '0A'), - r'\oslash' : ( 'cmsy10', '0B'), - r'\odot' : ( 'cmsy10', '0C'), - r'\bigcirc' : ( 'cmsy10', '0D'), - r'\circ' : ( 'cmsy10', '0E'), - r'\bullet' : ( 'cmsy10', '0F'), - r'\simeq' : ( 'cmsy10', '10'), - r'\asymp' : ( 'cmsy10', '11'), - r'\subseteq' : ( 'cmsy10', '12'), - r'\supseteq' : ( 'cmsy10', '13'), - r'\leq' : ( 'cmsy10', '14'), - r'\geq' : ( 'cmsy10', '15'), - r'\preceq' : ( 'cmsy10', '16'), - r'\succeq' : ( 'cmsy10', '17'), - r'\sim' : ( 'cmsy10', '18'), - r'\approx' : ( 'cmsy10', '19'), - r'\subset' : ( 'cmsy10', '1A'), - r'\supset' : ( 'cmsy10', '1B'), - r'\ll' : ( 'cmsy10', '1C'), - r'\gg' : ( 'cmsy10', '1D'), - r'\prec' : ( 'cmsy10', '1E'), - r'\succ' : ( 'cmsy10', '1F'), - r'\rightarrow' : ( 'cmsy10', '20'), - r'\leftarrow' : ( 'cmsy10', '21'), - r'\uparrow' : ( 'cmsy10', '22'), - r'\downarrow' : ( 'cmsy10', '23'), - r'\leftrightarrow' : ( 'cmsy10', '24'), - r'\nearrow' : ( 'cmsy10', '25'), - r'\searrow' : ( 'cmsy10', '26'), - r'\Leftarrow' : ( 'cmsy10', '28'), - r'\Rightarrow' : ( 'cmsy10', '29'), - r'\Uparrow' : ( 'cmsy10', '2A'), - r'\Downarrow' : ( 'cmsy10', '2B'), - r'\Leftrightarrow' : ( 'cmsy10', '2C'), - r'\nwarrow' : ( 'cmsy10', '2D'), - r'\swarrow' : ( 'cmsy10', '2E'), - r'\propto' : ( 'cmsy10', '2F'), - r'\prime' : ( 'cmsy10', '30'), - r'\infty' : ( 'cmsy10', '31'), - r'\in' : ( 'cmsy10', '32'), - r'\ni' : ( 'cmsy10', '33'), - r'\bigtriangleup' : ( 'cmsy10', '34'), - r'\bigtriangledown' : ( 'cmsy10', '35'), - r'\slash' : ( 'cmsy10', '36'), - r'\forall' : ( 'cmsy10', '38'), - r'\exists' : ( 'cmsy10', '39'), - r'\neg' : ( 'cmsy10', '3A'), - r'\emptyset' : ( 'cmsy10', '3B'), - r'\Re' : ( 'cmsy10', '3C'), - r'\Im' : ( 'cmsy10', '3D'), - r'\top' : ( 'cmsy10', '3E'), - r'\bot' : ( 'cmsy10', '3F'), - r'\aleph' : ( 'cmsy10', '40'), - r'\cup' : ( 'cmsy10', '5B'), - r'\cap' : ( 'cmsy10', '5C'), - r'\uplus' : ( 'cmsy10', '5D'), - r'\wedge' : ( 'cmsy10', '5E'), - r'\vee' : ( 'cmsy10', '5F'), - r'\vdash' : ( 'cmsy10', '60'), - r'\dashv' : ( 'cmsy10', '61'), - r'\lfloor' : ( 'cmsy10', '62'), - r'\rfloor' : ( 'cmsy10', '63'), - r'\lceil' : ( 'cmsy10', '64'), - r'\rceil' : ( 'cmsy10', '65'), - r'\lbrace' : ( 'cmsy10', '66'), - r'\rbrace' : ( 'cmsy10', '67'), - r'\langle' : ( 'cmsy10', '68'), - r'\rangle' : ( 'cmsy10', '69'), - r'\mid' : ( 'cmsy10', '6A'), - r'\vert' : ( 'cmsy10', '6A'), - r'\Vert' : ( 'cmsy10', '6B'), - r'\updownarrow' : ( 'cmsy10', '6C'), - r'\Updownarrow' : ( 'cmsy10', '6D'), - r'\backslash' : ( 'cmsy10', '6E'), - r'\wr' : ( 'cmsy10', '6F'), - r'\nabla' : ( 'cmsy10', '72'), - r'\sqcup' : ( 'cmsy10', '74'), - r'\sqcap' : ( 'cmsy10', '75'), - r'\sqsubseteq' : ( 'cmsy10', '76'), - r'\sqsupseteq' : ( 'cmsy10', '77'), - r'\S' : ( 'cmsy10', '78'), - r'\dag' : ( 'cmsy10', '79'), - r'\ddag' : ( 'cmsy10', '7A'), - r'\P' : ( 'cmsy10', '7B'), - r'\clubsuit' : ( 'cmsy10', '7C'), - r'\diamondsuit' : ( 'cmsy10', '7D'), - r'\heartsuit' : ( 'cmsy10', '7E'), - r'\spadesuit' : ( 'cmsy10', '7F'), -} - -""" -no mlname for \RIGHTangle -no type1 key \equiv # could not find in ASM -no type1 key \kappa # could not find kappa -no type1 key \RIGHTangle # could not find -""" - latex_to_standard = { r'\cong' : ('psyr', 64), r'\Delta' : ('psyr', 68), @@ -757,17 +329,60 @@ r'\langle' : ('psyr', 225), r'\Sigma' : ('psyr', 229), r'\sum' : ('psyr', 229), - # these are mathml names, I think. I'm just using them for the - # tex methods noted - r'\circumflexaccent' : ('pncri8a', 124), # for \hat - r'\combiningbreve' : ('pncri8a', 81), # for \breve - r'\combininggraveaccent' : ('pncri8a', 114), # for \grave - r'\combiningacuteaccent' : ('pncri8a', 63), # for \accute - r'\combiningdiaeresis' : ('pncri8a', 91), # for \ddot - r'\combiningtilde' : ('pncri8a', 75), # for \tilde + r'\forall' : ('psyr', 34), + r'\exists' : ('psyr', 36), + r'\lceil' : ('psyr', 233), + r'\lbrace' : ('psyr', 123), + r'\Psi' : ('psyr', 89), + r'\bot' : ('psyr', 0136), + r'\Omega' : ('psyr', 0127), + r'\leftbracket' : ('psyr', 0133), + r'\rightbracket' : ('psyr', 0135), + r'\leftbrace' : ('psyr', 123), + r'\leftparen' : ('psyr', 050), + r'\prime' : ('psyr', 0242), + r'\sharp' : ('psyr', 043), + r'\slash' : ('psyr', 057), + r'\Lamda' : ('psyr', 0114), + r'\neg' : ('psyr', 0330), + r'\Upsilon' : ('psyr', 0241), + r'\rightbrace' : ('psyr', 0175), + r'\rfloor' : ('psyr', 0373), + r'\lambda' : ('psyr', 0154), + r'\to' : ('psyr', 0256), + r'\Xi' : ('psyr', 0130), + r'\emptyset' : ('psyr', 0306), + r'\lfloor' : ('psyr', 0353), + r'\rightparen' : ('psyr', 051), + r'\rceil' : ('psyr', 0371), + r'\ni' : ('psyr', 047), + r'\epsilon' : ('psyr', 0145), + r'\Theta' : ('psyr', 0121), + r'\langle' : ('psyr', 0341), + r'\leftangle' : ('psyr', 0341), + r'\rangle' : ('psyr', 0361), + r'\rightangle' : ('psyr', 0361), + r'\rbrace' : ('psyr', 0175), + r'\circ' : ('psyr', 0260), + r'\diamond' : ('psyr', 0340), + r'\mu' : ('psyr', 0155), + r'\mid' : ('psyr', 0352), + r'\imath' : ('pncri8a', 105), + r'\%' : ('pncr8a', 37), + r'\$' : ('pncr8a', 36), + r'\{' : ('pncr8a', 123), + r'\}' : ('pncr8a', 125), + r'\backslash' : ('pncr8a', 92), + r'\ast' : ('pncr8a', 42), + + r'\circumflexaccent' : ('pncri8a', 124), # for \hat + r'\combiningbreve' : ('pncri8a', 81), # for \breve + r'\combininggraveaccent' : ('pncri8a', 114), # for \grave + r'\combiningacuteaccent' : ('pncri8a', 63), # for \accute + r'\combiningdiaeresis' : ('pncri8a', 91), # for \ddot + r'\combiningtilde' : ('pncri8a', 75), # for \tilde r'\combiningrightarrowabove' : ('pncri8a', 110), # for \vec - r'\combiningdotabove' : ('pncri8a', 26), # for \dot - r'\imath' : ('pncri8a', 105) + r'\combiningdotabove' : ('pncri8a', 26), # for \dot } # Automatically generated. @@ -2214,7 +1829,7 @@ 'updownarrows': 8645, 'heartsuit': 9825, 'trianglelefteq': 8884, -'ddagger': 8225, +'ddag': 8225, 'sqsubseteq': 8849, 'mapsfrom': 8612, 'boxbar': 9707, @@ -2516,7 +2131,7 @@ 'succcurlyeq': 8829, 'gamma': 947, 'scrR': 8475, -'dagger': 8224, +'dag': 8224, 'thickspace': 8197, 'frakZ': 8488, 'lessdot': 8918, @@ -2589,7 +2204,39 @@ 'divideontimes': 8903, 'lbrack': 91, 'textquotedblright': 8221, -'Colon': 8759} +'Colon': 8759, +'%': 37, +'$': 36, +'{': 123, +'}': 125, +'imath': 0xfd, +'circumflexaccent' : 770, +'combiningbreve' : 774, +'combiningoverline' : 772, +'combininggraveaccent' : 768, +'combiningacuteaccent' : 764, +'combiningdiaeresis' : 776, +'combiningtilde' : 771, +'combiningrightarrowabove' : 8407, +'combiningdotabove' : 775, +'to': 8594, +'succeq': 8829, +'emptyset': 8709, +'leftparen': 40, +'rightparen': 41, +'bigoplus': 10753, +'leftangle': 10216, +'rightangle': 10217, +'leftbrace': 124, +'rightbrace': 125, +'jmath': 567, +'bigodot': 10752, +'preceq': 8828, +'biguplus': 10756, +'epsilon': 949, +'vartheta': 977, +'bigotimes': 10754 +} uni2tex = dict([(v,k) for k,v in tex2uni.items()]) @@ -2692,7 +2339,7 @@ 'updownarrows': 'uni21C5', 'heartsuit': 'uni2661', 'trianglelefteq': 'uni22B4', -'ddagger': 'daggerdbl', +'ddag': 'daggerdbl', 'sqsubseteq': 'uni2291', 'mapsfrom': 'uni21A4', 'boxbar': 'uni25EB', @@ -2994,7 +2641,7 @@ 'succcurlyeq': 'uni227D', 'gamma': 'uni03B3', 'scrR': 'uni211B', -'dagger': 'dagger', +'dag': 'dagger', 'thickspace': 'uni2005', 'frakZ': 'uni2128', 'lessdot': 'uni22D6', @@ -3070,4 +2717,3 @@ 'Colon': 'uni2237'} type12tex = dict([(v,k) for k,v in tex2type1.items()]) - Modified: trunk/matplotlib/lib/matplotlib/mathtext.py =================================================================== --- trunk/matplotlib/lib/matplotlib/mathtext.py 2007-07-30 07:03:47 UTC (rev 3630) +++ trunk/matplotlib/lib/matplotlib/mathtext.py 2007-07-30 16:41:53 UTC (rev 3631) @@ -131,7 +131,9 @@ from __future__ import division import os, sys from cStringIO import StringIO +from math import floor, ceil from sets import Set +from unicodedata import category from warnings import warn from matplotlib import verbose @@ -146,7 +148,7 @@ is_string_like from matplotlib.ft2font import FT2Font, KERNING_UNFITTED from matplotlib.font_manager import fontManager, FontProperties -from matplotlib._mathtext_data import latex_to_bakoma, cmkern, \ +from matplotlib._mathtext_data import latex_to_bakoma, \ latex_to_standard, tex2uni, type12uni, tex2type1, uni2type1 from matplotlib import get_data_path, rcParams @@ -164,14 +166,6 @@ ############################################################################## # FONTS -def font_open(filename): - ext = filename.rsplit('.',1)[1] - if ext == 'afm': - return AFM(str(filename)) - else: - return FT2Font(str(filename)) - - def get_unicode_index(symbol): """get_unicode_index(symbol) -> integer @@ -187,10 +181,6 @@ try:# Is symbol a TeX symbol (i.e. \alpha) return tex2uni[symbol.strip("\\")] except KeyError: - pass - try:# Is symbol a Type1 name (i.e. degree)? If not raise error - return type12uni[symbol] - except KeyError: message = """'%(symbol)s' is not a valid Unicode character or TeX/Type1 symbol"""%locals() raise ValueError, message @@ -219,574 +209,473 @@ # The user did not suply a valid symbol, show usage raise ValueError, get_type1_name.__doc__ +class MathtextBackend(object): + def __init__(self): + fonts_object = None -class Fonts: - """ - An abstract base class for fonts that want to render mathtext + def set_canvas_size(self, w, h): + 'Dimension the drawing canvas; may be a noop' + self.width = w + self.height = h - The class must be able to take symbol keys and font file names and - return the character metrics as well as do the drawing - """ - - def get_kern(self, facename, symleft, symright, fontsize, dpi): - """ - Get the kerning distance for font between symleft and symright. + def render_glyph(self, ox, oy, info): + raise NotImplementedError() - facename is one of tt, it, rm, cal or None + def render_filled_rect(self, x1, y1, x2, y2): + raise NotImplementedError() - sym is a single symbol(alphanum, punct) or a special symbol - like \sigma. - - """ - return 0 - - def get_metrics(self, facename, sym, fontsize, dpi): - """ - facename is one of tt, it, rm, cal or None - - sym is a single symbol(alphanum, punct) or a special symbol - like \sigma. - - fontsize is in points - - Return object has attributes - see - http://www.freetype.org/freetype2/docs/tutorial/step2.html for - a pictoral representation of these attributes - - advance - height - width - xmin, xmax, ymin, ymax - the ink rectangle of the glyph - """ - raise NotImplementedError('Derived must override') - + def get_results(self): + """Return a backend specific tuple of things to return to the + backend after all processing is done.""" + raise NotImplementedError() + +class MathtextBackendAgg(MathtextBackend): def set_canvas_size(self, w, h): - 'Dimension the drawing canvas; may be a noop' - self.width, self.height = w, h + MathtextBackend.set_canvas_size(self, w, h) + for font in self.fonts_object.get_fonts(): + font.set_bitmap_size(int(w), int(h)) - def render(self, ox, oy, facename, sym, fontsize, dpi): - pass + def render_glyph(self, ox, oy, info): + info.font.draw_glyph_to_bitmap( + int(ox), int(oy - info.metrics.ymax), info.glyph) def render_rect_filled(self, x1, y1, x2, y2): - pass + font = self.fonts_object.get_fonts()[0] + font.draw_rect_filled( + floor(max(0, x1 - 1)), + floor(y1), + ceil(max(x2 - 1, x1)), + ceil(max(y2 - 1, y1))) + + def get_results(self): + return (self.width, + self.height, + self.fonts_object.get_fonts(), + self.fonts_object.get_used_characters()) - def get_used_characters(self): - return {} +class MathtextBackendPs(MathtextBackend): + def __init__(self): + self.pswriter = StringIO() - -class DummyFonts(Fonts): - 'dummy class for debugging parser' - def get_metrics(self, font, sym, fontsize, dpi): - - metrics = Bunch( - advance = 0, - height = 0, - width = 0, - xmin = 0, - xmax = 0, - ymin = 0, - ymax = 0, - ) - return metrics - - -class UnicodeFonts(Fonts): - """An abstract base class for handling Unicode fonts. - -Specific terminology: - * fontface: an FT2Font object, corresponding to a facename - * facename: a string that defines the (type)face's name - 'rm', 'it' etc. - * filename: a string that is used for generating a fontface object - * symbol*: a single Unicode character or a TeX command, - or to be precise, a TeX symbol command like \alpha (but not \frac) or - even a Type1/PS name - * filenamesd: a dict that maps the face's name to the filename: - filenamesd = { 'cal' : 'fontnamecal.ext', - 'rm' : 'fontnamerm.ext', - 'tt' : 'fontnamett.ext', - 'it' : 'fontnameit.ext', - None : 'fontnamesmth.ext'} - filenamesd should be declared as a class atribute - * glyphdict: a dict used for caching of glyph specific data - * fonts: a dict of facename -> fontface pairs - * charmaps: a dict of facename -> charmap pairs. Charmap maps character - codes to glyph indices - * glyphmaps: a dict of facename -> glyphmap pairs. A glyphmap is an - inverted charmap - * output: a string in ['Agg','SVG','PS'], coresponding to the backends - * index: Fontfile specific index of a glyph. - -""" - - # The path to the dir with the fontfiles - def __init__(self, output='Agg'): - self.facenames = self.filenamesd.keys() - # Set the filenames to full path - for facename in self.filenamesd: - self.filenamesd[facename] = self.filenamesd[facename] - if output: - self.output = output - # self.glyphdict[key] = facename, metrics, glyph, offset - self.glyphdict = {} - - self.fonts = dict( - [ (facename, font_open(self.filenamesd[facename])) for - facename in self.facenames]) - # a dict of charcode -> glyphindex pairs - self.charmaps = dict( - [ (facename, self.fonts[facename].get_charmap()) - for facename in self.facenames]) - # a dict of glyphindex -> charcode pairs - self.glyphmaps = {} - for facename in self.facenames: - charmap = self.charmaps[facename] - self.glyphmaps[facename] = dict([(glyphind, charcode) - for charcode, glyphind in charmap.items()]) - for fontface in self.fonts.values(): - fontface.clear() - if self.output == 'SVG': - # a list of "glyphs" we need to render this thing in SVG - self.svg_glyphs=[] - - def set_canvas_size(self, w, h, pswriter=None): - 'Dimension the drawing canvas; may be a noop' - # self.width = int(w) - # self.height = int(h) - # I don't know why this was different than the PS version - self.width = w - self.height = h - if pswriter: - self.pswriter = pswriter - else: - for fontface in self.fonts.values(): - fontface.set_bitmap_size(int(w), int(h)) - - def render(self, ox, oy, facename, symbol, fontsize, dpi): - filename = self.filenamesd[facename] - uniindex, metrics, glyph, offset = self._get_info(facename, - symbol, fontsize, dpi) - if self.output == 'SVG': - oy += offset - 512/2048.*10. - # TO-DO - make a method for it - # This gets the name of the font. - familyname = self.fonts[facename].get_sfnt()[(1,0,0,1)] - thetext = unichr(uniindex) - thetext.encode('utf-8') - self.svg_glyphs.append((familyname, fontsize, thetext, ox, oy, - metrics)) - elif self.output == 'PS': - # This should be changed to check for math mode or smth. - #if filename == 'cmex10.ttf': - # oy += offset - 512/2048.*10. - - # Get the PS name of a glyph (his unicode integer code) - # from the font object - symbolname = self._get_glyph_name(uniindex, facename) - psfontname = self.fonts[facename].postscript_name - ps = """/%(psfontname)s findfont + def render_glyph(self, ox, oy, info): + oy = self.height - oy + info.offset + postscript_name = info.postscript_name + fontsize = info.fontsize + symbol_name = info.symbol_name + + ps = """/%(postscript_name)s findfont %(fontsize)s scalefont setfont %(ox)f %(oy)f moveto -/%(symbolname)s glyphshow +/%(symbol_name)s glyphshow """ % locals() - self.pswriter.write(ps) - else: # Agg - fontface = self.fonts[facename] - fontface.draw_glyph_to_bitmap( - int(ox), int(self.height - oy - metrics.ymax), glyph) + self.pswriter.write(ps) - def get_metrics(self, facename, symbol, fontsize, dpi): - uniindex, metrics, glyph, offset = \ - self._get_info(facename, symbol, fontsize, dpi) - return metrics + def render_rect_filled(self, x1, y1, x2, y2): + ps = "%f %f %f %f rectfill" % (x1, self.height - y2, x2 - x1, y2 - y1) + self.pswriter.write(ps) - # Methods that must be overridden for fonts that are not unicode aware + def get_results(self): + return (self.width, + self.height, + self.pswriter, + self.fonts_object.get_used_characters()) + +class MathtextBackendPdf(MathtextBackend): + def __init__(self): + self.pswriter = [] + + def render_glyph(self, ox, oy, info): + filename = info.font.fname + oy = self.height - oy + info.offset - def _get_unicode_index(self, symbol): - return get_unicode_index(symbol) + self.pswriter.append(('glyph', ox, oy, filename, info.fontsize, info.num)) - def _get_glyph_name(self, uniindex, facename): - """get_glyph_name(self, uniindex, facename) -> string + def render_rect_filled(self, x1, y1, x2, y2): + self.pswriter.append(('rect', x1, self.height - y2, x2 - x1, y2 - y1)) -Returns the name of the glyph directly from the font object. + def get_results(self): + return (self.width, + self.height, + self.pswriter, + self.fonts_object.get_used_characters()) -""" - font = self.fonts[facename] - glyphindex = self.glyphmaps[facename][uniindex] - return font.get_glyph_name(glyphindex) - - def _get_info(self, facename, symbol, fontsize, dpi): - 'load the facename, metrics and glyph' - #print hex(index), symbol, filename, facename - key = facename, symbol, fontsize, dpi - tup = self.glyphdict.get(key) - if tup is not None: - return tup - filename = self.filenamesd[facename] - fontface = self.fonts[facename] - fontface.set_size(fontsize, dpi) - head = fontface.get_sfnt_table('head') - uniindex = self._get_unicode_index(symbol) - glyphindex = self.glyphmaps[facename][uniindex] - glyph = fontface.load_char(uniindex) - xmin, ymin, xmax, ymax = [val/64.0 for val in glyph.bbox] - # This is black magic to me (Edin) - if filename == 'cmex10.ttf': - if self.output == 'PS': - offset = -(head['yMin']+512)/head['unitsPerEm']*10. - else: - offset = glyph.height/64.0/2 + 256.0/64.0*dpi/72.0 - else: - offset = 0. - metrics = Bunch( - advance = glyph.linearHoriAdvance/65536.0, - height = glyph.height/64.0, - width = glyph.width/64.0, - xmin = xmin, - xmax = xmax, - ymin = ymin+offset, - ymax = ymax+offset, - ) - self.glyphdict[key] = uniindex, metrics, glyph, offset - return self.glyphdict[key] - - -class MyUnicodeFonts(UnicodeFonts): - _initialized = False +class MathtextBackendSvg(MathtextBackend): def __init__(self): - if not MyUnicodeFonts._initialized: - prop = FontProperties() - prop.set_family('serif') - self.rmfile = fontManager.findfont(prop) + self.svg_glyphs = [] + self.svg_rects = [] + + def render_glyph(self, ox, oy, info): + oy = self.height - oy + info.offset + thetext = unichr(info.num) + self.svg_glyphs.append( + (info.font, info.fontsize, thetext, ox, oy, info.metrics)) - prop.set_family('fantasy') - self.calfile = fontManager.findfont(prop) + def render_rect_filled(self, x1, y1, x2, y2): + self.svg_rects.append( + (x1, self.height - y1, x2 - x1, y2 - y1)) - prop.set_family('monospace') - self.ttfile = fontManager.findfont(prop) + def get_results(self): + svg_elements = Bunch(svg_glyphs = self.svg_glyphs, + svg_rects = self.svg_rects) + return (self.width, + self.height, + svg_elements, + self.fonts_object.get_used_characters()) + +class Fonts(object): + """ + An abstract base class for fonts that want to render mathtext - prop.set_family('serif') - prop.set_style('italic') - self.itfile = fontManager.findfont(prop) - self.filenamesd = { 'rm' : self.rmfile, - 'it' : self.itfile, - 'cal' : self.calfile, - 'tt' : self.ttfile, - } - MyUnicodeFonts._initialized = True + 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 + to do the actual drawing. + """ + 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.""" + self.default_font_prop = default_font_prop + self.mathtext_backend = mathtext_backend + # Make these classes doubly-linked + self.mathtext_backend.fonts_object = self + self.used_characters = {} + + def get_kern(self, font1, sym1, fontsize1, + font2, sym2, fontsize2, dpi): + """ + Get the kerning distance for font between sym1 and sym2. -# TO-DO: pretty much everything -class BakomaUnicodeFonts(UnicodeFonts): - """A class that simulates Unicode support in the BaKoMa fonts""" + 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 + + sym is a single symbol(alphanum, punct) or a special symbol + like \sigma. - filenamesd = { 'cal' : 'cmsy10.ttf', - 'rm' : 'cmr10.ttf', - 'tt' : 'cmtt10.ttf', - 'it' : 'cmmi10.ttf', - 'bf' : 'cmb10.ttf', - 'sf' : 'cmss10.ttf', - None : 'cmmi10.ttf', - } + """ + return 0. - # We override the UnicodeFonts methods, that depend on Unicode support - def _get_unicode_index(self, symbol): - uniindex = get_unicode_index(symbol) + def get_metrics(self, font, 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 - # Should be deleted - def _get_glyph_name(self, uniindex, facename): - """get_glyph_name(self, uniindex, facename) -> string + 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 + """ + info = self._get_info(font, sym, fontsize, dpi) + return info.metrics -Returns the name of the glyph directly from the font object. -Because BaKoma fonts don't support Unicode, 'uniindex' is misleading + def set_canvas_size(self, w, h): + 'Dimension the drawing canvas; may be a noop' + self.width, self.height = ceil(w), ceil(h) + self.mathtext_backend.set_canvas_size(self.width, self.height) -""" - font = self.fonts[facename] - glyphindex = self.glyphmaps[facename][uniindex] - return font.get_glyph_name(glyphindex) + def render_glyph(self, ox, oy, facename, sym, fontsize, dpi): + info = self._get_info(facename, sym, fontsize, dpi) + realpath, stat_key = get_realpath_and_stat(info.font.fname) + used_characters = self.used_characters.setdefault( + stat_key, (realpath, Set())) + used_characters[1].update(unichr(info.num)) + self.mathtext_backend.render_glyph(ox, oy, info) - def _get_info(self, facename, symbol, fontsize, dpi): - 'load the facename, metrics and glyph' - #print hex(index), symbol, filename, facename - key = facename, symbol, fontsize, dpi - tup = self.glyphdict.get(key) - if tup is not None: - return tup - filename = self.filenamesd[facename] - fontface = self.fonts[facename] - fontface.set_size(fontsize, dpi) - head = fontface.get_sfnt_table('head') - uniindex = self._get_unicode_index(symbol) - glyphindex = self.glyphmaps[facename][uniindex] - glyph = fontface.load_char(uniindex) - xmin, ymin, xmax, ymax = [val/64.0 for val in glyph.bbox] - # This is black magic to me (Edin) - if filename == 'cmex10.ttf': - if self.output == 'PS': - offset = -(head['yMin']+512)/head['unitsPerEm']*10. - else: - offset = glyph.height/64.0/2 + 256.0/64.0*dpi/72.0 - else: - offset = 0. - metrics = Bunch( - advance = glyph.linearHoriAdvance/65536.0, - height = glyph.height/64.0, - width = glyph.width/64.0, - xmin = xmin, - xmax = xmax, - ymin = ymin+offset, - ymax = ymax+offset, - ) - self.glyphdict[key] = uniindex, metrics, glyph, offset - return self.glyphdict[key] + def render_rect_filled(self, x1, y1, x2, y2): + self.mathtext_backend.render_rect_filled(x1, y1, x2, y2) + def get_xheight(self, font, fontsize, dpi): + raise NotImplementedError() -# TO-DO: Implement all methods -class CMUUnicodeFonts(UnicodeFonts): - """A class representing Computer Modern Unicode Fonts, made by -Andrey V. Panov -panov /at/ canopus. iacp. dvo. ru -They are distributed under the X11 License. + def get_underline_thickness(self, font, fontsize, dpi): + raise NotImplementedError() + + def get_used_characters(self): + return self.used_characters -""" + def get_results(self): + return self.mathtext_backend.get_results() - -# Old classes - -class BakomaFonts(Fonts): + def get_sized_alternatives_for_symbol(self, fontname, sym): + """Override if your font provides multiple sizes of the same + symbol.""" + return [(fontname, sym)] + +class TruetypeFonts(Fonts): """ + A generic base class for all font setups that use Truetype fonts + (through ft2font) + """ + """ Use the Bakoma true type fonts for rendering """ # allocate a new set of fonts basepath = os.path.join( get_data_path(), 'fonts', 'ttf' ) - fontmap = { 'cal' : 'Cmsy10', - 'rm' : 'Cmr10', - 'tt' : 'Cmtt10', - 'it' : 'Cmmi10', - 'bf' : 'Cmb10', - 'sf' : 'Cmss10', - None : 'Cmmi10', - 'ex' : 'Cmex10' - } - class CachedFont: def __init__(self, font): self.font = font self.charmap = font.get_charmap() self.glyphmap = dict( [(glyphind, ccode) for ccode, glyphind in self.charmap.items()]) - - def __init__(self): - self.glyphd = {} - self.fonts = {} - self.used_characters = {} + def __repr__(self): + return repr(self.font) + + def __init__(self, default_font_prop, mathtext_backend): + Fonts.__init__(self, default_font_prop, mathtext_backend) + self.glyphd = {} + self.fonts = {} + + filename = fontManager.findfont(default_font_prop) + default_font = self.CachedFont(FT2Font(str(filename))) + + self.fonts['default'] = default_font + 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.), a Computer Modern - font name (cmtt10, cmr10, etc.) or an FT2Font object.""" - if isinstance(font, str): - if font not in self.fontmap.values(): - basename = self.fontmap[font] - else: - basename = font + font may be a TeX font name (cal, rm, it etc.), or postscript name.""" + if font in self.fontmap: + basename = self.fontmap[font] else: - basename = font.postscript_name + basename = font cached_font = self.fonts.get(basename) if cached_font is None: - if isinstance(font, str): - font = FT2Font(os.path.join(self.basepath, basename.lower() + ".ttf")) - basename = font.postscript_name + font = FT2Font(os.path.join(self.basepath, basename + ".ttf")) cached_font = self.CachedFont(font) self.fonts[basename] = cached_font - return basename, cached_font + self.fonts[font.postscript_name] = cached_font + self.fonts[font.postscript_name.lower()] = cached_font + return cached_font - def get_font(self, font): - return self._get_font(font)[1].font - def get_fonts(self): - return [x.font for x in self.fonts.values()] - - def get_metrics(self, font, sym, fontsize, dpi): - basename, font, metrics, symbol_name, num, glyph, offset = \ - self._get_info(font, sym, fontsize, dpi) - return metrics + return list(set([x.font for x in self.fonts.values()])) - def _get_offset(self, basename, cached_font, glyph, fontsize, dpi): - if basename.lower() == 'cmex10': - return glyph.height/64.0/2 + 256.0/64.0*dpi/72.0 + def _get_offset(self, cached_font, glyph, fontsize, dpi): return 0. - - def _get_info (self, font, sym, fontsize, dpi): + + def _get_info (self, fontname, sym, fontsize, dpi, mark_as_used=True): 'load the cmfont, metrics and glyph with caching' - if hasattr(font, 'postscript_name'): - fontname = font.postscript_name - else: - fontname = font - key = fontname, sym, fontsize, dpi - tup = self.glyphd.get(key) + bunch = self.glyphd.get(key) + if bunch is not None: + return bunch - if tup is not None: return tup - - if font in self.fontmap and latex_to_bakoma.has_key(sym): - basename, num = latex_to_bakoma[sym] - basename, cached_font = self._get_font(basename.capitalize()) - symbol_name = cached_font.font.get_glyph_name(num) - num = cached_font.glyphmap[num] - elif len(sym) == 1: - basename, cached_font = self._get_font(font) - num = ord(sym) - symbol_name = cached_font.font.get_glyph_name(cached_font.charmap[num]) - else: - num = 0 - raise ValueError('unrecognized symbol "%s"' % sym) + cached_font, num, symbol_name, fontsize = \ + self._get_glyph(fontname, sym, fontsize) font = cached_font.font font.set_size(fontsize, dpi) glyph = font.load_char(num) - realpath, stat_key = get_realpath_and_stat(font.fname) - used_characters = self.used_characters.setdefault( - stat_key, (realpath, Set())) - used_characters[1].update(unichr(num)) - xmin, ymin, xmax, ymax = [val/64.0 for val in glyph.bbox] - offset = self._get_offset(basename, cached_font, glyph, fontsize, dpi) + offset = self._get_offset(cached_font, glyph, fontsize, dpi) metrics = Bunch( - advance = glyph.linearHoriAdvance/65536.0, - height = glyph.height/64.0, - width = glyph.width/64.0, - xmin = xmin, - xmax = xmax, - ymin = ymin+offset, - ymax = ymax+offset, + advance = glyph.linearHoriAdvance/65536.0, + height = glyph.height/64.0 + offset, + width = glyph.width/64.0, + xmin = xmin, + xmax = xmax, + ymin = ymin+offset, + ymax = ymax+offset, # iceberg is the equivalent of TeX's "height" iceberg = glyph.horiBearingY/64.0 + offset ) - self.glyphd[key] = basename, font, metrics, symbol_name, num, glyph, offset + self.glyphd[key] = Bunch( + font = font, + fontsize = fontsize, + postscript_name = font.postscript_name, + metrics = metrics, + symbol_name = symbol_name, + num = num, + glyph = glyph, + offset = offset + ) return self.glyphd[key] - def set_canvas_size(self, w, h): - 'Dimension the drawing canvas; may be a noop' - self.width = int(w) - self.height = int(h) - for cached_font in self.fonts.values(): - cached_font.font.set_bitmap_size(int(w), int(h)) - - def render(self, ox, oy, font, sym, fontsize, dpi): - basename, font, metrics, symbol_name, num, glyph, offset = \ - self._get_info(font, sym, fontsize, dpi) - - font.draw_glyph_to_bitmap( - int(ox), int(oy - metrics.ymax), glyph) - - def render_rect_filled(self, x1, y1, x2, y2): - assert len(self.fonts) - font = self.fonts.values()[0] - font.font.draw_rect_filled( - max(0, x1 - 1), - y1, - max(x2 - 1, x1), - max(y2 - 1, y1)) - - def get_used_characters(self): - return self.used_characters - def get_xheight(self, font, fontsize, dpi): - basename, cached_font = self._get_font(font) + cached_font = self._get_font(font) cached_font.font.set_size(fontsize, dpi) pclt = cached_font.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(font, 'x', fontsize, dpi) + return metrics.iceberg xHeight = pclt['xHeight'] / 64.0 return xHeight def get_underline_thickness(self, font, fontsize, dpi): - basename, cached_font = self._get_font(font) + cached_font = self._get_font(font) cached_font.font.set_size(fontsize, dpi) return max(1.0, cached_font.font.underline_thickness / 64.0) - def get_kern(self, fontleft, symleft, fontsizeleft, - fontright, symright, fontsizeright, dpi): - if fontsizeleft == fontsizeright: - basename, font1, metrics, symbol_name, num1, glyph1, offset = \ - self._get_info(fontleft, symleft, fontsizeleft, dpi) - basename, font2, metrics, symbol_name, num2, glyph2, offset = \ - self._get_info(fontright, symright, fontsizeright, dpi) - if font1 == font2: - basename, font = self._get_font(font1) - return font.font.get_kerning(num1, num2, KERNING_UNFITTED) / 64.0 + def get_kern(self, font1, sym1, fontsize1, + font2, sym2, fontsize2, dpi): + if font1 == font2 and fontsize1 == fontsize2: + info1 = self._get_info(font1, sym1, fontsize1, dpi) + info2 = self._get_info(font2, sym2, fontsize2, dpi) + font = info1.font + return font.get_kerning(info1.num, info2.num, KERNING_UNFITTED) / 64.0 return 0.0 -class BakomaPSFonts(BakomaFonts): +class BakomaFonts(TruetypeFonts): """ - Use the Bakoma postscript fonts for rendering to backend_ps + Use the Bakoma true type fonts for rendering """ - - def set_canvas_size(self, w, h, pswriter): - 'Dimension the drawing canvas; may be a noop' - self.width = w - self.height = h - self.pswriter = pswriter - - def render(self, ox, oy, font, sym, fontsize, dpi): - basename, font, metrics, symbol_name, num, glyph, offset = \ - self._get_info(font, sym, fontsize, dpi) - oy = self.height - oy + offset - - ps = """/%(basename)s findfont -%(fontsize)s scalefont -setfont -%(ox)f %(oy)f moveto -/%(symbol_name)s glyphshow -""" % locals() - self.pswriter.write(ps) - - def render_rect_filled(self, x1, y1, x2, y2): - ps = "%f %f %f %f rectfill" % (x1, self.height - y2, x2 - x1, y2 - y1) - self.pswriter.write(ps) + fontmap = { 'cal' : 'cmsy10', + 'rm' : 'cmr10', + 'tt' : 'cmtt10', + 'it' : 'cmmi10', + 'bf' : 'cmb10', + 'sf' : 'cmss10', + 'ex' : 'cmex10' + } -class BakomaPDFFonts(BakomaPSFonts): - """Hack of BakomaPSFonts for PDF support.""" + def _get_offset(self, cached_font, glyph, fontsize, dpi): + if cached_font.font.postscript_name == 'cmex10': + return glyph.height/64.0/2 + 256.0/64.0 * dpi/72.0 + return 0. - def render(self, ox, oy, font, sym, fontsize, dpi): - basename, font, metrics, symbol_name, num, glyph, offset = \ - self._get_info(font, sym, fontsize, dpi) - filename = font.fname - oy = self.height - oy + offset + def _get_glyph(self, fontname, sym, fontsize): + if fontname in self.fontmap and latex_to_bakoma.has_key(sym): + basename, num = latex_to_bakoma[sym] + cached_font = self._get_font(basename) + symbol_name = cached_font.font.get_glyph_name(num) + num = cached_font.glyphmap[num] + elif len(sym) == 1: + cached_font = self._get_font(fontname) + num = ord(sym) + symbol_name = cached_font.font.get_glyph_name( + cached_font.charmap[num]) + else: + raise ValueError('unrecognized symbol "%s"' % sym) - self.pswriter.append(('glyph', ox, oy, filename, fontsize, num)) + return cached_font, num, symbol_name, fontsize - def render_rect_filled(self, x1, y1, x2, y2): - self.pswriter.append(('rect', x1, self.height - y2, x2 - x1, y2 - y1)) + # The Bakoma fonts contain many pre-sized alternatives for the + # delimiters. The AutoSizedChar class will use these alternatives + # and select the best (closest sized) glyph. + _size_alternatives = { + '(' : [('rm', '('), ('ex', '\xa1'), ('ex', '\xb3'), + ('ex', '\xb5'), ('ex', '\xc3')], + ')' : [('rm', ')'), ('ex', '\xa2'), ('ex', '\xb4'), + ('ex', '\xb6'), ('ex', '\x21')], + '{' : [('cal', '{'), ('ex', '\xa9'), ('ex', '\x6e'), + ('ex', '\xbd'), ('ex', '\x28')], + '}' : [('cal', '}'), ('ex', '\xaa'), ('ex', '\x6f'), + ('ex', '\xbe'), ('ex', '\x29')], + # The fourth size of '[' is mysteriously missing from the BaKoMa font, + # so I've ommitted it for both + '[' : [('rm', '['), ('ex', '\xa3'), ('ex', '\x68'), + ('ex', '\x22')], + ']' : [('rm', ']'), ('ex', '\xa4'), ('ex', '\x69'), + ('ex', '\x23')], + r'\lfloor' : [('cal', '\x62'), ('ex', '\xa5'), ('ex', '\x6a'), + ('ex', '\xb9'), ('ex', '\x24')], + r'\rfloor' : [('cal', '\x63'), ('ex', '\xa6'), ('ex', '\x6b'), + ('ex', '\xba'), ('ex', '\x25')], + r'\lceil' : [('cal', '\x64'), ('ex', '\xa7'), ('ex', '\x6c'), + ('ex', '\xbb'), ('ex', '\x26')], + r'\rceil' : [('cal', '\x65'), ('ex', '\xa8'), ('ex', '\x6d'), + ('ex', '\xbc'), ('ex', '\x27')], + r'\langle' : [('cal', '\x68'), ('ex', '\xad'), ('ex', '\x44'), + ('ex', '\xbf'), ('ex', '\x2a')], + r'\rangle' : [('cal', '\x69'), ('ex', '\xae'), ('ex', '\x45'), + ... [truncated message content] |