Example #1
0
    def action(self, args):
        (pre, l, r, post) = args[0]
        left = l.resolve(self.parser.fontfeatures, self.parser.font)
        right = r.resolve(self.parser.fontfeatures, self.parser.font)
        precontext = None
        postcontext = None
        if pre:
            precontext = [
                g.resolve(self.parser.fontfeatures, self.parser.font)
                for g in pre
            ]
        if post:
            postcontext = [
                g.resolve(self.parser.fontfeatures, self.parser.font)
                for g in post
            ]

        lefttoright = fontFeatures.Routine(
            rules=[fontFeatures.Substitution([left], [right])])
        righttoleft = fontFeatures.Routine(
            rules=[fontFeatures.Substitution([right], [left])])
        return [
            fontFeatures.Chaining(
                [left, right],
                lookups=[[lefttoright], [righttoleft]],
                precontext=precontext,
                postcontext=postcontext,
            )
        ]
Example #2
0
    def store(self, parser, tokens):
        from glyphtools import determine_kern, bin_glyphs_by_metric
        import fontFeatures
        import itertools

        units = int(tokens[-1].token)
        kerns = []

        bincount = 5
        lefts = parser.expandGlyphOrClassName(tokens[0].token)
        rights = parser.expandGlyphOrClassName(tokens[1].token)

        def make_kerns(rise=0, context=[], direction="LTR"):
            kerns = []
            for l in lefts:
                for r in rights:
                    if direction == "LTR":
                        kern = determine_kern(parser.font,
                                              l,
                                              r,
                                              units,
                                              offset1=(0, rise))
                    else:
                        kern = determine_kern(parser.font,
                                              r,
                                              l,
                                              units,
                                              offset1=(0, rise))
                    if abs(kern) < 5:
                        continue
                    v = fontFeatures.ValueRecord(xAdvance=kern)
                    kerns.append(
                        fontFeatures.Positioning([[r]], [v],
                                                 precontext=[[l]],
                                                 postcontext=context))
            return kerns

        context = [
            parser.expandGlyphOrClassName(x.token) for x in tokens[2:-1]
        ]
        if len(context) == 0:
            return [fontFeatures.Routine(rules=make_kerns())]

        kerns = []
        binned_contexts = [
            bin_glyphs_by_metric(parser.font,
                                 glyphs,
                                 "rise",
                                 bincount=bincount) for glyphs in context
        ]
        for c in itertools.product(*binned_contexts):
            totalrise = sum([x[1] for x in c])
            precontext = [x[0] for x in c]
            kerns.extend(
                make_kerns(totalrise, context=precontext, direction="RTL"))

        return [fontFeatures.Routine(rules=kerns)]
    def action(self, args):
        parser = self.parser
        bincount = 5
        lefts, rights, units, pre = args[0]
        lefts = lefts.resolve(parser.fontfeatures, parser.font)
        rights = rights.resolve(parser.fontfeatures, parser.font)
        pre = [g.resolve(parser.fontfeatures, parser.font) for g in pre]

        def make_kerns(rise=0, context=[], direction="LTR"):
            kerns = []
            for l in lefts:
                for r in rights:
                    if direction == "LTR":
                        kern = determine_kern(parser.font,
                                              l,
                                              r,
                                              units,
                                              offset1=(0, rise))
                    else:
                        kern = determine_kern(parser.font,
                                              r,
                                              l,
                                              units,
                                              offset1=(0, rise))
                    if abs(kern) < 5:
                        continue
                    v = fontFeatures.ValueRecord(xAdvance=kern)
                    kerns.append(
                        fontFeatures.Positioning([[r]], [v],
                                                 precontext=[[l]],
                                                 postcontext=context))
            return kerns

        if not pre:
            return [fontFeatures.Routine(rules=make_kerns())]

        kerns = []
        binned_contexts = [
            bin_glyphs_by_metric(parser.font,
                                 glyphs,
                                 "rise",
                                 bincount=bincount) for glyphs in pre
        ]
        for c in itertools.product(*binned_contexts):
            totalrise = sum([x[1] for x in c])
            precontext = [x[0] for x in c]
            kerns.extend(
                make_kerns(totalrise, context=precontext, direction="RTL"))

        return [fontFeatures.Routine(rules=kerns)]
