test_pdfbase_ttfonts.py :  » PDF » ReportLab » ReportLab_2_4 » tests » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » PDF » ReportLab 
ReportLab » ReportLab_2_4 » tests » test_pdfbase_ttfonts.py

"""Test TrueType font subsetting & embedding code.

This test uses a sample font (Vera.ttf) taken from Bitstream which is called Vera
Serif Regular and is covered under the license in ../fonts/bitstream-vera-license.txt.
"""
from reportlab.lib.testutils import setOutDir,makeSuiteForClasses,outputfile,printLocation,NearTestCase
setOutDir(__name__)
import string
from cStringIO import StringIO
import unittest
from reportlab.pdfgen.canvas import Canvas
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.pdfdoc import PDFDocument,PDFError
from reportlab.pdfbase.ttfonts import TTFont,TTFontFace,TTFontFile,TTFOpenFile,\
                                      TTFontParser, TTFontMaker, TTFError, \
                                      parse_utf8, makeToUnicodeCMap, \
                                      FF_SYMBOLIC, FF_NONSYMBOLIC, \
                                      calcChecksum, add32

def utf8(code):
    "Convert a given UCS character index into UTF-8"
    if code < 0 or code > 0x7FFFFFFF:
        raise ValueError, 'Invalid UCS character 0x%x' % code
    elif code < 0x00000080:
        return chr(code)
    elif code < 0x00000800:
        return '%c%c' % \
                 (0xC0 + (code >> 6),
                  0x80 + (code & 0x3F))
    elif code < 0x00010000:
        return '%c%c%c' % \
                 (0xE0 + (code >> 12),
                  0x80 + ((code >> 6) & 0x3F),
                  0x80 + (code & 0x3F))
    elif code < 0x00200000:
        return '%c%c%c%c' % \
                 (0xF0 + (code >> 18),
                  0x80 + ((code >> 12) & 0x3F),
                  0x80 + ((code >> 6) & 0x3F),
                  0x80 + (code & 0x3F))
    elif code < 0x04000000:
        return '%c%c%c%c%c' % \
                 (0xF8 + (code >> 24),
                  0x80 + ((code >> 18) & 0x3F),
                  0x80 + ((code >> 12) & 0x3F),
                  0x80 + ((code >> 6) & 0x3F),
                  0x80 + (code & 0x3F))
    else:
        return '%c%c%c%c%c%c' % \
                 (0xFC + (code >> 30),
                  0x80 + ((code >> 24) & 0x3F),
                  0x80 + ((code >> 18) & 0x3F),
                  0x80 + ((code >> 12) & 0x3F),
                  0x80 + ((code >> 6) & 0x3F),
                  0x80 + (code & 0x3F))

def _simple_subset_generation(fn,npages,alter=0):
    c = Canvas(outputfile(fn))
    c.setFont('Helvetica', 30)
    c.drawString(100,700, 'Unicode TrueType Font Test %d pages' % npages)
    # Draw a table of Unicode characters
    for p in xrange(npages):
        for fontName in ('Vera','VeraBI'):
            c.setFont(fontName, 10)
            for i in xrange(32):
                for j in xrange(32):
                    ch = utf8(i * 32 + j+p*alter)
                    c.drawString(80 + j * 13 + int(j / 16) * 4, 600 - i * 13 - int(i / 8) * 8, ch)
        c.showPage()
    c.save()

class TTFontsTestCase(unittest.TestCase):
    "Make documents with TrueType fonts"

    def testTTF(self):
        "Test PDF generation with TrueType fonts"
        pdfmetrics.registerFont(TTFont("Vera", "Vera.ttf"))
        pdfmetrics.registerFont(TTFont("VeraBI", "VeraBI.ttf"))
        _simple_subset_generation('test_pdfbase_ttfonts1.pdf',1)
        _simple_subset_generation('test_pdfbase_ttfonts3.pdf',3)
        _simple_subset_generation('test_pdfbase_ttfonts35.pdf',3,5)

        # Do it twice with the same font object
        c = Canvas(outputfile('test_pdfbase_ttfontsadditional.pdf'))
        # Draw a table of Unicode characters
        c.setFont('Vera', 10)
        c.drawString(100, 700, 'Hello, ' + utf8(0xffee))
        c.save()


