/// <summary> /// calculate Baseline-to-Baseline Distance (BTBD) for macOS /// </summary> /// <param name="typeface"></param> /// <returns>return 'unscaled-to-pixel' BTBD value</returns> static int CalculateBTBD_Mac(Typeface typeface) { //from https://www.microsoft.com/typography/otspec/recom.htm#tad //Ascender and Descender are metrics defined by Apple //and are not to be confused with the Windows ascent or descent, //nor should they be confused with the true typographic ascender and descender that are found in AFM files. //The Macintosh metrics below are returned by the Apple Advanced Typography(AAT) GetFontInfo() API. // // //Macintosh Metric OpenType Metric //ascender Ascender //descender Descender //leading LineGap //The suggested BTBD = ascent + descent + leading //If pixels extend above the ascent or below the descent, //the character will be squashed in the vertical direction //so that all pixels fit within these limitations; this is true for screen display only. //TODO: please test this HorizontalHeader hhea = typeface.HheaTable; return(hhea.Ascent + hhea.Descent + hhea.LineGap); }
public Typeface Read(Stream stream) { var little = BitConverter.IsLittleEndian; using (BinaryReader input = new ByteOrderSwappingBinaryReader(stream)) { UInt32 version = input.ReadUInt32(); UInt16 tableCount = input.ReadUInt16(); UInt16 searchRange = input.ReadUInt16(); UInt16 entrySelector = input.ReadUInt16(); UInt16 rangeShift = input.ReadUInt16(); var tables = new List <TableEntry>(tableCount); for (int i = 0; i < tableCount; i++) { tables.Add(TableEntry.ReadFrom(input)); } var header = Head.From(FindTable(tables, "head")); var maximumProfile = MaxProfile.From(FindTable(tables, "maxp")); var glyphLocations = new GlyphLocations(FindTable(tables, "loca"), maximumProfile.GlyphCount, header.WideGlyphLocations); var glyphs = Glyf.From(FindTable(tables, "glyf"), glyphLocations); var cmaps = CmapReader.From(FindTable(tables, "cmap")); var horizontalHeader = HorizontalHeader.From(FindTable(tables, "hhea")); var horizontalMetrics = HorizontalMetrics.From(FindTable(tables, "hmtx"), horizontalHeader.HorizontalMetricsCount, maximumProfile.GlyphCount); return(new Typeface(header.Bounds, header.UnitsPerEm, glyphs, cmaps, horizontalMetrics)); } }
protected double GetLineHeight(FontUnitType units, OS2Table os2, HorizontalHeader hhead, FontHeader header, double emsize) { double h; bool useTypo; if (units == FontUnitType.UseFontPreference) { useTypo = (os2.Version >= OS2TableVersion.OpenType15) && ((os2.Selection & FontSelection.UseTypographicSizes) > 0); } else if (units == FontUnitType.UseHeadMetrics) { useTypo = false; } else if (null == os2) { useTypo = false; } else { useTypo = true; } if (useTypo) { h = ((double)(os2.TypoAscender - os2.TypoDescender + os2.TypoLineGap) / ((double)header.UnitsPerEm) * emsize); } else { h = ((double)(hhead.Ascender - hhead.Descender + hhead.LineGap) / ((double)header.UnitsPerEm) * emsize); } return(h); }
public static TTFStringMeasurer Create(TrueTypeFile forfont, CMapEncoding encoding, TypeMeasureOptions options) { HorizontalMetrics table = forfont.Directories["hmtx"].Table as HorizontalMetrics; CMAPTable cmap = forfont.Directories["cmap"].Table as CMAPTable; OS2Table os2 = forfont.Directories["OS/2"].Table as OS2Table; FontHeader head = forfont.Directories["head"].Table as FontHeader; HorizontalHeader hhead = forfont.Directories["hhea"].Table as HorizontalHeader; CMAPSubTable map = cmap.GetOffsetTable(encoding); if (map == null) { encoding = CMapEncoding.Unicode_20; map = cmap.GetOffsetTable(CMapEncoding.Unicode_20); } if (map == null) { encoding = CMapEncoding.MacRoman; map = cmap.GetOffsetTable(CMapEncoding.MacRoman); } return(new TTFStringMeasurer(head.UnitsPerEm, map, os2, hhead, table.HMetrics, forfont, encoding, options)); }
private TTFStringMeasurer(int unitsPerEm, CMAPSubTable offsets, OS2Table oS2, HorizontalHeader hheader, List <HMetric> metrics, TrueTypeFile font, CMapEncoding encoding, TypeMeasureOptions options) { this._unitsPerEm = unitsPerEm; this._offsets = offsets; this._os2 = oS2; this._metrics = metrics; this._lookup = new Dictionary <char, HMetric>(); this._options = options; this._fontUseTypo = (oS2.Version >= OS2TableVersion.OpenType15) && ((oS2.Selection & FontSelection.UseTypographicSizes) > 0); this._hheader = hheader; this._fontfile = font; this._cMapEncoding = encoding; }
/// <summary> /// calculate Baseline-to-Baseline Distance (BTBD) for Windows /// </summary> /// <param name="typeface"></param> /// <returns>return 'unscaled-to-pixel' BTBD value</returns> static int Calculate_BTBD_Windows(Typeface typeface) { //from https://www.microsoft.com/typography/otspec/recom.htm#tad //Baseline to Baseline Distances //The 'OS/2' table fields sTypoAscender, sTypoDescender, and sTypoLineGap //free applications from Macintosh-or Windows - specific metrics //which are constrained by backward compatibility requirements. // //The following discussion only pertains to the platform-specific metrics. //The suggested Baseline to Baseline Distance(BTBD) is computed differently for Windows and the Macintosh, //and it is based on different OpenType metrics. //However, if the recommendations below are followed, the BTBD will be the same for both Windows and the Mac. //Windows Metric OpenType Metric //ascent usWinAscent //descent usWinDescent //internal leading usWinAscent + usWinDescent - unitsPerEm //external leading MAX(0, LineGap - ((usWinAscent + usWinDescent) - (Ascender - Descender))) //The suggested BTBD = ascent + descent + external leading //It should be clear that the “external leading” can never be less than zero. //Pixels above the ascent or below the descent will be clipped from the character; //this is true for all output devices. //The usWinAscent and usWinDescent are values //from the 'OS/2' table. //The unitsPerEm value is from the 'head' table. //The LineGap, Ascender and Descender values are from the 'hhea' table. HorizontalHeader hhea = typeface.HheaTable; int usWinAscent = typeface.OS2Table.usWinAscent; int usWinDescent = typeface.OS2Table.usWinDescent; int internal_leading = usWinAscent + usWinDescent - typeface.UnitsPerEm; int external_leading = System.Math.Max(0, hhea.LineGap - ((usWinAscent + usWinDescent) - (hhea.Ascent - hhea.Descent))); return(usWinAscent + usWinDescent + external_leading); }
private TrueTypeFontTable ReadHorizontalHeader(uint length, TrueTypeTableEntryList list, BigEndianReader reader) { HorizontalHeader hhead = new HorizontalHeader(reader.Position); hhead.TableVersion = reader.ReadFixedVersion(); hhead.Ascender = reader.ReadInt16(); hhead.Descender = reader.ReadInt16(); hhead.LineGap = reader.ReadInt16(); hhead.AdvanceWidthMax = reader.ReadUInt16(); hhead.MinimumLeftSideBearing = reader.ReadInt16(); hhead.MinimumRightSideBearing = reader.ReadInt16(); hhead.XMaxExtent = reader.ReadInt16(); hhead.CaretSlopeRise = reader.ReadInt16(); hhead.CaretSlopeRun = reader.ReadInt16(); hhead.CaretOffset = reader.ReadInt16(); hhead.Reserved1 = reader.ReadInt16(); hhead.Reserved2 = reader.ReadInt16(); hhead.Reserved3 = reader.ReadInt16(); hhead.Reserved4 = reader.ReadInt16(); hhead.MetricDataFormat = reader.ReadInt16(); hhead.NumberOfHMetrics = reader.ReadUInt16(); return(hhead); }
internal Typeface ReadTableEntryCollection(TableEntryCollection tables, BinaryReader input) { OS2Table os2Table = ReadTableIfExists(tables, input, new OS2Table()); NameEntry nameEntry = ReadTableIfExists(tables, input, new NameEntry()); Head header = ReadTableIfExists(tables, input, new Head()); MaxProfile maximumProfile = ReadTableIfExists(tables, input, new MaxProfile()); HorizontalHeader horizontalHeader = ReadTableIfExists(tables, input, new HorizontalHeader()); HorizontalMetrics horizontalMetrics = ReadTableIfExists(tables, input, new HorizontalMetrics(horizontalHeader.HorizontalMetricsCount, maximumProfile.GlyphCount)); //--- PostTable postTable = ReadTableIfExists(tables, input, new PostTable()); CFFTable ccf = ReadTableIfExists(tables, input, new CFFTable()); //-------------- Cmap cmaps = ReadTableIfExists(tables, input, new Cmap()); GlyphLocations glyphLocations = ReadTableIfExists(tables, input, new GlyphLocations(maximumProfile.GlyphCount, header.WideGlyphLocations)); Glyf glyf = ReadTableIfExists(tables, input, new Glyf(glyphLocations)); //-------------- Gasp gaspTable = ReadTableIfExists(tables, input, new Gasp()); VerticalDeviceMetrics vdmx = ReadTableIfExists(tables, input, new VerticalDeviceMetrics()); //-------------- Kern kern = ReadTableIfExists(tables, input, new Kern()); //-------------- //advanced typography GDEF gdef = ReadTableIfExists(tables, input, new GDEF()); GSUB gsub = ReadTableIfExists(tables, input, new GSUB()); GPOS gpos = ReadTableIfExists(tables, input, new GPOS()); BASE baseTable = ReadTableIfExists(tables, input, new BASE()); COLR colr = ReadTableIfExists(tables, input, new COLR()); CPAL cpal = ReadTableIfExists(tables, input, new CPAL()); VerticalHeader vhea = ReadTableIfExists(tables, input, new VerticalHeader()); if (vhea != null) { VerticalMetrics vmtx = ReadTableIfExists(tables, input, new VerticalMetrics(vhea.NumOfLongVerMetrics)); } //test math table MathTable mathtable = ReadTableIfExists(tables, input, new MathTable()); EBLCTable fontBmpTable = ReadTableIfExists(tables, input, new EBLCTable()); //--------------------------------------------- //about truetype instruction init //--------------------------------------------- Typeface typeface = null; bool isPostScriptOutline = false; if (glyf == null) { //check if this is cff table ? if (ccf == null) { //TODO: review here throw new NotSupportedException(); } //... //PostScript outline font isPostScriptOutline = true; typeface = new Typeface( nameEntry, header.Bounds, header.UnitsPerEm, ccf, horizontalMetrics, os2Table); } else { typeface = new Typeface( nameEntry, header.Bounds, header.UnitsPerEm, glyf.Glyphs, horizontalMetrics, os2Table); } //---------------------------- typeface.CmapTable = cmaps; typeface.KernTable = kern; typeface.GaspTable = gaspTable; typeface.MaxProfile = maximumProfile; typeface.HheaTable = horizontalHeader; //---------------------------- if (!isPostScriptOutline) { FpgmTable fpgmTable = ReadTableIfExists(tables, input, new FpgmTable()); //control values table CvtTable cvtTable = ReadTableIfExists(tables, input, new CvtTable()); if (cvtTable != null) { typeface.ControlValues = cvtTable._controlValues; } if (fpgmTable != null) { typeface.FpgmProgramBuffer = fpgmTable._programBuffer; } PrepTable propProgramTable = ReadTableIfExists(tables, input, new PrepTable()); if (propProgramTable != null) { typeface.PrepProgramBuffer = propProgramTable._programBuffer; } } //------------------------- typeface.LoadOpenFontLayoutInfo( gdef, gsub, gpos, baseTable, colr, cpal); //------------ //test { SvgTable svgTable = ReadTableIfExists(tables, input, new SvgTable()); if (svgTable != null) { typeface._svgTable = svgTable; } } typeface.PostTable = postTable; if (mathtable != null) { var mathGlyphLoader = new MathGlyphLoader(); mathGlyphLoader.LoadMathGlyph(typeface, mathtable); } #if DEBUG //test //int found = typeface.GetGlyphIndexByName("Uacute"); if (typeface.IsCffFont) { //optional typeface.UpdateAllCffGlyphBounds(); } #endif return(typeface); }
public TtfTypeface Read(Stream stream, ReadFlags readFlags = ReadFlags.Full) { var little = BitConverter.IsLittleEndian; using (var input = new ByteOrderSwappingBinaryReader(stream)) { ushort majorVersion = input.ReadUInt16(); ushort minorVersion = input.ReadUInt16(); ushort tableCount = input.ReadUInt16(); ushort searchRange = input.ReadUInt16(); ushort entrySelector = input.ReadUInt16(); ushort rangeShift = input.ReadUInt16(); var tables = new TableEntryCollection(); for (int i = 0; i < tableCount; i++) { tables.AddEntry(new UnreadTableEntry(ReadTableHeader(input))); } //------------------------------------------------------------------ OS2Table os2Table = ReadTableIfExists(tables, input, new OS2Table()); NameEntry nameEntry = ReadTableIfExists(tables, input, new NameEntry()); Head header = ReadTableIfExists(tables, input, new Head()); MaxProfile maximumProfile = ReadTableIfExists(tables, input, new MaxProfile()); HorizontalHeader horizontalHeader = ReadTableIfExists(tables, input, new HorizontalHeader()); HorizontalMetrics horizontalMetrics = ReadTableIfExists(tables, input, new HorizontalMetrics(horizontalHeader.HorizontalMetricsCount, maximumProfile.GlyphCount)); //-------------- Cmap cmaps = ReadTableIfExists(tables, input, new Cmap()); GlyphLocations glyphLocations = ReadTableIfExists(tables, input, new GlyphLocations(maximumProfile.GlyphCount, header.WideGlyphLocations)); Glyf glyf = ReadTableIfExists(tables, input, new Glyf(glyphLocations)); //-------------- Gasp gaspTable = ReadTableIfExists(tables, input, new Gasp()); VerticalDeviceMetrics vdmx = ReadTableIfExists(tables, input, new VerticalDeviceMetrics()); //-------------- PostTable postTable = ReadTableIfExists(tables, input, new PostTable()); Kern kern = ReadTableIfExists(tables, input, new Kern()); //-------------- //advanced typography GDEF gdef = ReadTableIfExists(tables, input, new GDEF()); GSUB gsub = ReadTableIfExists(tables, input, new GSUB()); GPOS gpos = ReadTableIfExists(tables, input, new GPOS()); BASE baseTable = ReadTableIfExists(tables, input, new BASE()); COLR colr = ReadTableIfExists(tables, input, new COLR()); CPAL cpal = ReadTableIfExists(tables, input, new CPAL()); VerticalHeader vhea = ReadTableIfExists(tables, input, new VerticalHeader()); if (vhea != null) { VerticalMatric vmtx = ReadTableIfExists(tables, input, new VerticalMatric(vhea.NumOfLongVerMetrics)); } EBLCTable fontBmpTable = ReadTableIfExists(tables, input, new EBLCTable()); //--------------------------------------------- //about truetype instruction init //--------------------------------------------- var typeface = new TtfTypeface( nameEntry, header.Bounds, header.UnitsPerEm, glyf.Glyphs, horizontalMetrics, os2Table); //---------------------------- typeface.CmapTable = cmaps; typeface.KernTable = kern; typeface.GaspTable = gaspTable; typeface.MaxProfile = maximumProfile; typeface.HheaTable = horizontalHeader; //---------------------------- FpgmTable fpgmTable = ReadTableIfExists(tables, input, new FpgmTable()); //control values table CvtTable cvtTable = ReadTableIfExists(tables, input, new CvtTable()); if (cvtTable != null) { typeface.ControlValues = cvtTable.controlValues; } if (fpgmTable != null) { typeface.FpgmProgramBuffer = fpgmTable.programBuffer; } PrepTable propProgramTable = ReadTableIfExists(tables, input, new PrepTable()); if (propProgramTable != null) { typeface.PrepProgramBuffer = propProgramTable.programBuffer; } //------------------------- typeface.LoadOpenFontLayoutInfo( gdef, gsub, gpos, baseTable, colr, cpal); return(typeface); } }
//------------------------------------------------------------------------------------------xx.02.2005 /// <summary>Reads the font data from the file.</summary> /// <param name="openTypeReader">Open Type Reader</param> private void ReadFontDataFromFile(OpenTypeReader openTypeReader) { #region TrueType Collection if (bTrueTypeCollection) { String sTTCTag = openTypeReader.sReadTag(); if (sTTCTag != "ttcf") { throw new ReportException("'" + sFontName + " is not a valid TTC font file."); } UInt32 uVersion = openTypeReader.uReadULONG(); Int32 iNumFonts = (Int32)openTypeReader.uReadULONG(); if (iTrueTypeCollectionIndex >= iNumFonts) { throw new ReportException("'" + sFontName + " has invalid TrueType collection index."); } openTypeReader.Skip(iTrueTypeCollectionIndex * 4); Int32 iOffset = (Int32)openTypeReader.uReadULONG(); openTypeReader.Seek(iOffset); } #endregion #region Offset Table UInt32 uSfntVersion = openTypeReader.uReadULONG(); if (uSfntVersion == 0x4F54544F /* 'OTTO' */) { fontDataType = FontDataType.CFFdata; } #if DEBUG else if (uSfntVersion == 0x00010000 /* Version 1.0 */) { fontDataType = FontDataType.TrueTypeOutlines; } else { throw new ReportException("'" + sFontName + " is not a valid TTF, OTF or TTC font file."); } #endif iNumTables = (Int32)openTypeReader.iReadUSHORT(); iSearchRange = (Int32)openTypeReader.iReadUSHORT(); iEntrySelector = (Int32)openTypeReader.iReadUSHORT(); iRangeShift = (Int32)openTypeReader.iReadUSHORT(); #endregion #region Table Directory for (Int32 iTable = 0; iTable < iNumTables; iTable++) { TableDirectory tableDirectory_New = new TableDirectory(); tableDirectory_New.sTag = openTypeReader.sReadTag(); tableDirectory_New.uCheckSum = openTypeReader.uReadULONG(); tableDirectory_New.iOffset = (Int32)openTypeReader.uReadULONG(); tableDirectory_New.iLength = (Int32)openTypeReader.uReadULONG(); dict_TableDirectory.Add(tableDirectory_New.sTag, tableDirectory_New); } Boolean bCFF = dict_TableDirectory.ContainsKey("CFF"); if ((bCFF && fontDataType == FontDataType.TrueTypeOutlines) || (!bCFF && fontDataType == FontDataType.CFFdata)) { throw new ReportException("'" + sFontName + " is not a valid TTC font file."); } #endregion #region Font Header TableDirectory tableDirectory = dict_TableDirectory["head"]; fontHeader = new FontHeader(); openTypeReader.Seek(tableDirectory.iOffset); fontHeader.uTableVersionNumber = openTypeReader.uReadULONG(); Debug.Assert(fontHeader.uTableVersionNumber == 0x00010000 /* Version 1.0 */); openTypeReader.Skip(12); fontHeader.iFlags = openTypeReader.iReadUSHORT(); fontHeader.iUnitsPerEm = openTypeReader.iReadUSHORT(); openTypeReader.Skip(16); fontHeader.iXMin = openTypeReader.int16_ReadSHORT(); fontHeader.iYMin = openTypeReader.int16_ReadSHORT(); fontHeader.iXMax = openTypeReader.int16_ReadSHORT(); fontHeader.iYMax = openTypeReader.int16_ReadSHORT(); fontHeader.iMacStyle = openTypeReader.iReadUSHORT(); fontHeader.iLowestRecPPEM = openTypeReader.iReadUSHORT(); fontHeader.iFontDirectionHint = openTypeReader.int16_ReadSHORT(); fontHeader.iIndexToLocFormat = openTypeReader.int16_ReadSHORT(); fontHeader.iGlyphDataFormat = openTypeReader.int16_ReadSHORT(); #endregion #region Horizontal Header tableDirectory = dict_TableDirectory["hhea"]; horizontalHeader = new HorizontalHeader(); openTypeReader.Seek(tableDirectory.iOffset); horizontalHeader.uTableVersionNumber = openTypeReader.uReadULONG(); Debug.Assert(horizontalHeader.uTableVersionNumber == 0x00010000 /* Version 1.0 */); horizontalHeader.iAscender = openTypeReader.int16_ReadFWORD(); horizontalHeader.iDescender = openTypeReader.int16_ReadFWORD(); horizontalHeader.iLineGap = openTypeReader.int16_ReadFWORD(); horizontalHeader.iAdvanceWidthMax = openTypeReader.iReadUFWORD(); horizontalHeader.iMinLeftSideBearing = openTypeReader.int16_ReadFWORD(); horizontalHeader.iMinRightSideBearing = openTypeReader.int16_ReadFWORD(); horizontalHeader.iXMaxExtent = openTypeReader.int16_ReadFWORD(); horizontalHeader.iCaretSlopeRise = openTypeReader.int16_ReadSHORT(); horizontalHeader.iCaretSlopeRun = openTypeReader.int16_ReadSHORT(); horizontalHeader.iCaretOffset = openTypeReader.int16_ReadSHORT(); openTypeReader.Skip(10); horizontalHeader.iNumberOfHMetrics = openTypeReader.iReadUSHORT(); #endregion #region Windows Metrics (OS/2) tableDirectory = dict_TableDirectory["OS/2"]; winMetrics = new WinMetrics(); openTypeReader.Seek(tableDirectory.iOffset); winMetrics.iVersion = openTypeReader.iReadUSHORT(); winMetrics.iXAvgCharWidth = openTypeReader.int16_ReadSHORT(); winMetrics.iUsWeightClass = openTypeReader.iReadUSHORT(); winMetrics.iUsWidthClass = openTypeReader.iReadUSHORT(); winMetrics.iFsType = openTypeReader.iReadUSHORT(); winMetrics.iYSubscriptXSize = openTypeReader.int16_ReadSHORT(); winMetrics.iYSubscriptYSize = openTypeReader.int16_ReadSHORT(); winMetrics.iYSubscriptXOffset = openTypeReader.int16_ReadSHORT(); winMetrics.iYSubscriptYOffset = openTypeReader.int16_ReadSHORT(); winMetrics.iYSuperscriptXSize = openTypeReader.int16_ReadSHORT(); winMetrics.iYSuperscriptYSize = openTypeReader.int16_ReadSHORT(); winMetrics.iYSuperscriptXOffset = openTypeReader.int16_ReadSHORT(); winMetrics.iYSuperscriptYOffset = openTypeReader.int16_ReadSHORT(); winMetrics.iYStrikeoutSize = openTypeReader.int16_ReadSHORT(); winMetrics.iYStrikeoutPosition = openTypeReader.int16_ReadSHORT(); winMetrics.iSFamilyClass = openTypeReader.int16_ReadSHORT(); winMetrics.aByte_Panose = openTypeReader.aByte_ReadBYTE(10); if (winMetrics.iVersion == 0) { openTypeReader.Skip(4); // skip ulCharRange } else { winMetrics.uUlUnicodeRange1 = openTypeReader.uReadULONG(); winMetrics.uUlUnicodeRange2 = openTypeReader.uReadULONG(); winMetrics.uUlUnicodeRange3 = openTypeReader.uReadULONG(); winMetrics.uUlUnicodeRange4 = openTypeReader.uReadULONG(); } winMetrics.sAchVendID = openTypeReader.sReadTag(); winMetrics.iFsSelection = openTypeReader.iReadUSHORT(); winMetrics.iUsFirstCharIndex = openTypeReader.iReadUSHORT(); winMetrics.iUsLastCharIndex = openTypeReader.iReadUSHORT(); winMetrics.iSTypoAscender = openTypeReader.int16_ReadSHORT(); winMetrics.iSTypoDescender = openTypeReader.int16_ReadSHORT(); winMetrics.iSTypoLineGap = openTypeReader.int16_ReadSHORT(); winMetrics.iUsWinAscent = openTypeReader.iReadUSHORT(); winMetrics.iUsWinDescent = openTypeReader.iReadUSHORT(); if (winMetrics.iVersion > 0) { winMetrics.iUlCodePageRange1 = openTypeReader.uReadULONG(); winMetrics.iUlCodePageRange2 = openTypeReader.uReadULONG(); if (winMetrics.iVersion > 1) { winMetrics.iSXHeight = openTypeReader.int16_ReadSHORT(); winMetrics.iSCapHeight = openTypeReader.int16_ReadSHORT(); winMetrics.uUsDefaultChar = openTypeReader.uReadULONG(); winMetrics.uUsBreakChar = openTypeReader.uReadULONG(); winMetrics.uUsMaxContext = openTypeReader.uReadULONG(); } } #endregion #region Post tableDirectory = dict_TableDirectory["post"]; openTypeReader.Seek(tableDirectory.iOffset + 4); Double r1 = openTypeReader.int16_ReadSHORT(); Double r0 = openTypeReader.iReadUSHORT(); rItalicAngle = r1 + r0 / 16384.0; iUnderlinePosition = openTypeReader.int16_ReadFWORD(); iUnderlineThickness = openTypeReader.int16_ReadFWORD(); bFixedPitch = (openTypeReader.uReadULONG() != 0); #endregion ReadTable_name(openTypeReader); ReadTable_hmtx(openTypeReader); }
public LineSize MeasureString(CMapEncoding encoding, string s, int startOffset, double emsize, double available, bool wordboundary, out int charsfitted, FontUnitType useUnits = FontUnitType.UseFontPreference) { HorizontalMetrics table = this.Directories["hmtx"].Table as HorizontalMetrics; CMAPTable cmap = this.Directories["cmap"].Table as CMAPTable; OS2Table os2 = this.Directories["OS/2"].Table as OS2Table; CMAPSubTable mac = cmap.GetOffsetTable(encoding); if (mac == null) { mac = cmap.GetOffsetTable(CMapEncoding.MacRoman); } HorizontalHeader hhead = this.Directories["hhea"].Table as HorizontalHeader; FontHeader head = this.Directories["head"].Table as FontHeader; available = (available * head.UnitsPerEm) / emsize; double len = 0.0; double lastwordlen = 0.0; int lastwordcount = 0; charsfitted = 0; for (int i = startOffset; i < s.Length; i++) { char c = s[i]; if (char.IsWhiteSpace(c)) { lastwordlen = len; lastwordcount = charsfitted; } int moffset = (int)mac.GetCharacterGlyphOffset(c); //System.Diagnostics.Debug.WriteLine("Character '" + chars[i].ToString() + "' (" + ((byte)chars[i]).ToString() + ") has offset '" + moffset.ToString() + "' in mac encoding and '" + woffset + "' in windows encoding"); if (moffset >= table.HMetrics.Count) { moffset = table.HMetrics.Count - 1; } Scryber.OpenType.SubTables.HMetric metric; metric = table.HMetrics[moffset]; if (i == 0) { len = -metric.LeftSideBearing; } len += metric.AdvanceWidth; //check if we can fit more if (len > available) { len -= metric.AdvanceWidth; break; } charsfitted++; } bool isboundary = false; if ((charsfitted + startOffset < s.Length) && wordboundary && lastwordlen > 0) { len = lastwordlen; charsfitted = lastwordcount; isboundary = true; } len = len / (double)head.UnitsPerEm; len = len * emsize; double h = GetLineHeight(useUnits, os2, hhead, head, emsize); return(new LineSize((float)len, (float)h, charsfitted, startOffset, isboundary)); }
/// <summary> /// Measures the size of the provided string at the specified font size (starting at a specific offset), /// stopping when the available space is full and returning the number of characters fitted. /// </summary> /// <param name="encoding">The encoding to use to map the characters</param> /// <param name="s">The string to measure the size of</param> /// <param name="startOffset">The starting (zero based) offset in that string to start measuring from</param> /// <param name="emsize">The M size in font units</param> /// <param name="availablePts">The max width allowed for this string</param> /// <param name="wordspace">The spacing between words in font units. Default 0</param> /// <param name="charspace">The spacing between characters in font units. Default 0</param> /// <param name="hscale">The horizontal scaling of all characters. Default 100</param> /// <param name="vertical">If true then this is vertical writing</param> /// <param name="wordboundary">If True the measuring will stop at a boundary to a word rather than character.</param> /// <param name="charsfitted">Set to the number of characters that can be renered at this size within the width.</param> /// <returns></returns> public LineSize MeasureString(CMapEncoding encoding, string s, int startOffset, double emsize, double availablePts, double?wordspacePts, double charspacePts, double hscale, bool vertical, bool wordboundary, out int charsfitted, FontUnitType useUnits = FontUnitType.UseFontPreference) { HorizontalMetrics table = this.Directories["hmtx"].Table as HorizontalMetrics; CMAPTable cmap = this.Directories["cmap"].Table as CMAPTable; OS2Table os2 = this.Directories["OS/2"].Table as OS2Table; CMAPSubTable mac = cmap.GetOffsetTable(encoding); if (mac == null) { mac = cmap.GetOffsetTable(CMapEncoding.MacRoman); } HorizontalHeader hhead = this.Directories["hhea"].Table as HorizontalHeader; FontHeader head = this.Directories["head"].Table as FontHeader; double availableFU = availablePts * ((double)head.UnitsPerEm / emsize); double charspaceFU = NoCharacterSpace; if (charspacePts != NoCharacterSpace) { charspaceFU = charspacePts * ((double)head.UnitsPerEm / emsize); } double wordspaceFU = NoWordSpace; if (wordspacePts.HasValue) { wordspaceFU = (wordspacePts.Value * ((double)head.UnitsPerEm / emsize)); } else if (charspacePts != NoCharacterSpace) { //If we dont have explicit wordspacing then we use the character spacing wordspaceFU = charspaceFU; } double len = 0.0; double lastwordlen = 0.0; int lastwordcount = 0; charsfitted = 0; for (int i = startOffset; i < s.Length; i++) { char c = s[i]; if (char.IsWhiteSpace(c)) { lastwordlen = len; lastwordcount = charsfitted; } int moffset = (int)mac.GetCharacterGlyphOffset(c); if (moffset >= table.HMetrics.Count) { moffset = table.HMetrics.Count - 1; } Scryber.OpenType.SubTables.HMetric metric; metric = table.HMetrics[moffset]; double w = metric.AdvanceWidth; if (i == 0) { w -= metric.LeftSideBearing; } if (c == ' ') { if (wordspaceFU != NoWordSpace) { w += wordspaceFU; } } else if (charspaceFU != NoCharacterSpace) { w += charspaceFU; } if (hscale != NoHorizontalScale) { w *= hscale; } len += w; //check if we can fit more if (len > availableFU) { len -= w; break; } charsfitted++; } bool isboundary = false; if ((charsfitted + startOffset < s.Length) && wordboundary && lastwordlen > 0) { len = lastwordlen; charsfitted = lastwordcount; isboundary = true; } len = len * emsize; len = len / (double)head.UnitsPerEm; double h = GetLineHeight(useUnits, os2, hhead, head, emsize); return(new LineSize(len, h, charsfitted, startOffset, isboundary)); }
internal bool ReadTableEntryCollection(Typeface typeface, RestoreTicket ticket, TableEntryCollection tables, BinaryReader input) { if (ticket != null) { return(ReadTableEntryCollectionOnRestoreMode(typeface, ticket, tables, input)); } typeface.SetTableEntryCollection(tables.CloneTableHeaders()); var rd = new EntriesReaderHelper(tables, input); //PART 1: basic information OS2Table os2Table = rd.Read(new OS2Table()); Meta meta = rd.Read(new Meta()); NameEntry nameEntry = rd.Read(new NameEntry()); Head head = rd.Read(new Head()); MaxProfile maxProfile = rd.Read(new MaxProfile()); HorizontalHeader horizontalHeader = rd.Read(new HorizontalHeader()); HorizontalMetrics horizontalMetrics = rd.Read(new HorizontalMetrics(horizontalHeader.NumberOfHMetrics, maxProfile.GlyphCount)); VerticalHeader vhea = rd.Read(new VerticalHeader()); if (vhea != null) { VerticalMetrics vmtx = rd.Read(new VerticalMetrics(vhea.NumOfLongVerMetrics)); } Cmap cmaps = rd.Read(new Cmap()); VerticalDeviceMetrics vdmx = rd.Read(new VerticalDeviceMetrics()); Kern kern = rd.Read(new Kern()); //------------------------------------ //PART 2: glyphs detail //2.1 True type font GlyphLocations glyphLocations = rd.Read(new GlyphLocations(maxProfile.GlyphCount, head.WideGlyphLocations)); Glyf glyf = rd.Read(new Glyf(glyphLocations)); Gasp gaspTable = rd.Read(new Gasp()); COLR colr = rd.Read(new COLR()); CPAL cpal = rd.Read(new CPAL()); //2.2 Cff font PostTable postTable = rd.Read(new PostTable()); CFFTable cff = rd.Read(new CFFTable()); //additional math table (if available) MathTable mathtable = rd.Read(new MathTable()); //------------------------------------ //PART 3: advanced typography GDEF gdef = rd.Read(new GDEF()); GSUB gsub = rd.Read(new GSUB()); GPOS gpos = rd.Read(new GPOS()); BASE baseTable = rd.Read(new BASE()); JSTF jstf = rd.Read(new JSTF()); STAT stat = rd.Read(new STAT()); if (stat != null) { //variable font FVar fvar = rd.Read(new FVar()); if (fvar != null) { GVar gvar = rd.Read(new GVar()); CVar cvar = rd.Read(new CVar()); HVar hvar = rd.Read(new HVar()); MVar mvar = rd.Read(new MVar()); AVar avar = rd.Read(new AVar()); } } bool isPostScriptOutline = false; bool isBitmapFont = false; typeface.SetBasicTypefaceTables(os2Table, nameEntry, head, horizontalMetrics); if (glyf == null) { //check if this is cff table ? if (cff == null) { //check cbdt/cblc ? CBLC cblcTable = rd.Read(new CBLC()); if (cblcTable != null) { CBDT cbdtTable = rd.Read(new CBDT()); //read cbdt //bitmap font BitmapFontGlyphSource bmpFontGlyphSrc = new BitmapFontGlyphSource(cblcTable); bmpFontGlyphSrc.LoadCBDT(cbdtTable); Glyph[] glyphs = bmpFontGlyphSrc.BuildGlyphList(); typeface.SetBitmapGlyphs(glyphs, bmpFontGlyphSrc); isBitmapFont = true; } else { //TODO: EBLC fontBmpTable = rd.Read(new EBLC()); throw new NotSupportedException(); } } else { isPostScriptOutline = true; typeface.SetCffFontSet(cff.Cff1FontSet); } } else { typeface.SetTtfGlyphs(glyf.Glyphs); } //---------------------------- typeface.CmapTable = cmaps; typeface.KernTable = kern; typeface.MaxProfile = maxProfile; typeface.HheaTable = horizontalHeader; //---------------------------- typeface.GaspTable = gaspTable; if (!isPostScriptOutline && !isBitmapFont) { //for true-type font outline FpgmTable fpgmTable = rd.Read(new FpgmTable()); //control values table CvtTable cvtTable = rd.Read(new CvtTable()); PrepTable propProgramTable = rd.Read(new PrepTable()); typeface.ControlValues = cvtTable?._controlValues; typeface.FpgmProgramBuffer = fpgmTable?._programBuffer; typeface.PrepProgramBuffer = propProgramTable?._programBuffer; } //------------------------- typeface.LoadOpenFontLayoutInfo( gdef, gsub, gpos, baseTable, colr, cpal); //------------ typeface.SetSvgTable(rd.Read(new SvgTable())); typeface.PostTable = postTable; if (mathtable != null) { MathGlyphLoader.LoadMathGlyph(typeface, mathtable); } #if DEBUG //test //int found = typeface.GetGlyphIndexByName("Uacute"); if (typeface.IsCffFont) { //optional typeface.UpdateAllCffGlyphBounds(); } #endif typeface.UpdateLangs(meta); typeface.UpdateFrequentlyUsedValues(); return(true); }