Example #4
0
    def action(self, args):
        (routinename, statements, flags_languages) = args
        flags, languages = flags_languages

        if routinename is not None:
            routinename = routinename[0].value

        if flags is None: flags = []

        if not statements:
            rr = fontFeatures.RoutineReference(name = routinename)
            return [rr]
        r = fontFeatures.Routine()
        if routinename:
            r.name = routinename
        r.rules = []
        for res in self.parser.filterResults(statements):
            if isinstance(res, fontFeatures.Routine):
                r.rules.extend(res.rules)
            else:
                r.rules.append(res)
        r.flags = 0
        for f in flags:
            if isinstance(f, tuple):
                r.flags |= f[0]
                if f[0] == 0x10:
                    r.markFilteringSet = f[1].resolve(self.parser.fontfeatures, self.parser.font)
                elif f[0] == 0xFF00:
                    r.markAttachmentSet = f[1].resolve(self.parser.fontfeatures, self.parser.font)
            else:
                r.flags |= f
        r.languages = languages
        if not self.parser.current_feature:
            self.parser.fontfeatures.routines.append(r)
        return [r]
Example #5
0
 def unparseMarkToLigature(self, lookup):
     """Turn a GPOS5 (mark to ligature) subtable into a fontFeatures Routine."""
     b = fontFeatures.Routine(name=self.getname("MarkToLigature" +
                                                self.gensym()))
     self._fix_flags(b, lookup)
     self.unparsable(b, "Mark to lig pos", lookup)
     return b, []
Example #6
0
 def unparseCursiveAttachment(self, lookup):
     """Turn a GPOS3 (cursive attachment) subtable into a fontFeatures Routine."""
     b = fontFeatures.Routine(name=self.getname("CursiveAttachment" + self.gensym()))
     self._fix_flags(b, lookup)
     entries = {}
     exits = {}
     for s in lookup.SubTable:
         assert s.Format == 1
         for glyph, record in zip(s.Coverage.glyphs, s.EntryExitRecord):
             if record.EntryAnchor:
                 entries[glyph] = (
                     record.EntryAnchor.XCoordinate,
                     record.EntryAnchor.YCoordinate,
                 )
             if record.ExitAnchor:
                 exits[glyph] = (
                     record.ExitAnchor.XCoordinate,
                     record.ExitAnchor.YCoordinate,
                 )
     b.addRule(
         fontFeatures.Attachment(
             "cursive_entry",
             "cursive_exit",
             entries,
             exits,
             flags=lookup.LookupFlag,
             address=self.currentLookup,
         )
     )
     return b, []
Example #7
0
 def action(self, parser, routinename, tail):
     if not tail:
         rr = fontFeatures.RoutineReference(name=routinename)
         return [rr]
     statements, flags = tail
     r = fontFeatures.Routine()
     if routinename:
         r.name = routinename
     r.rules = []
     for res in parser.filterResults(statements):
         if isinstance(res, fontFeatures.Routine):
             r.rules.extend(res.rules)
         else:
             r.rules.append(res)
     r.flags = 0
     for f in flags:
         if isinstance(f, tuple):
             r.flags |= f[0]
             if f[0] == 0x10:
                 r.markFilteringSet = f[1].resolve(parser.fontfeatures,
                                                   parser.font)
             elif f[0] == 0xFF00:
                 r.markAttachmentSet = f[1].resolve(parser.fontfeatures,
                                                    parser.font)
         else:
             r.flags |= f
     if not parser.current_feature:
         parser.fontfeatures.routines.append(r)
     return [r]
Example #8
0
 def unparseMarkToMark(self, lookup):
     """Turn a GPOS6 (mark to mark) subtable into a fontFeatures Routine."""
     b = fontFeatures.Routine(name=self.getname("MarkToMark" + self.gensym()))
     self._fix_flags(b, lookup)
     for subtable in lookup.SubTable:  # fontTools.ttLib.tables.otTables.MarkBasePos
         assert subtable.Format == 1
         for classId in range(0, subtable.ClassCount):
             anchorClassPrefix = "Anchor" + self.gensym()
             marks = self._formatMarkArray(
                 subtable.Mark1Array, subtable.Mark1Coverage, classId
             )
             bases = self._formatMark2Array(
                 subtable.Mark2Array, subtable.Mark2Coverage, classId
             )
             b.addRule(
                 fontFeatures.Attachment(
                     anchorClassPrefix,
                     anchorClassPrefix + "_",
                     bases,
                     marks,
                     font=self.font,
                     address=self.currentLookup,
                     flags=lookup.LookupFlag,
                     force_markmark=True
                 )
             )
     return b, []