class TTFontFileTestCase(NearTestCase):
    "Tests TTFontFile, TTFontParser and TTFontMaker classes"

    def testFontFileFailures(self):
        "Tests TTFontFile constructor error checks"
        self.assertRaises(TTFError, TTFontFile, "nonexistent file")
        self.assertRaises(TTFError, TTFontFile, StringIO(""))
        self.assertRaises(TTFError, TTFontFile, StringIO("invalid signature"))
        self.assertRaises(TTFError, TTFontFile, StringIO("OTTO - OpenType not supported yet"))
        self.assertRaises(TTFError, TTFontFile, StringIO("\0\1\0\0"))

    def testFontFileReads(self):
        "Tests TTFontParset.read_xxx"

        class FakeTTFontFile(TTFontParser):
            def __init__(self, data):
                self._ttf_data = data
                self._pos = 0

        ttf = FakeTTFontFile("\x81\x02\x03\x04" "\x85\x06" "ABCD" "\x7F\xFF" "\x80\x00" "\xFF\xFF")
        self.assertEquals(ttf.read_ulong(), 0x81020304L) # big-endian
        self.assertEquals(ttf._pos, 4)
        self.assertEquals(ttf.read_ushort(), 0x8506)
        self.assertEquals(ttf._pos, 6)
        self.assertEquals(ttf.read_tag(), 'ABCD')
        self.assertEquals(ttf._pos, 10)
        self.assertEquals(ttf.read_short(), 0x7FFF)
        self.assertEquals(ttf.read_short(), -0x8000)
        self.assertEquals(ttf.read_short(), -1)

    def testFontFile(self):
        "Tests TTFontFile and TTF parsing code"
        ttf = TTFontFile("Vera.ttf")
        self.assertEquals(ttf.name, "BitstreamVeraSans-Roman")
        self.assertEquals(ttf.flags, FF_SYMBOLIC)
        self.assertEquals(ttf.italicAngle, 0.0)
        self.assertNear(ttf.ascent,759.765625)
        self.assertNear(ttf.descent,-240.234375)
        self.assertEquals(ttf.capHeight, 759.765625)
        self.assertNear(ttf.bbox, [-183.10546875, -235.83984375, 1287.109375, 928.22265625])
        self.assertEquals(ttf.stemV, 87)
        self.assertEquals(ttf.defaultWidth, 600.09765625)

    def testAdd32(self):
        "Test add32"
        self.assertEquals(add32(10, -6), 4)
        self.assertEquals(add32(6, -10), -4&0xFFFFFFFFL)
        self.assertEquals(add32(0x80000000L, -1), 0x7FFFFFFF)
        self.assertEquals(add32(0x7FFFFFFF, 1), 0x80000000L)

    def testChecksum(self):
        "Test calcChecksum function"
        self.assertEquals(calcChecksum(""), 0)
        self.assertEquals(calcChecksum("\1"), 0x01000000)
        self.assertEquals(calcChecksum("\x01\x02\x03\x04\x10\x20\x30\x40"), 0x11223344)
        self.assertEquals(calcChecksum("\x81"), 0x81000000L)
        self.assertEquals(calcChecksum("\x81\x02"), 0x81020000L)
        self.assertEquals(calcChecksum("\x81\x02\x03"), 0x81020300L)
        self.assertEquals(calcChecksum("\x81\x02\x03\x04"), 0x81020304L)
        self.assertEquals(calcChecksum("\x81\x02\x03\x04\x05"), 0x86020304L)
        self.assertEquals(calcChecksum("\x41\x02\x03\x04\xD0\x20\x30\x40"), 0x11223344)
        self.assertEquals(calcChecksum("\xD1\x02\x03\x04\x40\x20\x30\x40"), 0x11223344)
        self.assertEquals(calcChecksum("\x81\x02\x03\x04\x90\x20\x30\x40"), 0x11223344)
        self.assertEquals(calcChecksum("\x7F\xFF\xFF\xFF\x00\x00\x00\x01"), 0x80000000L)

    def testFontFileChecksum(self):
        "Tests TTFontFile and TTF parsing code"
        file = TTFOpenFile("Vera.ttf")[1].read()
        TTFontFile(StringIO(file), validate=1) # should not fail
        file1 = file[:12345] + "\xFF" + file[12346:] # change one byte
        self.assertRaises(TTFError, TTFontFile, StringIO(file1), validate=1)
        file1 = file[:8] + "\xFF" + file[9:] # change one byte
        self.assertRaises(TTFError, TTFontFile, StringIO(file1), validate=1)

    def testSubsetting(self):
        "Tests TTFontFile and TTF parsing code"
        ttf = TTFontFile("Vera.ttf")
        subset = ttf.makeSubset([0x41, 0x42])
        subset = TTFontFile(StringIO(subset), 0)
        for tag in ('cmap', 'head', 'hhea', 'hmtx', 'maxp', 'name', 'OS/2',
                    'post', 'cvt ', 'fpgm', 'glyf', 'loca', 'prep'):
            self.assert_(subset.get_table(tag))

        subset.seek_table('loca')
        for n in range(4):
            pos = subset.read_ushort()    # this is actually offset / 2
            self.failIf(pos % 2 != 0, "glyph %d at +%d should be long aligned" % (n, pos * 2))

        self.assertEquals(subset.name, "BitstreamVeraSans-Roman")
        self.assertEquals(subset.flags, FF_SYMBOLIC)
        self.assertEquals(subset.italicAngle, 0.0)
        self.assertNear(subset.ascent,759.765625)
        self.assertNear(subset.descent,-240.234375)
        self.assertEquals(subset.capHeight, 759.765625)
        self.assertNear(subset.bbox, [-183.10546875, -235.83984375, 1287.109375, 928.22265625])
        self.assertEquals(subset.stemV, 87)

    def testFontMaker(self):
        "Tests TTFontMaker class"
        ttf = TTFontMaker()
        ttf.add("ABCD", "xyzzy")
        ttf.add("QUUX", "123")
        ttf.add("head", "12345678xxxx")
        stm = ttf.makeStream()
        ttf = TTFontParser(StringIO(stm), 0)
        self.assertEquals(ttf.get_table("ABCD"), "xyzzy")
        self.assertEquals(ttf.get_table("QUUX"), "123")


