/// <summary> /// do glyph shaping and glyph out /// </summary> /// <param name="str"></param> /// <param name="startAt"></param> /// <param name="len"></param> public void Layout( char[] str, int startAt, int len) { if (_needPlanUpdate) { UpdateLayoutPlan(); } Typeface typeface = this._typeface; //clear before use _inputGlyphs.Clear(); for (int i = 0; i < len; ++i) { //convert input char to input glyphs char c = str[startAt + i]; _inputGlyphs.AddGlyph(c, (ushort)typeface.LookupIndex(c)); } //---------------------------------------------- //glyph substitution if (_gsub != null & len > 0) { //TODO: review perf here _gsub.EnableLigation = this.EnableLigature; _gsub.DoSubstitution(_inputGlyphs); // _inputGlyphs.CreateMapFromUserCharToGlyphIndics(); } //---------------------------------------------- //after glyph substitution, //number of input glyph MAY changed (increase or decrease).*** //so count again. int finalGlyphCount = _inputGlyphs.Count; //---------------------------------------------- //glyph position _glyphPositions.Clear(); for (int i = 0; i < finalGlyphCount; ++i) { //at this stage _inputGlyphs and _glyphPositions //has member 1:1 ushort glyIndex = _inputGlyphs[i]; _glyphPositions.Add(new GlyphPos( glyIndex, typeface.GetGlyphByIndex(glyIndex).GlyphClass, typeface.GetHAdvanceWidthFromGlyphIndex(glyIndex)) ); } PositionTechnique posTech = this.PositionTechnique; if (_gpos != null && len > 1 && posTech == PositionTechnique.OpenFont) { _gpos.DoGlyphPosition(_glyphPositions); } }
/// <summary> /// read GlyphPlan latest layout output /// </summary> private static void ReadOutput(GlyphLayout glyphLayout, UnscaledGlyphPlanList outputGlyphPlanList) { Typeface typeface = glyphLayout.Typeface; var glyphPositions = glyphLayout._glyphPositions; //3.read back int finalGlyphCount = glyphPositions.Count; int cx = 0; short cy = 0; PositionTechnique posTech = glyphLayout.PositionTechnique; ushort prev_index = 0; for (int i = 0; i < finalGlyphCount; ++i) { GlyphPos glyphPos = glyphPositions[i]; switch (posTech) { default: throw new NotSupportedException(); case PositionTechnique.None: outputGlyphPlanList.Append(new UnscaledGlyphPlan( 0, glyphPos.glyphIndex, glyphPos.advanceW, cx, cy)); break; case PositionTechnique.OpenFont: outputGlyphPlanList.Append(new UnscaledGlyphPlan( 0, glyphPos.glyphIndex, glyphPos.advanceW, cx + glyphPos.xoffset, (short)(cy + glyphPos.yoffset))); break; case PositionTechnique.Kerning: if (i > 0) { cx += typeface.GetKernDistance(prev_index, glyphPos.glyphIndex); } outputGlyphPlanList.Append(new UnscaledGlyphPlan( 0, prev_index = glyphPos.glyphIndex, glyphPos.advanceW, cx, cy)); break; } cx += glyphPos.advanceW; } }
void Layout(GlyphIndexList glyphs) { if (_needPlanUpdate) { UpdateLayoutPlan(); } //[C] //---------------------------------------------- //glyph substitution if (_gsub != null && glyphs.Count > 0) { //TODO: review perf here _gsub.EnableLigation = this.EnableLigature; _gsub.EnableComposition = this.EnableComposition; _gsub.DoSubstitution(glyphs); } //---------------------------------------------- //after glyph substitution, //number of input glyph MAY changed (increase or decrease).*** //so count again. int finalGlyphCount = glyphs.Count; //---------------------------------------------- //[D] //glyph position _glyphPositions.Clear(); _glyphPositions.Typeface = _typeface; for (int i = 0; i < finalGlyphCount; ++i) { //at this stage _inputGlyphs and _glyphPositions //has member 1:1 ushort glyIndex, input_codepointOffset, input_mapLen; glyphs.GetGlyphIndexAndMap(i, out glyIndex, out input_codepointOffset, out input_mapLen); // Glyph orgGlyph = _typeface.GetGlyphByIndex(glyIndex); //this is original value WITHOUT fit-to-grid adjust _glyphPositions.AddGlyph(input_codepointOffset, glyIndex, orgGlyph); } PositionTechnique posTech = this.PositionTechnique; if (_gpos != null && glyphs.Count > 1 && posTech == PositionTechnique.OpenFont) { _gpos.DoGlyphPosition(_glyphPositions); } //---------------------------------------------- //at this point, all positions are layouted at its original scale *** //then we will scale it to target scale later //---------------------------------------------- }
/// <summary> /// read latest layout output /// </summary> /// <param name="glyphLayout"></param> /// <param name="readDel"></param> public static void ReadOutput(this GlyphLayout glyphLayout, GlyphReadOutputDelegate readDel) { Typeface typeface = glyphLayout.Typeface; List <GlyphPos> glyphPositions = glyphLayout._glyphPositions; //3.read back int finalGlyphCount = glyphPositions.Count; int cx = 0; short cy = 0; PositionTechnique posTech = glyphLayout.PositionTechnique; ushort prev_index = 0; for (int i = 0; i < finalGlyphCount; ++i) { GlyphPos glyphPos = glyphPositions[i]; //---------------------------------- switch (posTech) { default: throw new NotSupportedException(); case PositionTechnique.None: readDel(i, new GlyphPlan(glyphPos.glyphIndex, cx, cy, glyphPos.advWidth)); break; case PositionTechnique.OpenFont: readDel(i, new GlyphPlan( glyphPos.glyphIndex, cx + glyphPos.xoffset, (short)(cy + glyphPos.yoffset), glyphPos.advWidth)); break; case PositionTechnique.Kerning: if (i > 0) { cx += typeface.GetKernDistance(prev_index, glyphPos.glyphIndex); } readDel(i, new GlyphPlan( prev_index = glyphPos.glyphIndex, cx, cy, glyphPos.advWidth)); break; } cx += glyphPos.advWidth; } }
/// <summary> /// do glyph shaping and glyph out /// </summary> /// <param name="str"></param> /// <param name="startAt"></param> /// <param name="len"></param> public void Layout( char[] str, int startAt, int len) { if (_needPlanUpdate) { UpdateLayoutPlan(); } //[A] // this is important! // ----------------------- // from @samhocevar's PR: (https://github.com/LayoutFarm/Typography/pull/56/commits/b71c7cf863531ebf5caa478354d3249bde40b96e) // In many places, "char" is not a valid type to handle characters, because it // only supports 16 bits.In order to handle the full range of Unicode characters, // we need to use "int". // This allows characters such as 🙌 or 𐐷 or to be treated as single codepoints even // though they are encoded as two "char"s in a C# string. _reusableCodePointFromUserCharList.Clear(); for (int i = 0; i < len; ++i) { char ch = str[startAt + i]; int codepoint = ch; if (ch >= 0xd800 && ch <= 0xdbff && i + 1 < len) { char nextCh = str[startAt + i + 1]; if (nextCh >= 0xdc00 && nextCh <= 0xdfff) { //please note: //num of codepoint may be less than original user input char ++i; codepoint = char.ConvertToUtf32(ch, nextCh); } } _reusableCodePointFromUserCharList.Add(new CodePointFromUserChar((ushort)i, codepoint)); } // //[B] // convert codepoint-list to input glyph-list // clear before use _inputGlyphs.Clear(); int codePointCount = _reusableCodePointFromUserCharList.Count; for (int i = 0; i < codePointCount; ++i) { CodePointFromUserChar cp = _reusableCodePointFromUserCharList[i]; ushort glyphIndex = _typeface.LookupIndex(cp.codePoint); if (i + 1 < codePointCount) { // Maybe this is a UVS sequence; in that case, //***SKIP*** the second codepoint CodePointFromUserChar nextCp = _reusableCodePointFromUserCharList[i + 1]; ushort variationGlyphIndex = _typeface.LookupIndex(cp.codePoint, nextCp.codePoint); if (variationGlyphIndex > 0) { //user glyph index from next codepoint glyphIndex = variationGlyphIndex; //but record as current code point i _inputGlyphs.AddGlyph(i, glyphIndex); ++i; //skip continue; //*** } } _inputGlyphs.AddGlyph(i, glyphIndex); } //[C] //---------------------------------------------- //glyph substitution if (_gsub != null & len > 0) { //TODO: review perf here _gsub.EnableLigation = this.EnableLigature; _gsub.EnableComposition = this.EnableComposition; _gsub.DoSubstitution(_inputGlyphs); } //---------------------------------------------- //after glyph substitution, //number of input glyph MAY changed (increase or decrease).*** //so count again. int finalGlyphCount = _inputGlyphs.Count; //---------------------------------------------- //[D] //glyph position _glyphPositions.Clear(); _glyphPositions.Typeface = _typeface; for (int i = 0; i < finalGlyphCount; ++i) { //at this stage _inputGlyphs and _glyphPositions //has member 1:1 ushort glyIndex, input_codepointOffset, input_mapLen; _inputGlyphs.GetGlyphIndexAndMap(i, out glyIndex, out input_codepointOffset, out input_mapLen); // Glyph orgGlyph = _typeface.GetGlyphByIndex(glyIndex); //this is original value WITHOUT fit-to-grid adjust _glyphPositions.AddGlyph((short)input_codepointOffset, glyIndex, orgGlyph); } PositionTechnique posTech = this.PositionTechnique; if (_gpos != null && len > 1 && posTech == PositionTechnique.OpenFont) { _gpos.DoGlyphPosition(_glyphPositions); } //---------------------------------------------- //at this point, all positions are layouted at its original scale *** //then we will scale it to target scale later //---------------------------------------------- }
List <int> _codepoints = new List <int>();//not thread-safe*** /// <summary> /// do glyph shaping and glyph out /// </summary> /// <param name="str"></param> /// <param name="startAt"></param> /// <param name="len"></param> public void Layout( char[] str, int startAt, int len) { if (_needPlanUpdate) { UpdateLayoutPlan(); } // this is important! // ----------------------- // from @samhocevar's PR: (https://github.com/LayoutFarm/Typography/pull/56/commits/b71c7cf863531ebf5caa478354d3249bde40b96e) // In many places, "char" is not a valid type to handle characters, because it // only supports 16 bits.In order to handle the full range of Unicode characters, // we need to use "int". // This allows characters such as 🙌 or 𐐷 or to be treated as single codepoints even // though they are encoded as two "char"s in a C# string. _codepoints.Clear(); for (int i = 0; i < len; ++i) { char ch = str[startAt + i]; int codepoint = ch; if (Char.IsHighSurrogate(ch) && i + 1 < len) { char nextCh = str[startAt + i + 1]; if (Char.IsLowSurrogate(nextCh)) { ++i; codepoint = char.ConvertToUtf32(ch, nextCh); } } _codepoints.Add(codepoint); } // clear before use _inputGlyphs.Clear(); // convert codepoints to input glyphs for (int i = 0; i < _codepoints.Count; ++i) { int codepoint = _codepoints[i]; ushort glyphIndex = _typeface.LookupIndex(codepoint); if (i + 1 < _codepoints.Count) { // Maybe this is a UVS sequence; in that case, skip the second codepoint int nextCodepoint = _codepoints[i + 1]; ushort variationGlyphIndex = _typeface.LookupIndex(codepoint, nextCodepoint); if (variationGlyphIndex > 0) { ++i; glyphIndex = variationGlyphIndex; } } _inputGlyphs.AddGlyph(codepoint, glyphIndex); } //---------------------------------------------- //glyph substitution if (_gsub != null & len > 0) { //TODO: review perf here _gsub.EnableLigation = this.EnableLigature; _gsub.EnableComposition = this.EnableComposition; _gsub.DoSubstitution(_inputGlyphs); // _inputGlyphs.CreateMapFromUserCharToGlyphIndices(); } //---------------------------------------------- //after glyph substitution, //number of input glyph MAY changed (increase or decrease).*** //so count again. int finalGlyphCount = _inputGlyphs.Count; //---------------------------------------------- //glyph position _glyphPositions.Clear(); _glyphPositions.Typeface = _typeface; for (int i = 0; i < finalGlyphCount; ++i) { //at this stage _inputGlyphs and _glyphPositions //has member 1:1 ushort glyIndex = _inputGlyphs[i]; // TtfGlyph orgGlyph = _typeface.GetGlyphByIndex(glyIndex); //this is original value WITHOUT fit-to-grid adjust _glyphPositions.AddGlyph(glyIndex, orgGlyph); } PositionTechnique posTech = this.PositionTechnique; if (_gpos != null && len > 1 && posTech == PositionTechnique.OpenFont) { _gpos.DoGlyphPosition(_glyphPositions); } //---------------------------------------------- //at this point, all position is layout at original scale *** //then we will scale it to target scale later //---------------------------------------------- }