Example #9
0
    def unparseSinglePositioning(self, lookup):
        """Turn a GPOS1 (single positioning) subtable into a fontFeatures Routine."""
        b = fontFeatures.Routine(name=self.getname("SinglePositioning" + self.gensym()))
        self._fix_flags(b, lookup)

        for subtable in lookup.SubTable:
            if subtable.Format == 1:
                spos = fontFeatures.Positioning(
                    [subtable.Coverage.glyphs],
                    [self.makeValueRecord(subtable.Value)],
                    address=self.currentLookup,
                    flags=lookup.LookupFlag,
                )
                b.addRule(spos)
            else:
                # Optimize it later
                for g, v in zip(subtable.Coverage.glyphs, subtable.Value):
                    spos = fontFeatures.Positioning(
                        [[g]],
                        [self.makeValueRecord(v)],
                        address=self.currentLookup,
                        flags=lookup.LookupFlag,
                    )
                    b.addRule(spos)
        return b, []
Example #10
0
 def unparseSingleSubstitution(self, lookup):
     b = fontFeatures.Routine(
         name=self.getname("SingleSubstitution" + self.gensym())
     )
     self._fix_flags(b, lookup)
     for sub in lookup.SubTable:
         if len(sub.mapping) > 5:
             k = list(sub.mapping.keys())
             v = list(sub.mapping.values())
             b.addRule(
                 fontFeatures.Substitution(
                     [k], [v], address=self.currentLookup, flags=lookup.LookupFlag
                 )
             )
         else:
             for k, v in sub.mapping.items():
                 b.addRule(
                     fontFeatures.Substitution(
                         [[k]],
                         [[v]],
                         address=self.currentLookup,
                         flags=lookup.LookupFlag,
                     )
                 )
     return b, []
Example #11
0
 def unparseReverseContextualSubstitution(self, lookup):
     b = fontFeatures.Routine(
         name=self.getname("ReverseContextualSubstitution" + self.gensym())
     )
     self._fix_flags(b, lookup)
     for sub in lookup.SubTable:
         prefix  = []
         outputs = []
         suffix  = []
         if hasattr(sub, "BacktrackCoverage"):
             for coverage in reversed(sub.BacktrackCoverage):
                 prefix.append(coverage.glyphs)
         if hasattr(sub, "LookAheadCoverage"):
             for i, coverage in enumerate(sub.LookAheadCoverage):
                 suffix.append(coverage.glyphs)
         outputs = [ sub.Substitute ]
         inputs =  [ sub.Coverage.glyphs ]
         b.addRule(
             fontFeatures.Substitution(
                 inputs,
                 outputs,
                 prefix,
                 suffix,
                 flags=lookup.LookupFlag,
                 reverse=True
             )
         )
     return b,[]
Example #12
0
    def action(self, parser, aFrom, aTo, attachtype):
        bases = {}
        marks = {}

        def _category(k):
            return parser.fontfeatures.glyphclasses.get(
                k, parser.font[k].category)

        for k, v in parser.fontfeatures.anchors.items():
            if aFrom in v:
                bases[k] = v[aFrom]
            if aTo in v:
                marks[k] = v[aTo]
            if attachtype == "marks":
                bases = {
                    k: v
                    for k, v in bases.items() if _category(k) == "mark"
                }
            else:
                bases = {
                    k: v
                    for k, v in bases.items() if _category(k) == "base"
                }
            if attachtype != "cursive":
                marks = {
                    k: v
                    for k, v in marks.items() if _category(k) == "mark"
                }
        return [
            fontFeatures.Routine(rules=[
                fontFeatures.Attachment(
                    aFrom, aTo, bases, marks, font=parser.font)
            ])
        ]