class TTFontFaceTestCase(unittest.TestCase):
    "Tests TTFontFace class"

    def testAddSubsetObjects(self):
        "Tests TTFontFace.addSubsetObjects"
        face = TTFontFace("Vera.ttf")
        doc = PDFDocument()
        fontDescriptor = face.addSubsetObjects(doc, "TestFont", [ 0x78, 0x2017 ])
        fontDescriptor = doc.idToObject[fontDescriptor.name].dict
        self.assertEquals(fontDescriptor['Type'], '/FontDescriptor')
        self.assertEquals(fontDescriptor['Ascent'], face.ascent)
        self.assertEquals(fontDescriptor['CapHeight'], face.capHeight)
        self.assertEquals(fontDescriptor['Descent'], face.descent)
        self.assertEquals(fontDescriptor['Flags'], (face.flags & ~FF_NONSYMBOLIC) | FF_SYMBOLIC)
        self.assertEquals(fontDescriptor['FontName'], "/TestFont")
        self.assertEquals(fontDescriptor['FontBBox'].sequence, face.bbox)
        self.assertEquals(fontDescriptor['ItalicAngle'], face.italicAngle)
        self.assertEquals(fontDescriptor['StemV'], face.stemV)
        fontFile = fontDescriptor['FontFile2']
        fontFile = doc.idToObject[fontFile.name]
        self.assert_(fontFile.content != "")


