/// <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); } }
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> /// 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 <ushort> inputGlyphs = new List <ushort>(); //not thread safe*** public void Layout(Typeface typeface, float size, char[] str, List <GlyphPlan> glyphPlanBuffer) { //---------------------------------------------- //1. convert char[] to glyph[] //2. send to shaping engine //3. layout position of each glyph //---------------------------------------------- //check if we have created a glyph cache for the typeface GlyphsCache glyphCache; if (!_glyphCaches.TryGetValue(typeface, out glyphCache)) { //create new glyphCache = new GlyphsCache(typeface); _glyphCaches.Add(typeface, glyphCache); } //---------------------------------------------- int j = str.Length; inputGlyphs.Clear(); for (int i = 0; i < j; ++i) { //1. convert char[] to glyphIndex[] inputGlyphs.Add((ushort)typeface.LookupIndex(str[i])); } //---------------------------------------------- //glyph substitution if (j > 1) { GlyphSubStitution glyphSubstitution = new GlyphSubStitution(typeface, this.ScriptLang.shortname); glyphSubstitution.EnableLigation = this.EnableLigature; glyphSubstitution.DoSubstitution(inputGlyphs); } //---------------------------------------------- //glyph position j = inputGlyphs.Count; List <GlyphPos> glyphPositions = new List <GlyphPos>(j); for (int i = 0; i < j; ++i) { ushort glyIndex = inputGlyphs[i]; glyphPositions.Add(new GlyphPos( glyIndex, typeface.GetGlyphByIndex(glyIndex).GlyphClass, typeface.GetHAdvanceWidthFromGlyphIndex(glyIndex)) ); } PositionTecnhique posTech = this.PositionTechnique; if (j > 1 && posTech == PositionTecnhique.OpenFont) { GlyphSetPosition glyphSetPos = new GlyphSetPosition(typeface, ScriptLang.shortname); glyphSetPos.DoGlyphPosition(glyphPositions); } //-------------- float scale = typeface.CalculateFromPointToPixelScale(size); float cx = 0; float cy = 0; j = inputGlyphs.Count; for (int i = 0; i < j; ++i) { ushort glyIndex = inputGlyphs[i]; GlyphPlan glyphPlan = new GlyphPlan(glyIndex); glyphPlanBuffer.Add(glyphPlan); //this advWidth in font design unit float advWidth = typeface.GetHAdvanceWidthFromGlyphIndex(glyIndex) * scale; //---------------------------------- switch (posTech) { case PositionTecnhique.None: { glyphPlan.x = cx; glyphPlan.y = cy; glyphPlan.advX = advWidth; } break; case PositionTecnhique.OpenFont: { GlyphPos gpos_offset = glyphPositions[i]; glyphPlan.x = cx + (scale * gpos_offset.xoffset); glyphPlan.y = cy + (scale * gpos_offset.yoffset); glyphPlan.advX = advWidth; } break; case PositionTecnhique.Kerning: { glyphPlan.x = cx; glyphPlan.y = cy; glyphPlan.advX = advWidth; if (i > 0) { advWidth += typeface.GetKernDistance(glyphPlanBuffer[i - 1].glyphIndex, glyphPlanBuffer[i].glyphIndex) * scale; } } break; } cx += advWidth; } }
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 //---------------------------------------------- }