Example #13
0
    def action(self, args):
        parser = self.parser
        (bases, matra, matras) = args
        bases = bases.resolve(parser.fontfeatures, parser.font)
        matra = matra.resolve(parser.fontfeatures, parser.font)
        matras = matras.resolve(parser.fontfeatures, parser.font)

        # Organise matras into overhang widths
        matras2bases = {}
        matrasAndOverhangs = [(m, -parser.font[m].rightMargin) for m in matras]
        for b in bases:
            w = self.find_stem(parser.font, b)
            warnings.warn("Stem location for %s: %s" % (b, w))
            (bestMatra, _) = min(matrasAndOverhangs,
                                 key=lambda s: abs(s[1] - w))
            if not bestMatra in matras2bases:
                matras2bases[bestMatra] = []
            matras2bases[bestMatra].append(b)
        rv = []
        for bestMatra, basesForThisMatra in matras2bases.items():
            rv.append(
                fontFeatures.Substitution([matra],
                                          postcontext=[basesForThisMatra],
                                          replacement=[[bestMatra]]))
        return [fontFeatures.Routine(rules=rv)]
Example #14
0
 def action(self, args):
     parser = self.parser
     sequence = args[:-1]
     routine = args[-1]
     combinations = [
         g.resolve(parser.fontfeatures, parser.font) for g in sequence
     ]
     named = [x for x in parser.fontfeatures.routines if x.name == routine]
     if len(named) != 1:
         raise ValueError("Could not find routine called %s" % routine)
     routine = named[0]
     janky = fontFeatures.jankyPOS.JankyPos(parser.font, direction="RTL")
     col = collidoscope.Collidoscope(
         None,
         {
             "cursive": False,
             "faraway": True,
             "area": 0.1
         },
         ttFont=parser.font,
     )
     col.direction = "RTL"
     r = []
     clashes = 0
     total = reduce(lambda x, y: x * y, [len(x) for x in combinations], 1)
     warnings.warn("Enumerating %i sequences" % total)
     for element in itertools.product(*combinations):
         buf = janky.positioning_buffer(element)
         buf = janky.process_fontfeatures(buf, parser.fontfeatures)
         glyphs = []
         cursor = 0
         for info in buf:
             g, vr = info["glyph"], info["position"]
             offset = beziers.point.Point(cursor + (vr.xPlacement or 0),
                                          vr.yPlacement or 0)
             glyphs.append(col.get_positioned_glyph(g, offset))
             glyphs[-1]["advance"] = vr.xAdvance
             cursor = cursor + vr.xAdvance
         overlaps = col.has_collisions(glyphs)
         if not overlaps:
             continue
         clashes = clashes + 1
         if clashes >= 0.6 * total:
             break
         # warnings.warn("Overlap found in glyph sequence %s" % (" ".join(element)))
         r.append(
             fontFeatures.Chaining([[x] for x in element],
                                   lookups=[[routine] for x in element]))
     if clashes >= 0.6 * total:
         warnings.warn(
             "Most enumerations of sequence overlapped, calling routine on whole sequence instead. This may not be what you want!"
         )
         r = [
             fontFeatures.Chaining(combinations,
                                   lookups=[[routine]
                                            for x in combinations])
         ]
     return [fontFeatures.Routine(rules=r)]
Example #15
0
    def action(self, args):
        overhang_padding, glyphs = args
        parser = self.parser
        for c in ["inits", "medis"]:
            if c not in parser.fontfeatures.namedClasses:
                raise ValueError("Please define @%s class before calling")

        medis = parser.fontfeatures.namedClasses["medis"]
        inits = parser.fontfeatures.namedClasses["inits"]
        overhangers = glyphs.resolve(parser.fontfeatures, parser.font)

        binned_medis = bin_glyphs_by_metric(parser.font,
                                            medis,
                                            "run",
                                            bincount=8)
        binned_inits = bin_glyphs_by_metric(parser.font,
                                            inits,
                                            "run",
                                            bincount=8)
        rules = []
        maxchainlength = 0
        longeststring = []
        for yb in overhangers:
            entry_anchor = parser.fontfeatures.anchors[yb]["entry"]
            overhang = max(
                -get_glyph_metrics(parser.font, yb)["rsb"],
                get_glyph_metrics(parser.font, yb)["xMax"] - entry_anchor[0],
            )

            workqueue = [[x] for x in binned_inits]
            while workqueue:
                string = workqueue.pop(0)
                totalwidth = sum([max(x[1], failsafe_min_run) for x in string])
                if totalwidth > overhang or len(string) > failsafe_max_length:
                    continue

                adjustment = overhang - totalwidth + int(overhang_padding)
                postcontext = [x[0] for x in string[:-1]] + [[yb]]
                input_ = string[-1]
                example = [input_[0][0]] + [x[0] for x in postcontext]
                warnings.warn(
                    "For glyphs in %s, overhang=%i totalwidth=%i adjustment=%i"
                    % (example, overhang, totalwidth, adjustment))
                maxchainlength = max(maxchainlength, len(string))

                rules.append(
                    fontFeatures.Positioning(
                        [input_[0]],
                        [fontFeatures.ValueRecord(xAdvance=int(adjustment))],
                        postcontext=postcontext,
                    ))
                for medi in binned_medis:
                    workqueue.append([medi] + string)
        warnings.warn("Bari Ye collision maximum chain length was %i glyphs" %
                      maxchainlength)
        return [fontFeatures.Routine(rules=rules, flags=8)]
