Example #1
0
        /**
         * This will read the required data from the stream.
         *
         * @param ttf The font that is being read.
         * @param data The stream to read the data from.
         * @ If there is an error reading the data.
         */
        public override void Read(TrueTypeFont ttf, TTFDataStream data)
        {
            HorizontalHeaderTable hHeader = ttf.HorizontalHeader;

            if (hHeader == null)
            {
                throw new IOException("Could not get hhea table");
            }
            numHMetrics = hHeader.NumberOfHMetrics;
            int numGlyphs = ttf.NumberOfGlyphs;

            int bytesRead = 0;

            advanceWidth    = new int[numHMetrics];
            leftSideBearing = new short[numHMetrics];
            for (int i = 0; i < numHMetrics; i++)
            {
                advanceWidth[i]    = data.ReadUnsignedShort();
                leftSideBearing[i] = data.ReadSignedShort();
                bytesRead         += 4;
            }

            int numberNonHorizontal = numGlyphs - numHMetrics;

            // handle bad fonts with too many hmetrics
            if (numberNonHorizontal < 0)
            {
                numberNonHorizontal = numGlyphs;
            }

            // make sure that table is never null and correct size, even with bad fonts that have no
            // "leftSideBearing" table although they should
            nonHorizontalLeftSideBearing = new short[numberNonHorizontal];

            if (bytesRead < Length)
            {
                for (int i = 0; i < numberNonHorizontal; i++)
                {
                    if (bytesRead < Length)
                    {
                        nonHorizontalLeftSideBearing[i] = data.ReadSignedShort();
                        bytesRead += 2;
                    }
                }
            }

            initialized = true;
        }
Example #2
0
        private byte[] buildHheaTable()
        {
            using (var bos = new MemoryStream())
                using (var output = new BinaryWriter(bos))
                {
                    HorizontalHeaderTable h = ttf.HorizontalHeader;
                    WriteFixed(output, h.Version);
                    WriteSInt16(output, h.Ascender);
                    WriteSInt16(output, h.Descender);
                    WriteSInt16(output, h.LineGap);
                    WriteUint16(output, h.AdvanceWidthMax);
                    WriteSInt16(output, h.MinLeftSideBearing);
                    WriteSInt16(output, h.MinRightSideBearing);
                    WriteSInt16(output, h.XMaxExtent);
                    WriteSInt16(output, h.CaretSlopeRise);
                    WriteSInt16(output, h.CaretSlopeRun);
                    WriteSInt16(output, h.Reserved1); // caretOffset
                    WriteSInt16(output, h.Reserved2);
                    WriteSInt16(output, h.Reserved3);
                    WriteSInt16(output, h.Reserved4);
                    WriteSInt16(output, h.Reserved5);
                    WriteSInt16(output, h.MetricDataFormat);

                    // input there a GID >= numberOfHMetrics ? Then keep the last entry of original hmtx table,
                    // (add if it isn't in our set of GIDs), see also in buildHmtxTable()
                    int hmetrics = glyphIds.GetViewBetween(0, h.NumberOfHMetrics).Count();
                    if (glyphIds.LastOrDefault() >= h.NumberOfHMetrics && !glyphIds.Contains(h.NumberOfHMetrics - 1))
                    {
                        ++hmetrics;
                    }
                    WriteUint16(output, hmetrics);

                    output.Flush();
                    return(bos.ToArray());
                }
        }
Example #3
0
        private byte[] BuildHmtxTable()
        {
            MemoryStream bos = new MemoryStream();

            HorizontalHeaderTable  h  = ttf.HorizontalHeader;
            HorizontalMetricsTable hm = ttf.HorizontalMetrics;

            Bytes.Buffer input = ttf.OriginalData;

            // more info: https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6hmtx.html
            int lastgid = h.NumberOfHMetrics - 1;
            // true if lastgid input not in the set: we'll need its width (but not its left side bearing) later
            bool needLastGidWidth = false;

            if (glyphIds.LastOrDefault() > lastgid && !glyphIds.Contains(lastgid))
            {
                needLastGidWidth = true;
            }

            try
            {
                long isResult = input.Skip(hm.Offset);

                if (isResult.CompareTo(hm.Offset) != 0)
                {
                    Debug.WriteLine($"debug: Tried skipping {hm.Offset} bytes but only {isResult} bytes skipped");
                }

                long lastOffset = 0;
                foreach (int glyphId in glyphIds)
                {
                    // offset in original file
                    long offset;
                    if (glyphId <= lastgid)
                    {
                        // copy width and lsb
                        offset     = glyphId * 4L;
                        lastOffset = CopyBytes(input, bos, offset, lastOffset, 4);
                    }
                    else
                    {
                        if (needLastGidWidth)
                        {
                            // one time only: copy width from lastgid, whose width applies
                            // to all later glyphs
                            needLastGidWidth = false;
                            offset           = lastgid * 4L;
                            lastOffset       = CopyBytes(input, bos, offset, lastOffset, 2);

                            // then go on with lsb from actual glyph (lsb are individual even in monotype fonts)
                        }

                        // copy lsb only, as we are beyond numOfHMetrics
                        offset     = h.NumberOfHMetrics * 4L + (glyphId - h.NumberOfHMetrics) * 2L;
                        lastOffset = CopyBytes(input, bos, offset, lastOffset, 2);
                    }
                }

                return(bos.ToArray());
            }
            finally
            {
                input.Dispose();
            }
        }
