public FontMetric(XDocument fontXDoc) { m_fontName = fontXDoc.Descendants("fontname").FirstOrDefault().Value; m_fullName = fontXDoc.Descendants("fullname").FirstOrDefault().Value; m_family = fontXDoc.Descendants("fontfamily").FirstOrDefault().Value; m_weight = fontXDoc.Descendants("weight").FirstOrDefault().Value; m_boundingBox[0] = fontXDoc.Descendants("fontbbox").Where(x => x.Name == "xmin").Select(x => double.Parse(x.Value)).FirstOrDefault(); m_boundingBox[1] = fontXDoc.Descendants("fontbbox").Where(x => x.Name == "ymin").Select(x => double.Parse(x.Value)).FirstOrDefault(); m_boundingBox[2] = fontXDoc.Descendants("fontbbox").Where(x => x.Name == "xmax").Select(x => double.Parse(x.Value)).FirstOrDefault(); m_boundingBox[3] = fontXDoc.Descendants("fontbbox").Where(x => x.Name == "ymax").Select(x => double.Parse(x.Value)).FirstOrDefault(); m_capHeight = (double)fontXDoc.Descendants("capheight").FirstOrDefault().Value.DoubleOrNull(); m_xHeight = (double)fontXDoc.Descendants("xheight").FirstOrDefault().Value.DoubleOrNull(); m_ascender = (double)fontXDoc.Descendants("ascender").FirstOrDefault().Value.DoubleOrNull(); m_descender = (double)fontXDoc.Descendants("descender").FirstOrDefault().Value.DoubleOrNull(); m_ruleWidth = fontXDoc.Descendants("rulewidth").FirstOrDefault().Value.DoubleOrNull(); m_vGap = double.Parse(fontXDoc.Descendants("vgap").FirstOrDefault().Value); // m_stdHw = (double) fontXDoc.Descendants("stdhw").FirstOrDefault().Value.DoubleOrNull(); m_stdVw = (double)fontXDoc.Descendants("stdvw").FirstOrDefault().Value.DoubleOrNull(); m_undelinePosition = (double)fontXDoc.Descendants("underlineposition").FirstOrDefault().Value.DoubleOrNull(); m_underlineThickness = (double)fontXDoc.Descendants("underlinethickness").FirstOrDefault().Value.DoubleOrNull(); m_italicAngle = (double)fontXDoc.Descendants("italicangle").FirstOrDefault().Value.DoubleOrNull(); m_charWidth = (double)fontXDoc.Descendants("charwidth").FirstOrDefault().Value.DoubleOrNull(); m_axisPosition = (double)fontXDoc.Descendants("axisposition").FirstOrDefault().Value.DoubleOrNull(); CharData = fontXDoc.Descendants("char") .Select(x => new KeyValuePair <int, CharMetric>( int.Parse(x.Element("charid").Value.Substring(2), NumberStyles.HexNumber), new CharMetric( charName: x.Element("name").Value, codes: x.Descendants("charid").Select(z => (uint)int.Parse(z.Value.Substring(2), NumberStyles.HexNumber)).ToList(), width: double.Parse(x.Element("w").Value), bbox: new double[] { double.Parse(x.Element("b").Element("xmin").Value), double.Parse(x.Element("b").Element("ymin").Value), double.Parse(x.Element("b").Element("xmax").Value), double.Parse(x.Element("b").Element("ymax").Value) }))).ToDictionary(kvp => kvp.Key, kvp => kvp.Value); XElement mg = fontXDoc.Descendants("missingglyph").FirstOrDefault(); m_missingGlyph = new CharMetric( charName: mg.Element("name").Value, width: double.Parse(mg.Element("w").Value), codes: mg.Descendants("charid").Select(x => (uint)int.Parse(x.Value)).ToList(), bbox: new double[] { double.Parse(mg.Element("b").Element("xmin").Value), double.Parse(mg.Element("b").Element("ymin").Value), double.Parse(mg.Element("b").Element("xmax").Value), double.Parse(mg.Element("b").Element("ymax").Value) }); }
//To Do: Implement. (/fonts/metric) public FontMetric(XDocument fontXDoc) { FontName = fontXDoc.Descendants("fontname").FirstOrDefault().Value; FullName = fontXDoc.Descendants("fullname").FirstOrDefault().Value; Family = fontXDoc.Descendants("fontfamily").FirstOrDefault().Value; Weight = fontXDoc.Descendants("weight").FirstOrDefault().Value; BBox[0] = fontXDoc.Descendants("fontbbox").Where(x=>x.Name=="xmin").Select(x=> double.Parse(x.Value)).FirstOrDefault(); BBox[1] = fontXDoc.Descendants("fontbbox").Where(x => x.Name == "ymin").Select(x => double.Parse(x.Value)).FirstOrDefault(); BBox[2] = fontXDoc.Descendants("fontbbox").Where(x => x.Name == "xmax").Select(x => double.Parse(x.Value)).FirstOrDefault(); BBox[3] = fontXDoc.Descendants("fontbbox").Where(x => x.Name == "ymax").Select(x => double.Parse(x.Value)).FirstOrDefault(); CapHeight = DoubleOrNull(fontXDoc.Descendants("capheight").FirstOrDefault().Value); XHeight = DoubleOrNull(fontXDoc.Descendants("xheight").FirstOrDefault().Value); Ascender= DoubleOrNull(fontXDoc.Descendants("ascender").FirstOrDefault().Value); Descender = DoubleOrNull(fontXDoc.Descendants("descender").FirstOrDefault().Value); RuleWidth = DoubleOrNull(fontXDoc.Descendants("rulewidth").FirstOrDefault().Value); VGap = double.Parse(fontXDoc.Descendants("vgap").FirstOrDefault().Value); StdHw = DoubleOrNull(fontXDoc.Descendants("stdhw").FirstOrDefault().Value); StdVw = DoubleOrNull(fontXDoc.Descendants("stdvw").FirstOrDefault().Value); UndelinePosition = DoubleOrNull(fontXDoc.Descendants("underlineposition").FirstOrDefault().Value); UnderlineThickness = DoubleOrNull(fontXDoc.Descendants("underlinethickness").FirstOrDefault().Value); ItalicAngle = DoubleOrNull(fontXDoc.Descendants("italicangle").FirstOrDefault().Value); CharWidth = DoubleOrNull(fontXDoc.Descendants("charwidth").FirstOrDefault().Value); AxisPosition = DoubleOrNull(fontXDoc.Descendants("axisposition").FirstOrDefault().Value); CharData = fontXDoc.Descendants("char").Select(x => new KeyValuePair<int, CharMetric>(int.Parse(x.Element("charid").Value.Substring(2), NumberStyles.HexNumber), new CharMetric() { GlyphName = x.Element("name").Value, Width = double.Parse(x.Element("w").Value), Codes = x.Descendants("charid").Select(z => int.Parse(z.Value.Substring(2), NumberStyles.HexNumber)).ToList(), BBox = new double[] { double.Parse(x.Element("b").Element("xmin").Value), double.Parse(x.Element("b").Element("ymin").Value), double.Parse(x.Element("b").Element("xmax").Value), double.Parse(x.Element("b").Element("ymax").Value) } })).ToDictionary(kvp => kvp.Key, kvp=> kvp.Value); XElement mg = fontXDoc.Descendants("missingglyph").FirstOrDefault(); MissingGlyph = new CharMetric() { GlyphName = mg.Element("name").Value, Width = double.Parse(mg.Element("w").Value), Codes = mg.Descendants("charid").Select(x => int.Parse(x.Value)).ToList(), BBox = new double[] { double.Parse(mg.Element("b").Element("xmin").Value), double.Parse(mg.Element("b").Element("ymin").Value), double.Parse(mg.Element("b").Element("xmax").Value), double.Parse(mg.Element("b").Element("ymax").Value) } }; }
public object[] FindChar(int ch) { foreach (FontMetricRecord fd in FontPool()) { if (fd.Metric.CharData.ContainsKey(ch)) { CharMetric cm = fd.Metric.CharData[ch]; return(new object[] { cm, fd }); } else { if (0 < ch && ch < 0xFFFF && MathDefaults.SpecialChars.ContainsKey(ch)) { return(FindChar(MathDefaults.SpecialChars[ch])); //ToDo: Verify correct working } return(null); // new object[] { null, null }; } } return(null);// new object[] { null, null }; //ToDo: verify correct working }
private void ReadFontMetrics() { SkipTo(0); // TTF version m_reader.ReadUInt32(); // Number of tables int numTables = numTables = m_reader.ReadUInt16(); numTables = BitConverter.ToInt16(BitConverter.GetBytes(numTables), 1); // searchRange m_reader.ReadUInt16(); // entrySelector m_reader.ReadUInt16(); // rangeShift m_reader.ReadUInt16(); m_fontType = c_ttfAbbreviation; for (int i = 0; i < numTables; i++) { string tag = Encoding.UTF8.GetString(BitConverter.GetBytes(m_reader.ReadUInt32())); uint checksum = ReadUInt32Bytes(); uint offset = ReadUInt32Bytes(); uint length = ReadUInt32Bytes(); m_tables.Add(tag, new Tuple <uint, uint>(offset, length)); } SwitchTables("head"); SkipTo(m_currentOffset + 12); uint magic = ReadUInt32Bytes(); if (magic != 0x5F0F3CF5) { throw new Exception("Magic number in 'head' table does not match the spec"); } Skip(2); m_unitsPerEm = ReadUInt16Bytes(); m_eMScale = 1.0 / Convert.ToDouble(m_unitsPerEm); Skip(16); double xMin = ReadInt16Bytes() * m_eMScale; double yMin = ReadInt16Bytes() * m_eMScale; double xMax = ReadInt16Bytes() * m_eMScale; double yMax = ReadInt16Bytes() * m_eMScale; m_boundingBox = new double[] { xMin, yMin, xMax, yMax }; Skip(6); m_indexToLocFormat = ReadInt16Bytes(); SwitchTables("maxp"); SkipTo(m_currentOffset); Skip(4); m_numGlyphs = ReadUInt16Bytes(); SwitchTables("name"); SkipTo(m_currentOffset); Skip(2); uint numRecords = ReadUInt16Bytes(); m_storageOffset = ReadUInt16Bytes() + m_currentOffset; for (int i = 0; i < numRecords; i++) { uint platformId = ReadUInt16Bytes(); uint encodingId = ReadUInt16Bytes(); uint languageId = ReadUInt16Bytes(); uint nameId = ReadUInt16Bytes(); uint nameLength = ReadUInt16Bytes(); uint nameOffset = ReadUInt16Bytes(); if (platformId == 3 && encodingId == 1) { if (englishCodes.Contains(languageId) || !m_uniNames.ContainsKey(nameId)) { m_uniNames[nameId] = new Tuple <uint, uint>(nameOffset, nameLength); } } else if (platformId == 1 && encodingId == 0) { if (languageId == 0 || !m_macNames.ContainsKey(nameId)) { m_macNames[nameId] = new Tuple <uint, uint>(nameOffset, nameLength); } } } m_family = GetName(1); m_fullName = GetName(4); m_fontName = GetName(6); SwitchTables("OS/2"); SkipTo(m_currentOffset); uint tableVersion = ReadUInt16Bytes(); int os2_xAvgCharWidth = ReadInt16Bytes(); if (os2_xAvgCharWidth > 0) { m_charWidth = os2_xAvgCharWidth * EmScale; } uint wght = ReadUInt16Bytes(); if (wght < 150) { m_weight = "Thin"; } else if (wght < 250) { m_weight = "Extra-Light"; } else if (wght < 350) { m_weight = "Light"; } else if (wght < 450) { m_weight = "Regular"; } else if (wght < 550) { m_weight = "Medium"; } else if (wght < 650) { m_weight = "Demi-Bold"; } else if (wght < 750) { m_weight = "Bold"; } else if (wght < 850) { m_weight = "Extra-Bold"; } else { m_weight = "Black"; } Skip(62); m_ascender = ReadInt16Bytes() * EmScale; m_descender = ReadInt16Bytes() * EmScale; if (tableVersion == 2) { Skip(14); int xh = ReadInt16Bytes(); if (xh > 0) { m_xHeight = xh * EmScale; } int ch = ReadInt16Bytes(); if (ch > 0) { m_capHeight = ch * EmScale; } } SwitchTables("post"); SkipTo(m_currentOffset); Skip(4); m_italicAngle = Convert.ToDouble(ReadFixed32()); m_undelinePosition = ReadInt16Bytes() * EmScale; m_underlineThickness = ReadInt16Bytes() * EmScale; SwitchTables("hhea"); SkipTo(m_currentOffset); Skip(34); uint numHmtx = ReadUInt16Bytes(); SwitchTables("hmtx"); SkipTo(m_currentOffset); List <CharMetric> glyphArray = new List <CharMetric>(); double w = 0; for (int i = 0; i < m_numGlyphs; i++) { if (i < numHmtx) { w = ReadUInt16Bytes() * EmScale; Skip(2); } glyphArray.Add(new CharMetric(w)); } SwitchTables("cmap"); SkipTo(m_currentOffset); Skip(2); Dictionary <Tuple <uint, uint>, uint> cmapEncodings = new Dictionary <Tuple <uint, uint>, uint>(); uint numSubTables = ReadUInt16Bytes(); for (int i = 0; i < numSubTables; i++) { uint platformId = ReadUInt16Bytes(); uint encodingId = ReadUInt16Bytes(); uint subTableoffset = ReadUInt32Bytes(); cmapEncodings.Add(new Tuple <uint, uint>(platformId, encodingId), subTableoffset); } string encodingScheme = "Unicode"; uint subtableOffset; Tuple <uint, uint> preferedSubTable = new Tuple <uint, uint>(3, 1); Tuple <uint, uint> symbolSubTable = new Tuple <uint, uint>(3, 0); if (!cmapEncodings.ContainsKey(preferedSubTable)) { if (!cmapEncodings.ContainsKey(symbolSubTable)) { throw new NotSupportedException(string.Format("Cannot use font {0}: no known subtable in 'cmap' table", m_fontName)); } encodingScheme = "Symbol"; subtableOffset = cmapEncodings[symbolSubTable]; } else { subtableOffset = cmapEncodings[preferedSubTable]; } SkipTo(m_currentOffset + subtableOffset); uint subTableFormat = ReadUInt16Bytes(); if (subTableFormat != 4) { throw new NotSupportedException(string.Format("Unsupported format in 'cmap' table: {0}", subtableOffset)); } uint subtableLength = ReadUInt16Bytes(); Skip(2); uint segCount = ReadUInt16Bytes() / 2; Skip(6); List <uint> endCounts = Enumerable.Range(0, (int)segCount).Select(x => ReadUInt16Bytes()).ToList(); Skip(2); List <uint> startCounts = Enumerable.Range(0, (int)segCount).Select(x => ReadUInt16Bytes()).ToList(); List <int> idDeltas = Enumerable.Range(0, (int)segCount).Select(x => ReadInt16Bytes()).ToList(); List <uint> rangeOffsets = Enumerable.Range(0, (int)segCount).Select(x => ReadUInt16Bytes()).ToList(); uint remainingLength = subtableLength - (8 * segCount) - 16; if (remainingLength <= 0) { remainingLength += 0x10000; } List <uint> glyphIdArray = Enumerable.Range(0, (int)remainingLength / 2).Select(x => ReadUInt16Bytes()).ToList(); for (int i = 0; i < segCount; i++) { for (uint c = startCounts[i]; c < endCounts[i] + 1; c++) { if (c == 0xFFFF) { continue; } uint gid; if (rangeOffsets[i] > 0) { uint idx = (c - startCounts[i]) + (rangeOffsets[i] / 2) - (segCount - (uint)i); gid = glyphIdArray[(int)idx]; } else { gid = c + ((uint)idDeltas[i]); } if (gid >= 0x10000) { gid -= 0x10000; } else if (gid < 0) { gid += 0x10000; } CharMetric cm = glyphArray[(int)gid]; cm.Codes.Add(c); if (encodingScheme == "Symbol" && (0xF020 <= c && c <= 0xF07F)) { cm.Codes.Add(c - 0xF000); } if (string.IsNullOrEmpty(cm.GlyphName)) { cm.GlyphName = string.Format("u{0:X4}", c); } } } SwitchTables("loca"); SkipTo(m_currentOffset); List <uint> glyphIndex = new List <uint>(); int scalefactor = m_indexToLocFormat + 1; if (m_indexToLocFormat == 0) { glyphIndex.AddRange(Enumerable.Range(0, (int)m_numGlyphs + 1).Select(x => ReadUInt16Bytes() * 2)); } else if (m_indexToLocFormat == 1) { glyphIndex.AddRange(Enumerable.Range(0, (int)m_numGlyphs + 1).Select(x => ReadUInt32Bytes())); } else { throw new InvalidDataException(string.Format("Invalid indexToLocFormat value ({0}) in 'head' table", m_indexToLocFormat)); } SwitchTables("glyf"); for (int i = 0; i < m_numGlyphs; i++) { CharMetric cm = glyphArray[i]; if (glyphIndex[i] == glyphIndex[i + 1]) { cm.SetBBox(0, 0, 0, 0); // empty glyph } else { SkipTo(m_currentOffset + glyphIndex[i] + 2); double xMinc = ReadInt16Bytes() * EmScale; double yMinc = ReadInt16Bytes() * EmScale; double xMaxc = ReadInt16Bytes() * EmScale; double yMaxc = ReadInt16Bytes() * EmScale; cm.SetBBox(xMinc, yMinc, xMaxc, yMaxc); } cm.Codes.ForEach(x => { CharMetric ccm = new CharMetric( charName: cm.GlyphName, codes: cm.Codes.Select(y => (uint)y).ToList(), width: cm.Width, bbox: cm.BBox); CharData.Add((int)x, ccm); }); } }
public void MeasureText() { if (Text.Length == 0) { IsSpace = true; return; } CharMetric cm0 = null; CharMetric cm1 = null; CharMetric cm = null; int[] ucstext = GetUCSText(); foreach (int chcode in ucstext) { object[] chardesc = FindChar(chcode); if (chardesc == null) { Width += Metric().MissingGlyph.Width; } else { cm = (CharMetric)chardesc[0]; FontMetricRecord fd = (FontMetricRecord)chardesc[1]; fd.Used = true; if (chcode == ucstext.First()) //ToDo: Verify correct translation { cm0 = cm; } if (chcode == ucstext.Last()) //ToDo: Verify correct translation { cm1 = cm; } Width += cm.Width; if (Height + Depth == 0) { Height = cm.BBox[3]; Depth = -(cm.BBox[1]); } else if (cm.BBox[3] != cm.BBox[1]) { Height = Math.Max(Height, cm.BBox[3]); Depth = Math.Max(Depth, -(cm.BBox[1])); } } } // Normalize to the font size Width *= FontSize; Depth *= FontSize; Height *= FontSize; // Add ascender/ descender values Ascender = NominalAscender(); Descender = NominalDescender(); // Shape correction if (cm0 != null) { LeftBearing = Math.Max(0, -cm0.BBox[0]) * FontSize; } if (cm1 != null) { RightBearing = Math.Max(0, cm1.BBox[2] - cm.Width) * FontSize; } Width += LeftBearing + RightBearing; //Reset nominal metric NominalMetric = null; }