Example #16
0
 def apply(self):
     right = self.matches()
     left = self.transform(right)
     left2, right2 = [], []
     routine = fontFeatures.Routine(name=self.name.title().replace(" ", ""))
     for l, r in zip(left, right):
         if l in self.glyphnames and r in self.glyphnames:
             routine.addRule(fontFeatures.Substitution([[l]], [[r]]))
     self.project.fontfeatures.routines.extend([routine])
     self.project.fontfeatures.addFeature(self.feature, [routine])
Example #17
0
 def apply(self):
     right = self.matches()
     routine = fontFeatures.Routine(name=self.name.title().replace(" ", ""))
     for r in right:
         left = r.replace(".liga","").split("_")
         if r == "fl" or r == "fi":
             left = list(r)
         if all(l in self.glyphnames for l in left) and r in self.glyphnames:
             routine.addRule(fontFeatures.Substitution([ [l] for l in left], [[r]]))
     self.project.fontfeatures.routines.extend([routine])
     self.project.fontfeatures.addFeature(self.feature, [routine])
Example #18
0
 def _start_routine(self, location, name):
     location = "%s:%i:%i" % (location)
     self._discard_empty_routine()
     self.currentRoutine = fontFeatures.Routine(name=name, address=location)
     if not name:
         self.currentRoutine.name = "unnamed_routine_%i" % self.gensym
         self.gensym = self.gensym + 1
     self.currentRoutineFlag = 0
     if self.currentFeature:
         reference = self.ff.referenceRoutine(self.currentRoutine)
         self.ff.addFeature(self.currentFeature, [reference])
     else:
         self.ff.routines.append(self.currentRoutine)
Example #19
0
 def unparseContextual(self, lookup):
     b = fontFeatures.Routine(name=self.getname("Contextual" + self._table +
                                                self.gensym()))
     self._fix_flags(b, lookup)
     for sub in lookup.SubTable:
         if sub.Format == 1:
             self._unparse_contextual_format1(sub, b, lookup)
         elif sub.Format == 2:
             self._unparse_contextual_format2(sub, b, lookup)
         elif sub.Format == 3:
             self._unparse_contextual_format3(sub, b, lookup)
         else:
             raise ValueError
     return b, []
Example #20
0
 def unparseSingleSubstitution(self, lookup):
     """Turn a GPOS1 (single substitution) subtable into a fontFeatures Routine."""
     b = fontFeatures.Routine(name=self.getname("SingleSubstitution" +
                                                self.gensym()))
     self._fix_flags(b, lookup)
     for sub in lookup.SubTable:
         for k, v in sub.mapping.items():
             b.addRule(
                 fontFeatures.Substitution(
                     [[k]],
                     [[v]],
                     address=self.currentLookup,
                     flags=lookup.LookupFlag,
                 ))
     return b, []
Example #21
0
 def unparseChainedContextual(self, lookup):
     """Handles a generic chained contextual lookup, in various formats."""
     b = fontFeatures.Routine(name=self.getname("ChainedContextual" +
                                                self._table +
                                                self.gensym()))
     self._fix_flags(b, lookup)
     for sub in lookup.SubTable:
         if sub.Format == 1:
             self._unparse_contextual_chain_format1(sub, b, lookup)
         elif sub.Format == 2:
             self._unparse_contextual_chain_format2(sub, b, lookup)
         elif sub.Format == 3:
             self._unparse_contextual_chain_format3(sub, b, lookup)
         else:
             raise ValueError
     return b, []
