示例#1
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, []
示例#2
0
    def store(self, parser, tokens, doFilter=None):
        import fontFeatures
        from fontFeatures.jankyPOS import JankyPos
        import collidoscope
        import warnings
        import beziers
        import itertools

        combinations = [
            parser.expandGlyphOrClassName(x.token) for x in tokens[0:-2]
        ]
        mitigation = tokens[-2].token
        units = int(tokens[-1].token)
        janky = fontFeatures.jankyPOS.JankyPos(parser.font)
        col = collidoscope.Collidoscope(None, {
            "cursive": False,
            "faraway": True,
            "area": 0
        },
                                        ttFont=parser.font)
        rv = []
        for element in itertools.product(*combinations):
            buf = janky.positioning_buffer(element)
            buf = janky.process_fontfeatures(buf, parser.fea)
            glyphs = []
            cursor = 0
            for g, vr in buf:
                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 overlaps:
                warnings.warn(
                    "Overlap found in glyph sequence %s - mitigating" %
                    (" ".join(element)))
                intersects = [p1.intersection(p2) for p1, p2 in overlaps]
                assert len(intersects) == 1
                # If it's not, we have to find the leftmost intersection
                if mitigation == "kern":
                    correction = intersects[0][0].bounds().width + units
                    v = fontFeatures.ValueRecord(xAdvance=int(correction))
                    s = fontFeatures.Positioning(
                        [[element[0]], [element[1]]],
                        [v, fontFeatures.ValueRecord()])
                else:
                    correction = intersects[0][0].bounds().height + units
                    v = fontFeatures.ValueRecord(yPlacement=int(correction))
                    s = fontFeatures.Positioning([[element[2]]], [v],
                                                 postcontext=[[element[1]],
                                                              [element[0]]])
                rv.append(s)
        return rv
示例#3
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, []
示例#4
0
 def add_specific_pair_pos(self, location, glyph1, value1, glyph2, value2):
     self._start_routine_if_necessary(location)
     location = "%s:%i:%i" % (location)
     s = fontFeatures.Positioning(glyphs=[[glyph1], [glyph2]],
                                  valuerecords=[value1, value2],
                                  address=location,
                                  languages=self.currentLanguage)
     self.currentRoutine.addRule(s)
示例#5
0
 def add_class_pair_pos(self, location, glyphclass1, value1, glyphclass2,
                        value2):
     location = "%s:%i:%i" % (location)
     s = fontFeatures.Positioning(
         glyphs=[glyphclass1, glyphclass2],
         valuerecords=[value1, value2],
         address=location,
     )
     self.currentRoutine.addRule(s)
示例#6
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)]
示例#7
0
 def add_single_pos(self, location, prefix, suffix, pos, forceChain):
     location = "%s:%i:%i" % (location)
     s = fontFeatures.Positioning(
         glyphs=[p[0] for p in pos],
         valuerecords=[p[1] for p in pos],
         precontext=prefix,
         postcontext=suffix,
         address=location,
     )
     self.currentRoutine.addRule(s)
示例#8
0
 def add_single_pos(self, location, prefix, suffix, pos, forceChain):
     self._start_routine_if_necessary(location)
     location = "%s:%i:%i" % (location)
     s = fontFeatures.Positioning(glyphs=[p[0] for p in pos],
                                  valuerecords=[p[1] for p in pos],
                                  precontext=[[str(g) for g in group]
                                              for group in prefix],
                                  postcontext=[[str(g) for g in group]
                                               for group in suffix],
                                  address=location,
                                  languages=self.currentLanguage)
     self.currentRoutine.addRule(s)
示例#9
0
    def action(self, parser, glyphs, maxlen, distance):
        glyphs = glyphs.resolve(parser.fontfeatures, parser.font)
        postcontext = [glyphs]
        rules = []

        # r = fontFeatures.Routine(flags=0x12)
        # r.markFilteringSet = parser.fontfeatures.namedClasses["nuktas"]
        for i in reversed(range(1, maxlen)):
            positions = []
            input_ = [glyphs] * i
            # Only include lo-rise medis
            for j in range(1, i + 1):
                positions.append(
                    fontFeatures.ValueRecord((i - j + 1) * distance, 0, 0, 0)
                )
            rules.append(
                fontFeatures.Positioning(input_, positions, postcontext=postcontext)
            )

        return rules
 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