Example #4
0
        private TTFTable ReadTableDirectory(TrueTypeFont font, TTFDataStream raf)
        {
            TTFTable table;
            string   tag = raf.ReadString(4);

            switch (tag)
            {
            case CmapTable.TAG:
                table = new CmapTable(font);
                break;

            case GlyphTable.TAG:
                table = new GlyphTable(font);
                break;

            case HeaderTable.TAG:
                table = new HeaderTable(font);
                break;

            case HorizontalHeaderTable.TAG:
                table = new HorizontalHeaderTable(font);
                break;

            case HorizontalMetricsTable.TAG:
                table = new HorizontalMetricsTable(font);
                break;

            case IndexToLocationTable.TAG:
                table = new IndexToLocationTable(font);
                break;

            case MaximumProfileTable.TAG:
                table = new MaximumProfileTable(font);
                break;

            case NamingTable.TAG:
                table = new NamingTable(font);
                break;

            case OS2WindowsMetricsTable.TAG:
                table = new OS2WindowsMetricsTable(font);
                break;

            case PostScriptTable.TAG:
                table = new PostScriptTable(font);
                break;

            case DigitalSignatureTable.TAG:
                table = new DigitalSignatureTable(font);
                break;

            case KerningTable.TAG:
                table = new KerningTable(font);
                break;

            case VerticalHeaderTable.TAG:
                table = new VerticalHeaderTable(font);
                break;

            case VerticalMetricsTable.TAG:
                table = new VerticalMetricsTable(font);
                break;

            case VerticalOriginTable.TAG:
                table = new VerticalOriginTable(font);
                break;

            case GlyphSubstitutionTable.TAG:
                table = new GlyphSubstitutionTable(font);
                break;

            default:
                table = ReadTable(font, tag);
                break;
            }
            table.Tag      = tag;
            table.CheckSum = raf.ReadUnsignedInt();
            table.Offset   = raf.ReadUnsignedInt();
            table.Length   = raf.ReadUnsignedInt();

            // skip tables with zero length (except glyf)
            if (table.Length == 0 && !tag.Equals(GlyphTable.TAG, StringComparison.Ordinal))
            {
                return(null);
            }

            return(table);
        }
Example #5
0
        /**
         * Parse all tables and check if all needed tables are present.
         *
         * @param font the TrueTypeFont instance holding the parsed data.
         * @ If there is an error parsing the TrueType font.
         */
        private void ParseTables(TrueTypeFont font)
        {
            foreach (TTFTable table in font.Tables)
            {
                if (!table.Initialized)
                {
                    font.ReadTable(table);
                }
            }

            bool isPostScript = AllowCFF && font.TableMap.ContainsKey(CFFTable.TAG);

            HeaderTable head = font.Header;

            if (head == null)
            {
                throw new IOException("head is mandatory");
            }

            HorizontalHeaderTable hh = font.HorizontalHeader;

            if (hh == null)
            {
                throw new IOException("hhead is mandatory");
            }

            MaximumProfileTable maxp = font.MaximumProfile;

            if (maxp == null)
            {
                throw new IOException("maxp is mandatory");
            }

            PostScriptTable post = font.PostScript;

            if (post == null && !isEmbedded)
            {
                // in an embedded font this table is optional
                throw new IOException("post is mandatory");
            }

            if (!isPostScript)
            {
                IndexToLocationTable loc = font.IndexToLocation;
                if (loc == null)
                {
                    throw new IOException("loca is mandatory");
                }

                if (font.Glyph == null)
                {
                    throw new IOException("glyf is mandatory");
                }
            }

            if (font.Naming == null && !isEmbedded)
            {
                throw new IOException("name is mandatory");
            }

            if (font.HorizontalMetrics == null)
            {
                throw new IOException("hmtx is mandatory");
            }

            if (!isEmbedded && font.Cmap == null)
            {
                throw new IOException("cmap is mandatory");
            }
        }