Example #22
0
 def unparseAlternateSubstitution(self, lookup):
     b = fontFeatures.Routine(
         name=self.getname("AlternateSubstitution" + self.gensym())
     )
     self._fix_flags(b, lookup)
     for sub in lookup.SubTable:
         for in_glyph, out_glyphs in sub.alternates.items():
             b.addRule(
                 fontFeatures.Substitution(
                     singleglyph(in_glyph),
                     [out_glyphs],
                     address=self.currentLookup,
                     flags=lookup.LookupFlag,
                 )
             )
     return b, []
Example #23
0
    def unparseMultipleSubstitution(self, lookup):
        """Turn a GPOS2 (multiple substitution) subtable into a fontFeatures Routine."""
        b = fontFeatures.Routine(name=self.getname("MultipleSubstitution" +
                                                   self.gensym()))
        self._fix_flags(b, lookup)

        for sub in lookup.SubTable:
            for in_glyph, out_glyphs in sub.mapping.items():
                b.addRule(
                    fontFeatures.Substitution(
                        singleglyph(in_glyph),
                        [glyph(x) for x in out_glyphs],
                        address=self.currentLookup,
                        flags=lookup.LookupFlag,
                    ))
        return b, []
Example #24
0
 def action(cls, parser, ligatures):
     ligatures = sorted(ligatures.resolve(parser.fontfeatures, parser.font),
                        key=lambda a: len(a))
     rv = []
     glyphnames = "|".join(sorted(parser.font.keys(), key=lambda a: len(a)))
     for l in ligatures:
         for liglen in range(5, 0, -1):
             ligre = "^" + ("(" + glyphnames +
                            ")_") * liglen + "(" + glyphnames + ")$"
             m = re.match(ligre, l)
             if m:
                 rv.append(
                     fontFeatures.Substitution([[c] for c in m.groups()],
                                               replacement=[[l]]))
                 break
     return [fontFeatures.Routine(rules=rv)]
Example #25
0
 def unparsePairPositioning(self, lookup):
     b = fontFeatures.Routine(name=self.getname("PairPositioning" +
                                                self.gensym()))
     self._fix_flags(b, lookup)
     for subtable in lookup.SubTable:
         if subtable.Format == 1:
             for g, pair in zip(subtable.Coverage.glyphs, subtable.PairSet):
                 for vr in pair.PairValueRecord:
                     spos = fontFeatures.Positioning(
                         [[g], [vr.SecondGlyph]],
                         [
                             self.makeValueRecord(vr.Value1,
                                                  subtable.ValueFormat1),
                             self.makeValueRecord(vr.Value2,
                                                  subtable.ValueFormat2),
                         ],
                         address=self.currentLookup,
                         flags=lookup.LookupFlag,
                     )
                     b.addRule(spos)
         else:
             class1 = self._invertClassDef(subtable.ClassDef1.classDefs,
                                           self.font)
             class2 = self._invertClassDef(subtable.ClassDef2.classDefs,
                                           self.font)
             for ix1, c1 in enumerate(subtable.Class1Record):
                 if ix1 not in class1:
                     continue  # XXX
                 for ix2, c2 in enumerate(c1.Class2Record):
                     if ix2 not in class2:
                         continue  # XXX
                     vr1 = self.makeValueRecord(c2.Value1,
                                                subtable.ValueFormat1)
                     vr2 = self.makeValueRecord(c2.Value2,
                                                subtable.ValueFormat2)
                     if not vr1 and not vr2:
                         continue
                     firstClass = list(
                         set(class1[ix1]) & set(subtable.Coverage.glyphs))
                     spos = fontFeatures.Positioning(
                         [firstClass, class2[ix2]],
                         [vr1, vr2],
                         address=self.currentLookup,
                         flags=lookup.LookupFlag,
                     )
                     b.addRule(spos)
     return b, []
Example #26
0
    def unparseLookups(self):
        if not self.table.LookupList:
            return
        # Create a dummy list first, to allow resolving chained lookups
        for _ in self.table.LookupList.Lookup:
            r = fontFeatures.Routine()
            self.lookups.append(r)
            self.fontFeatures.routines.append(r)

        for lookupIdx, lookup in enumerate(self.table.LookupList.Lookup):
            routine, deps = self.unparseLookup(lookup, lookupIdx)
            debug = self.getDebugInfo(self._table, lookupIdx)
            if debug:
                routine.address = (self._table, lookupIdx, *debug)
                if debug[1]:
                    routine.name = debug[1]
            self.copyRoutineToRoutine(routine, self.lookups[lookupIdx])
