[reportlab-users] Font size, baseline and typography stuff
Robin Becker
robin at reportlab.com
Thu Aug 4 10:41:51 EDT 2011
On 04/08/2011 14:25, Dinu Gherman wrote:
> Hi, long time no see!
>
> I started a little project with strong requirements on
> typography, which made me write the little attached
> test script to see how much the reportlab library can
> actually be helpful here. Doing so I noticed a couple
> of things which make me state/raise the following re-
> marks/questions:
>
> 1. The ascent/descent values seem to be numbers in the
> range [0 ... 1000] representing a fraction of the font
> size. That's fine, but I couldn't find a note in the
> code saying so explicitly, which would just reduce a
> bit of guess work.
Not all fonts use the same scale unfortunately; I think that most truetype fonts
come with a unitsperEm value of 2048. In ttfonts.py I think we adjust the
bounding boxes to be in 1/1000. I guess that's so the bounding boxes can be used
without knowing the original scale.
>
> 2. I wonder if there is a way for any given font to
> find the x-height defining the "mean line" (or
> "midline") as described and illustrated here:
>
> http://en.wikipedia.org/wiki/Typeface#Font_metrics
>
It might be possible with access to the curves. We do have that with tools in
the graphics extension _renderPM (at least for those fonts we can cope with).
eg
> C:\tmp>cat xheight.py
> from reportlab.graphics.charts.textlabels import _text2Path
> from reportlab.pdfbase import pdfmetrics, ttfonts
>
> pdfmetrics.registerFont(ttfonts.TTFont('Vera','Vera.ttf'))
>
> px = _text2Path('x',fontName='Vera',fontSize=2048)
> pX = _text2Path('X',fontName='Vera',fontSize=2048)
>
> print 'x',px.getBounds()
> print 'X',pX.getBounds()
>
> C:\tmp>xheight.py
> x (59, 0, 1145, 1120)
> X (61, 0, 1339, 1493)
so we seem able to get the path for x and X and there is a difference in the
curve heights (and luckily both seem to stay on and above the baseline ie 0)
Of course I'm using a fairly large value for the fontSize so the x height seems
to be fontSize * (1120-0)/2048. and for X fontSize * (1493-0)/2048.
> In the PDF generated by my script I approximated this
> as shown by the dashed lines using an x-height equal
> to the x-width which is not really acceptable, though.
>
Anyhow if I use that in your code it seems better; see attached.
> 3. Looking at the PDF output of the attached script,
> you'll see that for Times-Italic the rightmost letter
> N is slightly outside the grey box with a horizontal
> size calculated by the function stringWidth with the
> correct font name.
>
> I'm not a typography expert, but I wonder if this be-
> haviour is correct? If so, I wonder how the width of
> slanted fonts is actually defined, then? Unfortunately,
> the all knowing Wikipedia doesn't give an immediate
> answer, at least not here:
not sure on this one.
>
> http://en.wikipedia.org/wiki/Italic_type
> http://en.wikipedia.org/wiki/Oblique_type
>
> If you run the script in a directory that does also
> contain the Rina.ttf file from the reportlab distri-
> bution it will also be shown in the PDF.
>
> Regards,
>
> Dinu
........
--
Robin Becker
-------------- next part --------------
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"Test typography metrics with ReportLab."
import os
import sys
import datetime
import platform
import reportlab
from reportlab.lib.units import cm
from reportlab.lib.pagesizes import landscape, A4
from reportlab.lib import colors
from reportlab.pdfgen.canvas import Canvas
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.pdfbase.pdfmetrics import (stringWidth, getAscent, getDescent,
registerFont, registerFontFamily)
from reportlab.graphics.charts.textlabels import _text2Path
from reportlab.pdfbase import pdfmetrics, ttfonts
def create_page(out_path, text, width, fns, col, verbose=False):
"Create a page with text and metrics info using multiple fonts."
c = Canvas(out_path, pagesize=landscape(A4))
c.setLineWidth(1)
c.setFillColor(col)
fs = 72
width = 800
x = 275
fnl, fsl = "Helvetica", fs/7.2
for i, fn in enumerate(fns):
y = A4[0]-110 - i * fs * 2
asc, dsc = getAscent(fn), getDescent(fn)
# draw background box plus text
c.setFont(fn, fs)
d = fs*asc/1000.
w = stringWidth(text, fn, fs)
c.setFillColor(colors.lightgrey)
c.rect(x, y, w, d, stroke=0, fill=1)
c.setFillColor(colors.black)
c.drawString(x, y, text)
# base line
c.setStrokeColor(colors.red)
c.line(x, y, width, y)
c.setFont(fnl, fsl)
c.drawString(x-125, y, "baseline (0)")
# base line + font size
c.setStrokeColor(colors.green)
d = fs
c.line(x, y+d, width, y+d)
c.drawString(x-125, y+d, "baseline + fontsize")
# fontname
c.drawString(x-225, y+d, fn)
# base line - descent
c.setStrokeColor(colors.blue)
d = fs*dsc/1000.
c.line(x, y+d, width, y+d)
c.drawString(x-125, y+d, "baseline + descent")
# base line + ascent
c.setStrokeColor(colors.grey)
d = fs*asc/1000.
c.line(x, y+d, width, y+d)
c.drawString(x-125, y+d, "baseline + ascent")
# mean line (approximated)
c.setStrokeColor(colors.orange)
c.setDash([5, 5])
#d = stringWidth("x", fn, fs)
bx0,by0,bx1,by1 = _text2Path('x',fontName=fn,fontSize=2048).getBounds()
d = fs * (max(0,by1)-max(0,by0))/2048.
c.line(x, y+d, width, y+d)
c.drawString(x-125, y+d, "mean line?")
d = datetime.datetime.now().isoformat()
d = d[:d.rfind(":")]
args = (reportlab.Version, platform.python_version(), d)
s = "Reportlab v. %s, Python v. %s, Date/Time: %s" % args
c.drawString(width - stringWidth(s, fnl, fsl), A4[0]-2*fsl, s)
c.showPage()
c.save()
def test():
standardFonts = (
'Courier', 'Courier-Bold', 'Courier-Oblique', 'Courier-BoldOblique',
'Helvetica', 'Helvetica-Bold', 'Helvetica-Oblique',
'Helvetica-BoldOblique',
'Times-Roman', 'Times-Bold', 'Times-Italic', 'Times-BoldItalic',
'Symbol','ZapfDingbats')
# add TrueType font if available
ttfns = []
ttt = [
('Rina', 'Rina.ttf'),
('Vera', 'Vera.ttf'),
('VeraBd', 'VeraBd.ttf'),
('VeraIt', 'VeraIt.ttf'),
('VeraBI', 'VeraBI.ttf'),
]
reportlab.rl_config.warnOnMissingFontGlyphs = 0
for ttfn, path in ttt:
try:
registerFont(TTFont(ttfn, path))
ttfns.append(ttfn)
except reportlab.pdfbase.ttfonts.TTFError:
print "Could not find font file '%s'. Ignoring..." % path
out_path = os.path.splitext(sys.argv[0])[0] + ".pdf"
col = colors.black
width = 300
fns = ["Times-Roman", "Times-Italic", "Helvetica"] + ttfns
text = "xTÿpÔgrÁphyN"
create_page(out_path, text, width, fns, col)
if __name__ == "__main__":
test()
-------------- next part --------------
A non-text attachment was scrubbed...
Name: test_typometrics_2.pdf
Type: application/pdf
Size: 89141 bytes
Desc: not available
Url : <http://two.pairlist.net/pipermail/reportlab-users/attachments/20110804/9d04abbf/attachment-0001.pdf>
More information about the reportlab-users
mailing list