示例#11
0
def buildPos(self, font, lookuptype, ff):
    """Build a GPOS subtable."""
    builders = []
    if lookuptype == 1:
        builder = otl.SinglePosBuilder(font, self.address)
        for rule in self.rules:
            ot_valuerecs = [
                x.toOTValueRecord(ff, pairPosContext=False)
                for x in rule.valuerecords
            ]
            for glyph in rule.glyphs[0]:
                builder.add_pos(rule.address, glyph, ot_valuerecs[0])
    elif lookuptype == 2:
        builder = otl.PairPosBuilder(font, self.address)
        for rule in self.rules:
            ot_valuerecs = [
                x.toOTValueRecord(ff, pairPosContext=True)
                for x in rule.valuerecords
            ]
            if len(rule.glyphs[0]) == 1 and len(rule.glyphs[1]) == 1:
                builder.addGlyphPair(
                    rule.address,
                    rule.glyphs[0][0],
                    ot_valuerecs[0],
                    rule.glyphs[1][0],
                    ot_valuerecs[1],
                )
            else:
                builder.addClassPair(
                    rule.address,
                    tuple(rule.glyphs[0]),
                    ot_valuerecs[0],
                    tuple(rule.glyphs[1]),
                    ot_valuerecs[1],
                )
    elif lookuptype == 3:
        builder = otl.CursivePosBuilder(font, self.address)
        for rule in self.rules:
            allglyphs = set(rule.bases.keys()) | set(rule.marks.keys())
            for g in allglyphs:
                builder.attachments[g] = (
                    g in rule.bases and makeAnchor(rule.bases[g], ff) or None,
                    g in rule.marks and makeAnchor(rule.marks[g], ff) or None,
                )
    elif lookuptype == 4 or lookuptype == 6:
        if lookuptype == 4:
            builder = otl.MarkBasePosBuilder(font, self.address)
            baseholder = builder.bases
        else:
            builder = otl.MarkMarkPosBuilder(font, self.address)
            baseholder = builder.baseMarks
        for r in self.rules:
            for mark, anchor in r.marks.items():
                if mark in builder.marks:
                    raise ValueError(
                        "A mark glyph %s tried to be in two categories (%s, %s)"
                        % (mark, r.mark_name, builder.marks[mark][0]))
                builder.marks[mark] = (r.mark_name, makeAnchor(anchor, ff))
            for base, anchor in r.bases.items():
                if base not in baseholder:
                    baseholder[base] = {}
                baseholder[base][r.mark_name] = makeAnchor(anchor, ff)
        # XXX. We may need to express this as multiple lookups
    elif lookuptype == 8:
        builder = otl.ChainContextPosBuilder(font, self.address)
        for r in self.rules:
            new_lookup_list = []
            import fontFeatures
            if isinstance(r, fontFeatures.Positioning):
                lookups = []
                for glyphs, vr in zip(r.glyphs, r.valuerecords):
                    # Make a fake pos routine
                    subbuilder = buildPos(
                        fontFeatures.Routine(rules=[
                            fontFeatures.Positioning([glyphs],
                                                     valuerecords=[vr])
                        ]), font, 1, ff)
                    builders.extend(subbuilder)
                    new_lookup_list.append(subbuilder)
                glyphs = r.glyphs
            else:
                for list_of_lookups in r.lookups:
                    new_lookup_list.append([
                        lu.routine.__builder for lu in (list_of_lookups or [])
                    ])
                glyphs = r.input
            builder.rules.append(
                otl.ChainContextualRule(
                    r.precontext or [],
                    glyphs or [],
                    r.postcontext or [],
                    new_lookup_list,
                ))
    else:
        raise ValueError("Don't know how to build a POS type %i lookup" %
                         lookuptype)
    builder.lookupflag = self.flags
    # XXX mark filtering set
    self.__builder = builder
    builders.append(builder)
    return builders
示例#12
0
 def add_specific_pair_pos(self, location, glyph1, value1, glyph2, value2):
     location = "%s:%i:%i" % (location)
     s = fontFeatures.Positioning(glyphs=[[glyph1], [glyph2]],
                                  valuerecords=[value1, value2],
                                  address=location)
     self.currentRoutine.addRule(s)