public void AddGlyph(ushort glyphIndex, TtfGlyph glyph) { if (!glyph.HasOriginalAdvancedWidth) { glyph.OriginalAdvanceWidth = _typeface.GetHAdvanceWidthFromGlyphIndex(glyphIndex); } _glyphs.Add(new GlyphPos(glyphIndex, glyph.GlyphClass, glyph.OriginalAdvanceWidth)); }
void FillMarkAttachmentClassDefs(TtfGlyph[] inputGlyphs) { //Mark Attachment Class Definition Table //A Mark Class Definition Table is used to assign mark glyphs into different classes //that can be used in lookup tables within the GSUB or GPOS table to control how mark glyphs within a glyph sequence are treated by lookups. //For more information on the use of mark attachment classes, //see the description of lookup flags in the “Lookup Table” section of the chapter, OpenType Layout Common Table Formats. ClassDefTable markAttachmentClassDef = this.MarkAttachmentClassDef; if (markAttachmentClassDef == null) { return; } //----------------------------------------- switch (markAttachmentClassDef.Format) { default: Utils.WarnUnimplemented("GDEF MarkAttachmentClassDef Table Format {0}", markAttachmentClassDef.Format); break; case 1: { ushort startGlyph = markAttachmentClassDef.startGlyph; ushort[] classValues = markAttachmentClassDef.classValueArray; int len = classValues.Length; int gIndex = startGlyph; for (int i = 0; i < len; ++i) { #if DEBUG TtfGlyph dbugTestGlyph = inputGlyphs[gIndex]; #endif inputGlyphs[gIndex].MarkClassDef = classValues[i]; gIndex++; } } break; case 2: { ClassDefTable.ClassRangeRecord[] records = markAttachmentClassDef.records; int len = records.Length; for (int n = 0; n < len; ++n) { ClassDefTable.ClassRangeRecord rec = records[n]; for (int i = rec.startGlyphId; i <= rec.endGlyphId; ++i) { #if DEBUG TtfGlyph dbugTestGlyph = inputGlyphs[i]; #endif inputGlyphs[i].MarkClassDef = rec.classNo; } } } break; } }
TtfGlyph ReadCompositeGlyph(TtfGlyph[] createdGlyphs, BinaryReader reader, uint tableOffset, int compositeGlyphIndex) { //------------------------------------------------------ //https://www.microsoft.com/typography/OTSPEC/glyf.htm //Composite Glyph Description //This is the table information needed for composite glyphs (numberOfContours is -1). //A composite glyph starts with two USHORT values (“flags” and “glyphIndex,” i.e. the index of the first contour in this composite glyph); //the data then varies according to “flags”). //Type Name Description //USHORT flags component flag //USHORT glyphIndex glyph index of component //VARIABLE argument1 x-offset for component or point number; type depends on bits 0 and 1 in component flags //VARIABLE argument2 y-offset for component or point number; type depends on bits 0 and 1 in component flags //--------- //see more at https://fontforge.github.io/assets/old/Composites/index.html //--------- //move to composite glyph position reader.BaseStream.Seek(tableOffset + GlyphLocations.Offsets[compositeGlyphIndex], SeekOrigin.Begin); //reset //------------------------ short contoursCount = reader.ReadInt16(); // ignored Bounds bounds = Utils.ReadBounds(reader); TtfGlyph finalGlyph = null; CompositeGlyphFlags flags; do { flags = (CompositeGlyphFlags)reader.ReadUInt16(); ushort glyphIndex = reader.ReadUInt16(); if (createdGlyphs[glyphIndex] == null) { // This glyph is not read yet, resolve it first! long storedOffset = reader.BaseStream.Position; TtfGlyph missingGlyph = ReadCompositeGlyph(createdGlyphs, reader, tableOffset, glyphIndex); createdGlyphs[glyphIndex] = missingGlyph; reader.BaseStream.Position = storedOffset; } TtfGlyph newGlyph = TtfGlyph.Clone(createdGlyphs[glyphIndex]); short arg1 = 0; short arg2 = 0; ushort arg1and2 = 0; if (HasFlag(flags, CompositeGlyphFlags.ARG_1_AND_2_ARE_WORDS)) { arg1 = reader.ReadInt16(); arg2 = reader.ReadInt16(); } else { arg1and2 = reader.ReadUInt16(); } //----------------------------------------- float xscale = 1; float scale01 = 0; float scale10 = 0; float yscale = 1; bool useMatrix = false; //----------------------------------------- bool hasScale = false; if (HasFlag(flags, CompositeGlyphFlags.WE_HAVE_A_SCALE)) { //If the bit WE_HAVE_A_SCALE is set, //the scale value is read in 2.14 format-the value can be between -2 to almost +2. //The glyph will be scaled by this value before grid-fitting. xscale = yscale = ((float)reader.ReadInt16()) / (1 << 14); /* Format 2.14 */ hasScale = true; } else if (HasFlag(flags, CompositeGlyphFlags.WE_HAVE_AN_X_AND_Y_SCALE)) { xscale = ((float)reader.ReadInt16()) / (1 << 14); /* Format 2.14 */ yscale = ((float)reader.ReadInt16()) / (1 << 14); /* Format 2.14 */ hasScale = true; } else if (HasFlag(flags, CompositeGlyphFlags.WE_HAVE_A_TWO_BY_TWO)) { //The bit WE_HAVE_A_TWO_BY_TWO allows for linear transformation of the X and Y coordinates by specifying a 2 × 2 matrix. //This could be used for scaling and 90-degree*** rotations of the glyph components, for example. //2x2 matrix //The purpose of USE_MY_METRICS is to force the lsb and rsb to take on a desired value. //For example, an i-circumflex (U+00EF) is often composed of the circumflex and a dotless-i. //In order to force the composite to have the same metrics as the dotless-i, //set USE_MY_METRICS for the dotless-i component of the composite. //Without this bit, the rsb and lsb would be calculated from the hmtx entry for the composite //(or would need to be explicitly set with TrueType instructions). //Note that the behavior of the USE_MY_METRICS operation is undefined for rotated composite components. useMatrix = true; hasScale = true; xscale = ((float)reader.ReadInt16()) / (1 << 14); /* Format 2.14 */ scale01 = ((float)reader.ReadInt16()) / (1 << 14); /* Format 2.14 */ scale10 = ((float)reader.ReadInt16()) / (1 << 14); /* Format 2.14 */ yscale = ((float)reader.ReadInt16()) / (1 << 14); /* Format 2.14 */ if (HasFlag(flags, CompositeGlyphFlags.UNSCALED_COMPONENT_OFFSET)) { } else { } if (HasFlag(flags, CompositeGlyphFlags.USE_MY_METRICS)) { } } //-------------------------------------------------------------------- if (HasFlag(flags, CompositeGlyphFlags.ARGS_ARE_XY_VALUES)) { //Argument1 and argument2 can be either x and y offsets to be added to the glyph or two point numbers. //x and y offsets to be added to the glyph //When arguments 1 and 2 are an x and a y offset instead of points and the bit ROUND_XY_TO_GRID is set to 1, //the values are rounded to those of the closest grid lines before they are added to the glyph. //X and Y offsets are described in FUnits. if (useMatrix) { //use this matrix TtfGlyph.TransformNormalWith2x2Matrix(newGlyph, xscale, scale01, scale10, yscale); TtfGlyph.OffsetXY(newGlyph, (short)(arg1), arg2); } else { if (hasScale) { if (xscale == 1.0 && yscale == 1.0) { } else { TtfGlyph.TransformNormalWith2x2Matrix(newGlyph, xscale, 0, 0, yscale); } TtfGlyph.OffsetXY(newGlyph, arg1, arg2); } else { if (HasFlag(flags, CompositeGlyphFlags.ROUND_XY_TO_GRID)) { //TODO: implement round xy to grid*** //---------------------------- } //just offset*** TtfGlyph.OffsetXY(newGlyph, arg1, arg2); } } } else { //two point numbers. //the first point number indicates the point that is to be matched to the new glyph. //The second number indicates the new glyph's “matched” point. //Once a glyph is added,its point numbers begin directly after the last glyphs (endpoint of first glyph + 1) } // if (finalGlyph == null) { finalGlyph = newGlyph; } else { //merge TtfGlyph.AppendGlyph(finalGlyph, newGlyph); } } while (HasFlag(flags, CompositeGlyphFlags.MORE_COMPONENTS)); // if (HasFlag(flags, CompositeGlyphFlags.WE_HAVE_INSTRUCTIONS)) { ushort numInstr = reader.ReadUInt16(); byte[] insts = reader.ReadBytes(numInstr); finalGlyph.GlyphInstructions = insts; } //F2DOT14 16-bit signed fixed number with the low 14 bits of fraction (2.14). //Transformation Option // //The C pseudo-code fragment below shows how the composite glyph information is stored and parsed; definitions for “flags” bits follow this fragment: // do { // USHORT flags; // USHORT glyphIndex; // if ( flags & ARG_1_AND_2_ARE_WORDS) { // (SHORT or FWord) argument1; // (SHORT or FWord) argument2; // } else { // USHORT arg1and2; /* (arg1 << 8) | arg2 */ // } // if ( flags & WE_HAVE_A_SCALE ) { // F2Dot14 scale; /* Format 2.14 */ // } else if ( flags & WE_HAVE_AN_X_AND_Y_SCALE ) { // F2Dot14 xscale; /* Format 2.14 */ // F2Dot14 yscale; /* Format 2.14 */ // } else if ( flags & WE_HAVE_A_TWO_BY_TWO ) { // F2Dot14 xscale; /* Format 2.14 */ // F2Dot14 scale01; /* Format 2.14 */ // F2Dot14 scale10; /* Format 2.14 */ // F2Dot14 yscale; /* Format 2.14 */ // } //} while ( flags & MORE_COMPONENTS ) //if (flags & WE_HAVE_INSTR){ // USHORT numInstr // BYTE instr[numInstr] //------------------------------------------------------------ return(finalGlyph ?? TtfGlyph.Empty); }
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 //---------------------------------------------- }