summaryrefslogtreecommitdiff
path: root/misc/pylib/extractor/formats/type1.py
blob: ca3f767410439a38db66d6f409abac523c57f638 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
from fontTools.t1Lib import T1Font, T1Error
from fontTools.agl import AGL2UV
from fontTools.misc.psLib import PSInterpreter
from fontTools.misc.transform import Transform
from extractor.tools import RelaxedInfo

# specification: http://partners.adobe.com/public/developer/en/font/T1_SPEC.PDF

# ----------------
# Public Functions
# ----------------

def isType1(pathOrFile):
    try:
        font = T1Font(pathOrFile)
        del font
    except T1Error:
        return False
    return True

def extractFontFromType1(pathOrFile, destination, doGlyphs=True, doInfo=True, doKerning=True, customFunctions=[]):
    source = T1Font(pathOrFile)
    destination.lib["public.glyphOrder"] = _extractType1GlyphOrder(source)
    if doInfo:
        extractType1Info(source, destination)
    if doGlyphs:
        extractType1Glyphs(source, destination)
    if doKerning:
        # kerning extraction is not supported yet.
        # in theory, it could be retried from an AFM.
        # we need to find the AFM naming rules so that we can sniff for the file.
        pass
    for function in customFunctions:
        function(source, destination)

def extractType1Info(source, destination):
    info = RelaxedInfo(destination.info)
    _extractType1FontInfo(source, info)
    _extractType1Private(source, info)
    _extractType1FontMatrix(source, info)

# ----
# Info
# ----

def _extractType1FontInfo(source, info):
    sourceInfo = source["FontInfo"]
    # FontName
    info.postscriptFontName = source["FontName"]
    # version
    version = sourceInfo.get("version")
    if version is not None:
        # the spec says that version will be a string and no formatting info is given.
        # so, only move forward if the string can actually be parsed.
        try:
            # 1. convert to a float
            version = float(version)
            # 2. convert it back to a string
            version = "%.3f" % version
            # 3. split.
            versionMajor, versionMinor = version.split(".")
            # 4. convert.
            versionMajor = int(versionMajor)
            versionMinor = int(versionMinor)
            # 5. set.
            info.versionMajor = int(versionMajor)
            info.versionMinor = int(versionMinor)
        except ValueError:
            # couldn't parse. leve the object with the default values.
            pass
    # Notice
    notice = sourceInfo.get("Notice")
    if notice:
        info.copyright = notice
    # FullName
    fullName = sourceInfo.get("FullName")
    if fullName:
        info.postscriptFullName = fullName
    # FamilyName
    familyName = sourceInfo.get("FamilyName")
    if familyName:
        info.familyName = familyName
    # Weight
    postscriptWeightName = sourceInfo.get("Weight")
    if postscriptWeightName:
        info.postscriptWeightName = postscriptWeightName
    # ItalicAngle
    info.italicAngle = sourceInfo.get("ItalicAngle")
    # IsFixedPitch
    info.postscriptIsFixedPitch = sourceInfo.get("isFixedPitch")
    # UnderlinePosition/Thickness
    info.postscriptUnderlinePosition = sourceInfo.get("UnderlinePosition")
    info.postscriptUnderlineThickness = sourceInfo.get("UnderlineThickness")

def _extractType1FontMatrix(source, info):
    # units per em
    matrix = source["FontMatrix"]
    matrix = Transform(*matrix).inverse()
    info.unitsPerEm = int(round(matrix[3]))

def _extractType1Private(source, info):
    private = source["Private"]
    # UniqueID
    info.openTypeNameUniqueID = private.get("UniqueID", None)
    # BlueValues and OtherBlues
    info.postscriptBlueValues = private.get("BlueValues", [])
    info.postscriptOtherBlues = private.get("OtherBlues", [])
    # FamilyBlues and FamilyOtherBlues
    info.postscriptFamilyBlues = private.get("FamilyBlues", [])
    info.postscriptFamilyOtherBlues = private.get("FamilyOtherBlues", [])
    # BlueScale/Shift/Fuzz
    info.postscriptBlueScale = private.get("BlueScale", None)
    info.postscriptBlueShift = private.get("BlueShift", None)
    info.postscriptBlueFuzz = private.get("BlueFuzz", None)
    # StemSnapH/V
    info.postscriptStemSnapH = private.get("StemSnapH", [])
    info.postscriptStemSnapV = private.get("StemSnapV", [])
    # ForceBold
    info.postscriptForceBold = bool(private.get("ForceBold", None))

# --------
# Outlines
# --------

def extractType1Glyphs(source, destination):
    glyphSet = source.getGlyphSet()
    for glyphName in sorted(glyphSet.keys()):
        sourceGlyph = glyphSet[glyphName]
        # make the new glyph
        destination.newGlyph(glyphName)
        destinationGlyph = destination[glyphName]
        # outlines
        pen = destinationGlyph.getPen()
        sourceGlyph.draw(pen)
        # width
        destinationGlyph.width = sourceGlyph.width
        # synthesize the unicode value
        destinationGlyph.unicode = AGL2UV.get(glyphName)

# -----------
# Glyph order
# -----------

class GlyphOrderPSInterpreter(PSInterpreter):

    def __init__(self):
        PSInterpreter.__init__(self)
        self.glyphOrder = []
        self.collectTokenForGlyphOrder = False

    def do_literal(self, token):
        result = PSInterpreter.do_literal(self, token)
        if token == "/FontName":
            self.collectTokenForGlyphOrder = False
        if self.collectTokenForGlyphOrder:
            self.glyphOrder.append(result.value)
        if token == "/CharStrings":
            self.collectTokenForGlyphOrder = True
        return result

def _extractType1GlyphOrder(t1Font):
    interpreter = GlyphOrderPSInterpreter()
    interpreter.interpret(t1Font.data)
    return interpreter.glyphOrder