Hello,

AFAIK, current backends (I only tested agg, pdf, and ps) do not
properly respect the text baseline when text is rendered using TeX.
The get_text_width_height_descent() method in Agg and PS backends
simply return 0 for the descent value. While PDF backend uses the
dviread module to figure out correct descent values, there are cases
this does not work well (e.g. $\frac{1}{2}\pi$).

As an example, the attached figure shows the result for the Agg
backend. In all cases, the texts are placed at (0,0) with
baseline-alignment. Leftmost one is when usetex=False, which has a
correct baseline. The middle one is when usetex=True. It is bottom
aligned, which is not intended. The rightmost one is also when
usetex=True but after the patch I describe below.

First of all, I borrowed this idea from the PyX which is in GPL.
Although there is little of copying, other than the basic idea, I'm
not 100% sure if this could be BSD-compatible.

Anyhow, the idea is that you can have LateX to print out the width,
height, and descent (=depth) of a given text by enclosing the text in
a box. For example,

 \newbox\MatplotlibBox%
 \setbox\MatplotlibBox=\hbox{$\frac{1}{2}\pi$}%
 \copy\MatplotlibBox
 
\immediate\write16{MatplotlibBox:\the\wd\MatplotlibBox,\the\ht\MatplotlibBox,\the\dp\MatplotlibBox}%

I define a newbox (called MatplotlibBox) which encloses
$\frac{1}{2}\pi$. And then print out the width, height and depth of
the box.

Attached is a patch of a texmanager.py which utilize above method to
figure out the dimension of the text. The template string to generate
a  ".tex" file is slightly modified. After latex is run, the
dimensional information  of the text is extracted and saved in
".baseline" file and get_text_width_height_descent() method is added
under the TexManager class, which reads in the ".baseline" file and
return its content. (you need to empty out the tex.cache directory for
this work correctly).

A backend can simply call the get_text_width_height_descent() of
texmanager (a simple patch for the Agg backend is attached). I also
tested this with PS and PDF backends and they worked out fine.

So if the license issue is okay, I wonder if this patch can be
reviewed and applied (after any necessary modifications) to improve
the baseline handling in matploltib.

Regards,

-JJ

<<attachment: test_baseline_comparison.png>>

Index: lib/matplotlib/texmanager.py
===================================================================
--- lib/matplotlib/texmanager.py	(revision 6055)
+++ lib/matplotlib/texmanager.py	(working copy)
@@ -236,6 +236,12 @@
         else:
             unicode_preamble = ''
 
+
+
+        # newbox, setbox, immediate, etc. are used to find the box
+        # extent of the rendered text.
+
+
         s = r"""\documentclass{article}
 %s
 %s
@@ -243,7 +249,10 @@
 \usepackage[papersize={72in,72in}, body={70in,70in}, margin={1in,1in}]{geometry}
 \pagestyle{empty}
 \begin{document}
-\fontsize{%f}{%f}%s
+\newbox\MatplotlibBox%%
+\setbox\MatplotlibBox=\hbox{{\fontsize{%f}{%f}%s}}%%
+\copy\MatplotlibBox
+\immediate\write16{MatplotlibBox:\the\wd\MatplotlibBox,\the\ht\MatplotlibBox,\the\dp\MatplotlibBox}%%
 \end{document}
 """ % (self._font_preamble, unicode_preamble, custom_preamble,
        fontsize, fontsize*1.25, tex)
@@ -283,6 +292,14 @@
                 fh = file(outfile)
                 report = fh.read()
                 fh.close()
+
+                # find the box extent information in the latex output
+                # file and save them in basefile+".baseline" file
+                si = report.find("MatplotlibBox:")
+                ssi = si + len("MatplotlibBox:")
+                ei = report.find("\n", ssi)
+                open(basefile+'.baseline',"w").write(report[ssi:ei])
+                
             except IOError:
                 report = 'No latex error report available.'
             if exit_status:
@@ -292,6 +309,7 @@
             for fname in glob.glob(basefile+'*'):
                 if fname.endswith('dvi'): pass
                 elif fname.endswith('tex'): pass
+                elif fname.endswith('baseline'): pass
                 else:
                     try: os.remove(fname)
                     except OSError: pass
@@ -441,3 +459,22 @@
             self.rgba_arrayd[key] = Z
 
         return Z
+
+
+    def get_text_width_height_descent(self, tex, fontsize):
+        """
+        return text size in point
+        """
+        basefile = self.get_basefile(tex, fontsize)
+        baselinefile = '%s.baseline'% basefile
+
+
+        if DEBUG or not os.path.exists(baselinefile):
+            dvifile = self.make_dvi(tex, fontsize)
+
+        # width, height, depth of the bpx
+        # depth = descent, and height does not include depth
+        l = open(baselinefile).read().split(",")
+        width, height, depth = [float(l1[:-2]) for l1 in l] # get rid of "pt"
+
+        return width, height+depth, depth
Index: lib/matplotlib/backends/backend_agg.py
===================================================================
--- lib/matplotlib/backends/backend_agg.py	(revision 6055)
+++ lib/matplotlib/backends/backend_agg.py	(working copy)
@@ -121,15 +121,16 @@
         # texmanager more efficient.  It is not meant to be used
         # outside the backend
         """
+
+
         if ismath=='TeX':
-            # todo: handle props
-            size = prop.get_size_in_points()
             texmanager = self.get_texmanager()
-            Z = texmanager.get_grey(s, size, self.dpi)
-            m,n = Z.shape
-            # TODO: descent of TeX text (I am imitating backend_ps here -JKS)
-            return n, m, 0
+            fontsize = prop.get_size_in_points() /72.*self.dpi
 
+            w, h, d = texmanager.get_text_width_height_descent(s, fontsize)
+            return w, h, d
+
+
         if ismath:
             ox, oy, width, height, descent, fonts, used_characters = \
                 self.mathtext_parser.parse(s, self.dpi, prop)
-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
Matplotlib-devel mailing list
Matplotlib-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-devel

Reply via email to