class TTFontTestCase(NearTestCase):
    "Tests TTFont class"

    def testParseUTF8(self):
        "Tests parse_utf8"
        self.assertEquals(parse_utf8(""), [])
        for i in range(0, 0x80):
            self.assertEquals(parse_utf8(chr(i)), [i])
        for i in range(0x80, 0xA0):
            self.assertRaises(ValueError, parse_utf8, chr(i))
        self.assertEquals(parse_utf8("abc"), [0x61, 0x62, 0x63])
        self.assertEquals(parse_utf8("\xC2\xA9x"), [0xA9, 0x78])
        self.assertEquals(parse_utf8("\xE2\x89\xA0x"), [0x2260, 0x78])
        self.assertRaises(ValueError, parse_utf8, "\xE2\x89x")
        # for i in range(0, 0xFFFF): - overkill
        for i in range(0x80, 0x200) + range(0x300, 0x400) + [0xFFFE, 0xFFFF]:
            self.assertEquals(parse_utf8(utf8(i)), [i])

    def testStringWidth(self):
        "Test TTFont.stringWidth"
        font = TTFont("Vera", "Vera.ttf")
        self.assert_(font.stringWidth("test", 10) > 0)
        width = font.stringWidth(utf8(0x2260) * 2, 1000)
        expected = font.face.getCharWidth(0x2260) * 2
        self.assertNear(width,expected)

    def testSplitString(self):
        "Tests TTFont.splitString"
        doc = PDFDocument()
        font = TTFont("Vera", "Vera.ttf")
        text = string.join(map(utf8, xrange(0, 511)), "")
        allchars = string.join(map(chr, xrange(0, 256)), "")
        nospace = allchars[:32] + allchars[33:]
        chunks = [(0, allchars), (1, nospace)]
        self.assertEquals(font.splitString(text, doc), chunks)
        # Do it twice
        self.assertEquals(font.splitString(text, doc), chunks)

        text = string.join(map(utf8, range(510, -1, -1)), "")
        allchars = string.join(map(chr, range(255, -1, -1)), "")
        nospace = allchars[:223] + allchars[224:]
        chunks = [(1, nospace), (0, allchars)]
        self.assertEquals(font.splitString(text, doc), chunks)

    def testSplitStringSpaces(self):
        # In order for justification (word spacing) to work, the space
        # glyph must have a code 32, and no other character should have
        # that code in any subset, or word spacing will be applied to it.

        doc = PDFDocument()
        font = TTFont("Vera", "Vera.ttf")
        text = string.join(map(utf8, range(512, -1, -1)), "")
        chunks = font.splitString(text, doc)
        state = font.state[doc]
        self.assertEquals(state.assignments[32], 32)
        self.assertEquals(state.subsets[0][32], 32)
        self.assertEquals(state.subsets[1][32], 32)

    def testSubsetInternalName(self):
        "Tests TTFont.getSubsetInternalName"
        doc = PDFDocument()
        font = TTFont("Vera", "Vera.ttf")
        # Actually generate some subsets
        text = string.join(map(utf8, range(0, 513)), "")
        font.splitString(text, doc)
        self.assertRaises(IndexError, font.getSubsetInternalName, -1, doc)
        self.assertRaises(IndexError, font.getSubsetInternalName, 3, doc)
        self.assertEquals(font.getSubsetInternalName(0, doc), "/F1+0")
        self.assertEquals(font.getSubsetInternalName(1, doc), "/F1+1")
        self.assertEquals(font.getSubsetInternalName(2, doc), "/F1+2")
        self.assertEquals(doc.delayedFonts, [font])

    def testAddObjectsEmpty(self):
        "TTFont.addObjects should not fail when no characters were used"
        font = TTFont("Vera", "Vera.ttf")
        doc = PDFDocument()
        font.addObjects(doc)

    def no_longer_testAddObjectsResets(self):
        "Test that TTFont.addObjects resets the font"
        # Actually generate some subsets
        doc = PDFDocument()
        font = TTFont("Vera", "Vera.ttf")
        font.splitString('a', doc)            # create some subset
        doc = PDFDocument()
        font.addObjects(doc)
        self.assertEquals(font.frozen, 0)
        self.assertEquals(font.nextCode, 0)
        self.assertEquals(font.subsets, [])
        self.assertEquals(font.assignments, {})
        font.splitString('ba', doc)           # should work

    def testParallelConstruction(self):
        "Test that TTFont can be used for different documents at the same time"
        doc1 = PDFDocument()
        doc2 = PDFDocument()
        font = TTFont("Vera", "Vera.ttf")
        self.assertEquals(font.splitString(u'hello ', doc1), [(0, 'hello ')])
        self.assertEquals(font.splitString(u'hello ', doc2), [(0, 'hello ')])
        self.assertEquals(font.splitString(u'\u0410\u0411'.encode('UTF-8'), doc1), [(0, '\x80\x81')])
        self.assertEquals(font.splitString(u'\u0412'.encode('UTF-8'), doc2), [(0, '\x80')])
        font.addObjects(doc1)
        self.assertEquals(font.splitString(u'\u0413'.encode('UTF-8'), doc2), [(0, '\x81')])
        font.addObjects(doc2)

    def testAddObjects(self):
        "Test TTFont.addObjects"
        # Actually generate some subsets
        doc = PDFDocument()
        font = TTFont("Vera", "Vera.ttf")
        font.splitString('a', doc)            # create some subset
        internalName = font.getSubsetInternalName(0, doc)[1:]
        font.addObjects(doc)
        pdfFont = doc.idToObject[internalName]
        self.assertEquals(doc.idToObject['BasicFonts'].dict[internalName], pdfFont)
        self.assertEquals(pdfFont.Name, internalName)
        self.assertEquals(pdfFont.BaseFont, "AAAAAA+BitstreamVeraSans-Roman")
        self.assertEquals(pdfFont.FirstChar, 0)
        self.assertEquals(pdfFont.LastChar, 127)
        self.assertEquals(len(pdfFont.Widths.sequence), 128)
        toUnicode = doc.idToObject[pdfFont.ToUnicode.name]
        self.assert_(toUnicode.content != "")
        fontDescriptor = doc.idToObject[pdfFont.FontDescriptor.name]
        self.assertEquals(fontDescriptor.dict['Type'], '/FontDescriptor')

    def testMakeToUnicodeCMap(self):
        "Test makeToUnicodeCMap"
        self.assertEquals(makeToUnicodeCMap("TestFont", [ 0x1234, 0x4321, 0x4242 ]),
"""/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
/CIDSystemInfo
<< /Registry (TestFont)
/Ordering (TestFont)
/Supplement 0
>> def
/CMapName /TestFont def
/CMapType 2 def
1 begincodespacerange
<00> <02>
endcodespacerange
3 beginbfchar
<00> <1234>
<01> <4321>
<02> <4242>
endbfchar
endcmap
CMapName currentdict /CMap defineresource pop
end
end""")


def makeSuite():
    suite = makeSuiteForClasses(
        TTFontsTestCase,
        TTFontFileTestCase,
        TTFontFaceTestCase,
        TTFontTestCase)
    return suite


#noruntests
if __name__ == "__main__":
    unittest.TextTestRunner().run(makeSuite())
    printLocation()
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.