/// <summary>Checks if given glyph line at the given position matches given rule.</summary> /// <returns> /// either index which corresponds to the last glyph of the matching context inside the glyph line if context matches, /// or -1 if context doesn't match. /// </returns> protected internal virtual int CheckIfContextMatch(GlyphLine line, ContextualSubstRule rule) { int j; OpenTableLookup.GlyphIndexer gidx = new OpenTableLookup.GlyphIndexer(); gidx.line = line; gidx.idx = line.idx; //Note, that starting index shall be 1 for (j = 1; j < rule.GetContextLength(); ++j) { gidx.NextGlyph(openReader, lookupFlag); if (gidx.glyph == null || !rule.IsGlyphMatchesInput(gidx.glyph.GetCode(), j)) { break; } } bool isMatch = j == rule.GetContextLength(); if (isMatch) { return(gidx.idx); } else { return(-1); } }
public override bool TransformOne(GlyphLine line) { if (line.idx >= line.end || line.idx < line.start) { return(false); } bool changed = false; Glyph g1 = line.Get(line.idx); IDictionary <int, GposLookupType2.PairValueFormat> m = gposMap.Get(g1.GetCode()); if (m != null) { OpenTableLookup.GlyphIndexer gi = new OpenTableLookup.GlyphIndexer(); gi.line = line; gi.idx = line.idx; gi.NextGlyph(openReader, lookupFlag); if (gi.glyph != null) { GposLookupType2.PairValueFormat pv = m.Get(gi.glyph.GetCode()); if (pv != null) { Glyph g2 = gi.glyph; line.Set(line.idx, new Glyph(g1, 0, 0, pv.first.XAdvance, pv.first.YAdvance, 0)); line.Set(gi.idx, new Glyph(g2, 0, 0, pv.second.XAdvance, pv.second.YAdvance, 0)); line.idx = gi.idx; changed = true; } } } return(changed); }
public override bool TransformOne(GlyphLine line) { if (line.idx >= line.end) { return(false); } if (openReader.IsSkip(line.Get(line.idx).GetCode(), lookupFlag)) { line.idx++; return(false); } bool changed = false; OpenTableLookup.GlyphIndexer gi = null; foreach (GposLookupType4.MarkToBase mb in marksbases) { OtfMarkRecord omr = mb.marks.Get(line.Get(line.idx).GetCode()); if (omr == null) { continue; } if (gi == null) { gi = new OpenTableLookup.GlyphIndexer(); gi.idx = line.idx; gi.line = line; while (true) { gi.PreviousGlyph(openReader, lookupFlag); if (gi.glyph == null) { break; } // not mark => base glyph if (!mb.marks.ContainsKey(gi.glyph.GetCode())) { break; } } if (gi.glyph == null) { break; } } GposAnchor[] gpas = mb.bases.Get(gi.glyph.GetCode()); if (gpas == null) { continue; } int markClass = omr.markClass; GposAnchor baseAnchor = gpas[markClass]; GposAnchor markAnchor = omr.anchor; line.Set(line.idx, new Glyph(line.Get(line.idx), -markAnchor.XCoordinate + baseAnchor.XCoordinate, -markAnchor .YCoordinate + baseAnchor.YCoordinate, 0, 0, gi.idx - line.idx)); changed = true; break; } line.idx++; return(changed); }
public virtual void SubstituteManyToOne(OpenTypeFontTableReader tableReader, int lookupFlag, int rightPartLen , int substitutionGlyphIndex) { OpenTableLookup.GlyphIndexer gidx = new OpenTableLookup.GlyphIndexer(); gidx.line = this; gidx.idx = idx; StringBuilder chars = new StringBuilder(); Glyph currentGlyph = glyphs[idx]; if (currentGlyph.GetChars() != null) { chars.Append(currentGlyph.GetChars()); } else { if (currentGlyph.HasValidUnicode()) { chars.Append(iText.IO.Util.TextUtil.ConvertFromUtf32(currentGlyph.GetUnicode())); } } for (int j = 0; j < rightPartLen; ++j) { gidx.NextGlyph(tableReader, lookupFlag); currentGlyph = glyphs[gidx.idx]; if (currentGlyph.GetChars() != null) { chars.Append(currentGlyph.GetChars()); } else { if (currentGlyph.HasValidUnicode()) { chars.Append(iText.IO.Util.TextUtil.ConvertFromUtf32(currentGlyph.GetUnicode())); } } RemoveGlyph(gidx.idx--); } char[] newChars = new char[chars.Length]; chars.GetChars(0, chars.Length, newChars, 0); Glyph newGlyph = tableReader.GetGlyph(substitutionGlyphIndex); newGlyph.SetChars(newChars); glyphs[idx] = newGlyph; end -= rightPartLen; }
public override bool TransformOne(GlyphLine line) { //TODO > if (line.idx >= line.end) { return(false); } bool changed = false; Glyph g = line.Get(line.idx); bool match = false; if (ligatures.ContainsKey(g.GetCode()) && !openReader.IsSkip(g.GetCode(), lookupFlag)) { OpenTableLookup.GlyphIndexer gidx = new OpenTableLookup.GlyphIndexer(); gidx.line = line; IList <int[]> ligs = ligatures.Get(g.GetCode()); foreach (int[] lig in ligs) { match = true; gidx.idx = line.idx; for (int j = 1; j < lig.Length; ++j) { gidx.NextGlyph(openReader, lookupFlag); if (gidx.glyph == null || gidx.glyph.GetCode() != lig[j]) { match = false; break; } } if (match) { line.SubstituteManyToOne(openReader, lookupFlag, lig.Length - 1, lig[0]); break; } } } if (match) { changed = true; } line.idx++; return(changed); }
public override bool TransformOne(GlyphLine line) { if (line.idx >= line.end || line.idx < line.start) { return(false); } Glyph g1 = line.Get(line.idx); if (!coverageSet.Contains(g1.GetCode())) { return(false); } int c1 = classDef1.GetOtfClass(g1.GetCode()); GposLookupType2.PairValueFormat[] pvs = posSubs.Get(c1); if (pvs == null) { return(false); } OpenTableLookup.GlyphIndexer gi = new OpenTableLookup.GlyphIndexer(); gi.line = line; gi.idx = line.idx; gi.NextGlyph(openReader, lookupFlag); if (gi.glyph == null) { return(false); } Glyph g2 = gi.glyph; int c2 = classDef2.GetOtfClass(g2.GetCode()); if (c2 >= pvs.Length) { return(false); } GposLookupType2.PairValueFormat pv = pvs[c2]; line.Set(line.idx, new Glyph(g1, 0, 0, pv.first.XAdvance, pv.first.YAdvance, 0)); line.Set(gi.idx, new Glyph(g2, 0, 0, pv.second.XAdvance, pv.second.YAdvance, 0)); line.idx = gi.idx; return(true); }
public override bool TransformOne(GlyphLine line) { bool changed = false; int oldLineStart = line.start; int oldLineEnd = line.end; int initialLineIndex = line.idx; foreach (ContextualSubTable subTable in subTables) { ContextualSubstRule contextRule = subTable.GetMatchingContextRule(line); if (contextRule == null) { continue; } int lineEndBeforeSubstitutions = line.end; SubstLookupRecord[] substLookupRecords = contextRule.GetSubstLookupRecords(); OpenTableLookup.GlyphIndexer gidx = new OpenTableLookup.GlyphIndexer(); gidx.line = line; foreach (SubstLookupRecord substRecord in substLookupRecords) { // There could be some skipped glyphs inside the context sequence, therefore currently GlyphIndexer and // nextGlyph method are used to get to the glyph at "substRecord.sequenceIndex" index gidx.idx = initialLineIndex; for (int i = 0; i < substRecord.sequenceIndex; ++i) { gidx.NextGlyph(openReader, lookupFlag); } line.idx = gidx.idx; OpenTableLookup lookupTable = openReader.GetLookupTable(substRecord.lookupListIndex); changed = lookupTable.TransformOne(line) || changed; } line.idx = line.end; line.start = oldLineStart; int lenDelta = lineEndBeforeSubstitutions - line.end; line.end = oldLineEnd - lenDelta; return(changed); } ++line.idx; return(changed); }
public override bool TransformOne(GlyphLine line) { // TODO it seems that for complex cases (symbol1, symbol2, mark, symbol3) and (symbol1, symbol2, symbol3) compose a ligature, // mark should be placed in the corresponding anchor of that ligature (second component's anchor). // But for now we do not store all the substitution info and therefore not able to follow that logic. // Place the mark symbol in the first available place for now. if (line.idx >= line.end) { return(false); } if (openReader.IsSkip(line.Get(line.idx).GetCode(), lookupFlag)) { line.idx++; return(false); } bool changed = false; OpenTableLookup.GlyphIndexer gi = null; foreach (GposLookupType5.MarkToLigature mb in marksligatures) { OtfMarkRecord omr = mb.marks.Get(line.Get(line.idx).GetCode()); if (omr == null) { continue; } if (gi == null) { gi = new OpenTableLookup.GlyphIndexer(); gi.idx = line.idx; gi.line = line; while (true) { gi.PreviousGlyph(openReader, lookupFlag); if (gi.glyph == null) { break; } // not mark => ligature glyph if (!mb.marks.ContainsKey(gi.glyph.GetCode())) { break; } } if (gi.glyph == null) { break; } } IList <GposAnchor[]> gpas = mb.ligatures.Get(gi.glyph.GetCode()); if (gpas == null) { continue; } int markClass = omr.markClass; for (int component = 0; component < gpas.Count; component++) { if (gpas[component][markClass] != null) { GposAnchor baseAnchor = gpas[component][markClass]; GposAnchor markAnchor = omr.anchor; line.Add(line.idx, new Glyph(line.Get(line.idx), markAnchor.XCoordinate - baseAnchor.XCoordinate, markAnchor .YCoordinate - baseAnchor.YCoordinate, 0, 0, gi.idx - line.idx)); changed = true; break; } } break; } line.idx++; return(changed); }
public override bool TransformOne(GlyphLine line) { if (line.idx >= line.end) { return(false); } if (openReader.IsSkip(line.Get(line.idx).GetCode(), lookupFlag)) { line.idx++; return(false); } bool changed = false; OpenTableLookup.GlyphIndexer gi = null; foreach (GposLookupType6.MarkToBaseMark mb in marksbases) { OtfMarkRecord omr = mb.marks.Get(line.Get(line.idx).GetCode()); if (omr == null) { continue; } if (gi == null) { gi = new OpenTableLookup.GlyphIndexer(); gi.idx = line.idx; gi.line = line; while (true) { int prev = gi.idx; // avoid attaching this mark glyph to another very distant mark glyph bool foundBaseGlyph = false; gi.PreviousGlyph(openReader, lookupFlag); if (gi.idx != -1) { for (int i = gi.idx; i < prev; i++) { if (openReader.GetGlyphClass(line.Get(i).GetCode()) == OtfClass.GLYPH_BASE) { foundBaseGlyph = true; break; } } } if (foundBaseGlyph) { gi.glyph = null; break; } if (gi.glyph == null) { break; } if (mb.baseMarks.ContainsKey(gi.glyph.GetCode())) { break; } } if (gi.glyph == null) { break; } } GposAnchor[] gpas = mb.baseMarks.Get(gi.glyph.GetCode()); if (gpas == null) { continue; } int markClass = omr.markClass; GposAnchor baseAnchor = gpas[markClass]; GposAnchor markAnchor = omr.anchor; line.Set(line.idx, new Glyph(line.Get(line.idx), -markAnchor.XCoordinate + baseAnchor.XCoordinate, -markAnchor .YCoordinate + baseAnchor.YCoordinate, 0, 0, gi.idx - line.idx)); changed = true; break; } line.idx++; return(changed); }
public override bool TransformOne(GlyphLine line) { if (line.idx >= line.end) { return(false); } if (openReader.IsSkip(line.Get(line.idx).GetCode(), lookupFlag)) { line.idx++; return(false); } bool changed = false; OpenTableLookup.GlyphIndexer ligatureGlyphIndexer = null; foreach (GposLookupType5.MarkToLigature mb in marksligatures) { OtfMarkRecord omr = mb.marks.Get(line.Get(line.idx).GetCode()); if (omr == null) { continue; } if (ligatureGlyphIndexer == null) { ligatureGlyphIndexer = new OpenTableLookup.GlyphIndexer(); ligatureGlyphIndexer.idx = line.idx; ligatureGlyphIndexer.line = line; while (true) { ligatureGlyphIndexer.PreviousGlyph(openReader, lookupFlag); if (ligatureGlyphIndexer.glyph == null) { break; } // not mark => ligature glyph if (!mb.marks.ContainsKey(ligatureGlyphIndexer.glyph.GetCode())) { break; } } if (ligatureGlyphIndexer.glyph == null) { break; } } IList <GposAnchor[]> componentAnchors = mb.ligatures.Get(ligatureGlyphIndexer.glyph.GetCode()); if (componentAnchors == null) { continue; } int markClass = omr.markClass; // TODO DEVSIX-3732 For complex cases like (glyph1, glyph2, mark, glyph3) and // (glyph1, mark, glyph2, glyph3) when the base glyphs compose a ligature and the mark // is attached to the ligature afterwards, mark should be placed in the corresponding anchor // of that ligature (by finding the right component's anchor). // Excerpt from Microsoft Docs: "For a given mark assigned to a particular class, the appropriate // base attachment point is determined by which ligature component the mark is associated with. // This is dependent on the original character string and subsequent character- or glyph-sequence // processing, not the font data alone. While a text-layout client is performing any character-based // preprocessing or any glyph-substitution operations using the GSUB table, the text-layout client // must keep track of the associations of marks to particular ligature-glyph components." // For now we do not store all the substitution info and therefore not able to follow that logic. // We place the mark symbol in the last available place for now (seems to be better default than // first available place). for (int component = componentAnchors.Count - 1; component >= 0; component--) { if (componentAnchors[component][markClass] != null) { GposAnchor baseAnchor = componentAnchors[component][markClass]; GposAnchor markAnchor = omr.anchor; line.Set(line.idx, new Glyph(line.Get(line.idx), baseAnchor.XCoordinate - markAnchor.XCoordinate, baseAnchor .YCoordinate - markAnchor.YCoordinate, 0, 0, ligatureGlyphIndexer.idx - line.idx)); changed = true; break; } } break; } line.idx++; return(changed); }