Example #27
0
    def action(self, parser, filename):
        rules = {}
        reachable = set([])
        basedir = os.path.dirname(parser.current_file)
        trypath = os.path.join(basedir, filename)

        if not os.path.exists(trypath):
            trypath = filename
            if not os.path.exists(trypath):
                raise ValueError("Couldn't find connections file %s" % trypath)

        with open(trypath) as csvfile:
            reader = csv.DictReader(csvfile)
            for line in reader:
                left_glyph = line["Left Glyph"]
                remainder = list(line.items())[1:]
                for (g, v) in remainder:
                    old = g + "1"
                    if v == "1" or v == 1 or not v:
                        continue
                    replacement = g + str(v)
                    if not old in rules:
                        rules[old] = {}
                    if not replacement in rules[old]:
                        rules[old][replacement] = []
                    rules[old][replacement].append(left_glyph)

        r = fontFeatures.Routine(name="connections", flags=0x8)
        for oldglyph in rules:
            for replacement in rules[oldglyph]:
                context = rules[oldglyph][replacement]
                reachable |= set(context)
                reachable |= set([oldglyph, replacement])
                r.addRule(
                    fontFeatures.Substitution(
                        [[oldglyph]],
                        [[replacement]],
                        postcontext=[context],
                        reverse=True,
                    ))
        parser.fontfeatures.namedClasses["reachable_glyphs"] = tuple(
            sorted(reachable))

        return [r]
Example #28
0
 def unparseLigatureSubstitution(self, lookup):
     """Turn a GPOS4 (ligature substitution) subtable into a fontFeatures Routine."""
     b = fontFeatures.Routine(name=self.getname("LigatureSubstitution" +
                                                self.gensym()))
     self._fix_flags(b, lookup)
     for sub in lookup.SubTable:
         for first, ligatures in sub.ligatures.items():
             for lig in ligatures:
                 substarray = [glyph(first)]
                 for x in lig.Component:
                     substarray.append(glyph(x))
                 b.addRule(
                     fontFeatures.Substitution(
                         substarray,
                         singleglyph(lig.LigGlyph),
                         address=self.currentLookup,
                         flags=lookup.LookupFlag,
                     ))
     return b, []
Example #29
0
    def action(self, args):
        parser = self.parser
        bases = args[0].resolve(parser.fontfeatures, parser.font)
        medialra = args[1].resolve(parser.fontfeatures, parser.font)
        if len(args) == 3:
            otherras = args[2].resolve(parser.fontfeatures, parser.font)
            overshoot = 0
        else:
            overshoot = args[2]
            otherras = args[3].resolve(parser.fontfeatures, parser.font)

        # Chuck the original ra in the basket
        if overshoot is None:
            overshoot = 0
        ras = set(otherras + medialra)

        # Organise ras into overhang widths
        ras2bases = {}
        rasAndOverhangs = [(m, -parser.font[m].rightMargin + overshoot)
                           for m in ras]
        rasAndOverhangs = list(reversed(sorted(rasAndOverhangs)))

        for b in bases:
            w = parser.font[b].width - ((parser.font[b].rightMargin or 0) +
                                        (parser.font[b].leftMargin or 0))
            bestRa = None
            for ra, overhang in rasAndOverhangs:
                if overhang <= w:
                    bestRa = ra
                    break
            if not bestRa:
                continue
            if bestRa not in ras2bases:
                ras2bases[bestRa] = []
            ras2bases[bestRa].append(b)
        rv = []
        for bestRa, basesForThisRa in ras2bases.items():
            rv.append(
                fontFeatures.Substitution([medialra],
                                          postcontext=[basesForThisRa],
                                          replacement=[[bestRa]]))
        return [fontFeatures.Routine(rules=rv)]
Example #30
0
    def action(self, parser):
        glyphs = FontProxy(parser.font).glyphs
        rules = []
        for g in glyphs:
            m = re.match(self.ligature, g)
            if not m:
                continue
            first, second, position = m[1], m[2], m[3]
            if position == "u":
                first = first + "i1"
                second = second + "f1"
                rules.append(
                    fontFeatures.Substitution([[first], [second]], [[g]]))
            if position == "f":
                first = first + "m1"
                second = second + "f1"
                rules.append(
                    fontFeatures.Substitution([[first], [second]], [[g]]))

        return [fontFeatures.Routine(name="Ligatures", rules=rules, flags=0x8)]