예제 #1
0
        public static void ReadPost(DataReader reader, TableRecord[] tables, ref FaceHeader header)
        {
            if (!SeekToTable(reader, tables, FourCC.Post))
            {
                return;
            }

            // skip over version and italicAngle
            reader.Skip(sizeof(int) * 2);

            header.UnderlinePosition  = reader.ReadInt16BE();
            header.UnderlineThickness = reader.ReadInt16BE();
            header.IsFixedPitch       = reader.ReadUInt32BE() != 0;
        }
예제 #2
0
        public static MetricsHeader ReadMetricsHeader(DataReader reader)
        {
            // skip over version
            reader.Skip(sizeof(int));

            var header = new MetricsHeader {
                Ascender  = reader.ReadInt16BE(),
                Descender = reader.ReadInt16BE(),
                LineGap   = reader.ReadInt16BE()
            };

            // skip over advanceWidthMax, minLsb, minRsb, xMaxExtent, caretSlopeRise,
            // caretSlopeRun, caretOffset, 4 reserved entries, and metricDataFormat
            reader.Skip(sizeof(short) * 12);

            header.MetricCount = reader.ReadUInt16BE();
            return(header);
        }
예제 #3
0
        public static OS2Data ReadOS2(DataReader reader, TableRecord[] tables)
        {
            SeekToTable(reader, tables, FourCC.OS_2, required: true);

            // skip over version, xAvgCharWidth
            reader.Skip(sizeof(short) * 2);

            var result = new OS2Data
            {
                Weight  = (FontWeight)reader.ReadUInt16BE(),
                Stretch = (FontStretch)reader.ReadUInt16BE()
            };

            // skip over fsType, ySubscriptXSize, ySubscriptYSize, ySubscriptXOffset, ySubscriptYOffset,
            // ySuperscriptXSize, ySuperscriptYSize, ySuperscriptXOffset, ySuperscriptXOffset
            reader.Skip(sizeof(short) * 9);

            result.StrikeoutSize     = reader.ReadInt16BE();
            result.StrikeoutPosition = reader.ReadInt16BE();

            // skip over sFamilyClass, panose[10], ulUnicodeRange1-4, achVendID[4]
            reader.Skip(sizeof(short) + sizeof(int) * 4 + 14);

            // check various style flags
            var fsSelection = (FsSelectionFlags)reader.ReadUInt16BE();

            result.Style = (fsSelection & FsSelectionFlags.Italic) != 0 ? FontStyle.Italic :
                           (fsSelection & FsSelectionFlags.Bold) != 0 ? FontStyle.Bold :
                           (fsSelection & FsSelectionFlags.Oblique) != 0 ? FontStyle.Oblique :
                           FontStyle.Regular;
            result.IsWWSFont             = (fsSelection & FsSelectionFlags.WWS) != 0;
            result.UseTypographicMetrics = (fsSelection & FsSelectionFlags.UseTypoMetrics) != 0;

            // skip over usFirstCharIndex, usLastCharIndex
            reader.Skip(sizeof(short) * 2);

            result.TypographicAscender  = reader.ReadInt16BE();
            result.TypographicDescender = reader.ReadInt16BE();
            result.TypographicLineGap   = reader.ReadInt16BE();
            result.WinAscent            = reader.ReadUInt16BE();
            result.WinDescent           = reader.ReadUInt16BE();

            // skip over ulCodePageRange1-2
            reader.Skip(sizeof(int) * 2);

            result.XHeight   = reader.ReadInt16BE();
            result.CapHeight = reader.ReadInt16BE();

            return(result);
        }
예제 #4
0
        public static FUnit[] ReadCvt(DataReader reader, TableRecord[] tables)
        {
            var index = FindTable(tables, FourCC.Cvt);

            if (index == -1)
            {
                return(null);
            }

            reader.Seek(tables[index].Offset);

            var results = new FUnit[tables[index].Length / sizeof(short)];

            for (int i = 0; i < results.Length; i++)
            {
                results[i] = (FUnit)reader.ReadInt16BE();
            }

            return(results);
        }
예제 #5
0
        public static void ReadHead(DataReader reader, TableRecord[] tables, out FaceHeader header)
        {
            SeekToTable(reader, tables, FourCC.Head, required: true);

            // 'head' table contains global information for the font face
            // we only care about a few fields in it
            reader.Skip(sizeof(int) * 4);   // version, revision, checksum, magic number

            header = new FaceHeader
            {
                Flags = (HeadFlags)reader.ReadUInt16BE(),
                UnitsPerEm = reader.ReadUInt16BE()
            };
            if (header.UnitsPerEm == 0)
                throw new InvalidFontException("Invalid 'head' table.");

            // skip over created and modified times, bounding box,
            // deprecated style bits, direction hints, and size hints
            reader.Skip(sizeof(long) * 2 + sizeof(short) * 7);

            header.IndexFormat = (IndexFormat)reader.ReadInt16BE();
        }
예제 #6
0
        public static void ReadHead(DataReader reader, TableRecord[] tables, out FaceHeader header)
        {
            SeekToTable(reader, tables, FourCC.Head, required: true);

            // 'head' table contains global information for the font face
            // we only care about a few fields in it
            reader.Skip(sizeof(int) * 4);   // version, revision, checksum, magic number

            header = new FaceHeader {
                Flags      = (HeadFlags)reader.ReadUInt16BE(),
                UnitsPerEm = reader.ReadUInt16BE()
            };
            if (header.UnitsPerEm == 0)
            {
                throw new InvalidFontException("Invalid 'head' table.");
            }

            // skip over created and modified times, bounding box,
            // deprecated style bits, direction hints, and size hints
            reader.Skip(sizeof(long) * 2 + sizeof(short) * 7);

            header.IndexFormat = (IndexFormat)reader.ReadInt16BE();
        }
예제 #7
0
        static CompositeGlyph ReadCompositeGlyph(DataReader reader)
        {
            // we need to keep reading glyphs for as long as
            // our flags tell us that there are more to read
            var subglyphs = new List <Subglyph>();

            CompositeGlyphFlags flags;

            do
            {
                flags = (CompositeGlyphFlags)reader.ReadUInt16BE();

                var subglyph = new Subglyph {
                    Flags = flags
                };
                subglyph.Index = reader.ReadUInt16BE();

                // read in args; they vary in size based on flags
                if ((flags & CompositeGlyphFlags.ArgsAreWords) != 0)
                {
                    subglyph.Arg1 = reader.ReadInt16BE();
                    subglyph.Arg2 = reader.ReadInt16BE();
                }
                else
                {
                    subglyph.Arg1 = reader.ReadSByte();
                    subglyph.Arg2 = reader.ReadSByte();
                }

                // figure out the transform; we can either have no scale, a uniform
                // scale, two independent scales, or a full 2x2 transform matrix
                // transform components are in 2.14 fixed point format
                var transform = Matrix3x2.Identity;
                if ((flags & CompositeGlyphFlags.HaveScale) != 0)
                {
                    var scale = reader.ReadInt16BE() / F2Dot14ToFloat;
                    transform.M11 = scale;
                    transform.M22 = scale;
                }
                else if ((flags & CompositeGlyphFlags.HaveXYScale) != 0)
                {
                    transform.M11 = reader.ReadInt16BE() / F2Dot14ToFloat;
                    transform.M22 = reader.ReadInt16BE() / F2Dot14ToFloat;
                }
                else if ((flags & CompositeGlyphFlags.HaveTransform) != 0)
                {
                    transform.M11 = reader.ReadInt16BE() / F2Dot14ToFloat;
                    transform.M12 = reader.ReadInt16BE() / F2Dot14ToFloat;
                    transform.M21 = reader.ReadInt16BE() / F2Dot14ToFloat;
                    transform.M22 = reader.ReadInt16BE() / F2Dot14ToFloat;
                }

                subglyph.Transform = transform;
                subglyphs.Add(subglyph);
            } while ((flags & CompositeGlyphFlags.MoreComponents) != 0);

            var result = new CompositeGlyph {
                Subglyphs = subglyphs.ToArray()
            };

            // if we have instructions, read them now
            if ((flags & CompositeGlyphFlags.HaveInstructions) != 0)
            {
                var instructionLength = reader.ReadUInt16BE();
                result.Instructions = reader.ReadBytes(instructionLength);
            }

            return(result);
        }
예제 #8
0
        public static KerningTable ReadKern(DataReader reader, TableRecord[] tables)
        {
            // kern table is optional
            if (!SfntTables.SeekToTable(reader, tables, FourCC.Kern))
            {
                return(null);
            }

            // skip version
            reader.Skip(sizeof(short));

            // read each subtable and accumulate kerning values
            var tableData     = new Dictionary <uint, int>();
            var subtableCount = reader.ReadUInt16BE();

            for (int i = 0; i < subtableCount; i++)
            {
                // skip version
                var currentOffset = reader.Position;
                reader.Skip(sizeof(short));

                var length   = reader.ReadUInt16BE();
                var coverage = reader.ReadUInt16BE();

                // we (and Windows) only support Format 0 tables
                // only care about tables with horizontal kerning data
                var kc = (KernCoverage)coverage;
                if ((coverage & FormatMask) == 0 && (kc & KernCoverage.Horizontal) != 0 && (kc & KernCoverage.CrossStream) == 0)
                {
                    // read the number of entries; skip over the rest of the header
                    var entryCount = reader.ReadUInt16BE();
                    reader.Skip(sizeof(short) * 3);

                    var isMin      = (kc & KernCoverage.Minimum) != 0;
                    var isOverride = (kc & KernCoverage.Override) != 0;

                    // read in each entry and accumulate its kerning data
                    for (int j = 0; j < entryCount; j++)
                    {
                        var left  = reader.ReadUInt16BE();
                        var right = reader.ReadUInt16BE();
                        var value = reader.ReadInt16BE();

                        // look up the current value, if we have one; if not, start at zero
                        int current = 0;
                        var key     = ((uint)left << 16) | right;
                        tableData.TryGetValue(key, out current);

                        if (isMin)
                        {
                            if (current < value)
                            {
                                tableData[key] = value;
                            }
                        }
                        else if (isOverride)
                        {
                            tableData[key] = value;
                        }
                        else
                        {
                            tableData[key] = current + value;
                        }
                    }
                }

                // jump to the next subtable
                reader.Seek(currentOffset + length);
            }

            return(new KerningTable(tableData));
        }
예제 #9
0
        public static void ReadGlyph(
            DataReader reader, int glyphIndex, int recursionDepth,
            BaseGlyph[] glyphTable, uint glyfOffset, uint glyfLength, uint *loca
            )
        {
            // check if this glyph has already been loaded; this can happen
            // if we're recursively loading subglyphs as part of a composite
            if (glyphTable[glyphIndex] != null)
            {
                return;
            }

            // prevent bad font data from causing infinite recursion
            if (recursionDepth > MaxRecursion)
            {
                throw new InvalidFontException("Bad font data; infinite composite recursion.");
            }

            // check if this glyph doesn't have any actual data
            GlyphHeader header;
            var         offset = loca[glyphIndex];

            if ((glyphIndex < glyphTable.Length - 1 && offset == loca[glyphIndex + 1]) || offset >= glyfLength)
            {
                // this is an empty glyph, so synthesize a header
                header = default(GlyphHeader);
            }
            else
            {
                // seek to the right spot and load the header
                reader.Seek(glyfOffset + loca[glyphIndex]);
                header = new GlyphHeader {
                    ContourCount = reader.ReadInt16BE(),
                    MinX         = reader.ReadInt16BE(),
                    MinY         = reader.ReadInt16BE(),
                    MaxX         = reader.ReadInt16BE(),
                    MaxY         = reader.ReadInt16BE()
                };

                if (header.ContourCount < -1 || header.ContourCount > MaxContours)
                {
                    throw new InvalidFontException("Invalid number of contours for glyph.");
                }
            }

            if (header.ContourCount > 0)
            {
                // positive contours means a simple glyph
                glyphTable[glyphIndex] = ReadSimpleGlyph(reader, header.ContourCount);
            }
            else if (header.ContourCount == -1)
            {
                // -1 means composite glyph
                var composite = ReadCompositeGlyph(reader);
                var subglyphs = composite.Subglyphs;

                // read each subglyph recrusively
                for (int i = 0; i < subglyphs.Length; i++)
                {
                    ReadGlyph(reader, subglyphs[i].Index, recursionDepth + 1, glyphTable, glyfOffset, glyfLength, loca);
                }

                glyphTable[glyphIndex] = composite;
            }
            else
            {
                // no data, so synthesize an empty glyph
                glyphTable[glyphIndex] = new SimpleGlyph {
                    Points           = new Point[0],
                    ContourEndpoints = new int[0]
                };
            }

            // save bounding box
            var glyph = glyphTable[glyphIndex];

            glyph.MinX = header.MinX;
            glyph.MinY = header.MinY;
            glyph.MaxX = header.MaxX;
            glyph.MaxY = header.MaxY;
        }
예제 #10
0
        static SimpleGlyph ReadSimpleGlyph(DataReader reader, int contourCount)
        {
            // read contour endpoints
            var contours     = new int[contourCount];
            var lastEndpoint = reader.ReadUInt16BE();

            contours[0] = lastEndpoint;
            for (int i = 1; i < contours.Length; i++)
            {
                var endpoint = reader.ReadUInt16BE();
                contours[i] = endpoint;
                if (contours[i] <= lastEndpoint)
                {
                    throw new InvalidFontException("Glyph contour endpoints are unordered.");
                }

                lastEndpoint = endpoint;
            }

            // the last contour's endpoint is the number of points in the glyph
            var pointCount = lastEndpoint + 1;
            var points     = new Point[pointCount];

            // read instruction data
            var instructionLength = reader.ReadUInt16BE();
            var instructions      = reader.ReadBytes(instructionLength);

            // read flags
            var flags     = new SimpleGlyphFlags[pointCount];
            int flagIndex = 0;

            while (flagIndex < flags.Length)
            {
                var f = (SimpleGlyphFlags)reader.ReadByte();
                flags[flagIndex++] = f;

                // if Repeat is set, this flag data is repeated n more times
                if ((f & SimpleGlyphFlags.Repeat) != 0)
                {
                    var count = reader.ReadByte();
                    for (int i = 0; i < count; i++)
                    {
                        flags[flagIndex++] = f;
                    }
                }
            }

            // Read points, first doing all X coordinates and then all Y coordinates.
            // The point packing is insane; coords are either 1 byte or 2; they're
            // deltas from previous point, and flags let you repeat identical points.
            var x = 0;

            for (int i = 0; i < points.Length; i++)
            {
                var f     = flags[i];
                var delta = 0;

                if ((f & SimpleGlyphFlags.ShortX) != 0)
                {
                    delta = reader.ReadByte();
                    if ((f & SimpleGlyphFlags.SameX) == 0)
                    {
                        delta = -delta;
                    }
                }
                else if ((f & SimpleGlyphFlags.SameX) == 0)
                {
                    delta = reader.ReadInt16BE();
                }

                x          += delta;
                points[i].X = (FUnit)x;
            }

            var y = 0;

            for (int i = 0; i < points.Length; i++)
            {
                var f     = flags[i];
                var delta = 0;

                if ((f & SimpleGlyphFlags.ShortY) != 0)
                {
                    delta = reader.ReadByte();
                    if ((f & SimpleGlyphFlags.SameY) == 0)
                    {
                        delta = -delta;
                    }
                }
                else if ((f & SimpleGlyphFlags.SameY) == 0)
                {
                    delta = reader.ReadInt16BE();
                }

                y             += delta;
                points[i].Y    = (FUnit)y;
                points[i].Type = (f & SimpleGlyphFlags.OnCurve) != 0 ? PointType.OnCurve : PointType.Quadratic;
            }

            return(new SimpleGlyph {
                Points = points,
                ContourEndpoints = contours,
                Instructions = instructions
            });
        }
예제 #11
0
        public static OS2Data ReadOS2(DataReader reader, TableRecord[] tables)
        {
            SeekToTable(reader, tables, FourCC.OS_2, required: true);

            // skip over version, xAvgCharWidth
            reader.Skip(sizeof(short) * 2);

            var result = new OS2Data
            {
                Weight = (FontWeight)reader.ReadUInt16BE(),
                Stretch = (FontStretch)reader.ReadUInt16BE()
            };

            // skip over fsType, ySubscriptXSize, ySubscriptYSize, ySubscriptXOffset, ySubscriptYOffset,
            // ySuperscriptXSize, ySuperscriptYSize, ySuperscriptXOffset, ySuperscriptXOffset
            reader.Skip(sizeof(short) * 9);

            result.StrikeoutSize = reader.ReadInt16BE();
            result.StrikeoutPosition = reader.ReadInt16BE();

            // skip over sFamilyClass, panose[10], ulUnicodeRange1-4, achVendID[4]
            reader.Skip(sizeof(short) + sizeof(int) * 4 + 14);

            // check various style flags
            var fsSelection = (FsSelectionFlags)reader.ReadUInt16BE();
            result.Style = (fsSelection & FsSelectionFlags.Italic) != 0 ? FontStyle.Italic :
                            (fsSelection & FsSelectionFlags.Bold) != 0 ? FontStyle.Bold :
                            (fsSelection & FsSelectionFlags.Oblique) != 0 ? FontStyle.Oblique :
                            FontStyle.Regular;
            result.IsWWSFont = (fsSelection & FsSelectionFlags.WWS) != 0;
            result.UseTypographicMetrics = (fsSelection & FsSelectionFlags.UseTypoMetrics) != 0;

            // skip over usFirstCharIndex, usLastCharIndex
            reader.Skip(sizeof(short) * 2);

            result.TypographicAscender = reader.ReadInt16BE();
            result.TypographicDescender = reader.ReadInt16BE();
            result.TypographicLineGap = reader.ReadInt16BE();
            result.WinAscent = reader.ReadUInt16BE();
            result.WinDescent = reader.ReadUInt16BE();

            // skip over ulCodePageRange1-2
            reader.Skip(sizeof(int) * 2);

            result.XHeight = reader.ReadInt16BE();
            result.CapHeight = reader.ReadInt16BE();

            return result;
        }
예제 #12
0
        public static MetricsEntry[] ReadMetricsTable(DataReader reader, int glyphCount, int metricCount)
        {
            var results = new MetricsEntry[glyphCount];
            for (int i = 0; i < metricCount; i++)
            {
                results[i] = new MetricsEntry
                {
                    Advance = reader.ReadUInt16BE(),
                    FrontSideBearing = reader.ReadInt16BE()
                };
            }

            // there might be an additional array of fsb-only entries
            var extraCount = glyphCount - metricCount;
            var lastAdvance = results[metricCount - 1].Advance;
            for (int i = 0; i < extraCount; i++)
            {
                results[i + metricCount] = new MetricsEntry
                {
                    Advance = lastAdvance,
                    FrontSideBearing = reader.ReadInt16BE()
                };
            }

            return results;
        }
예제 #13
0
        unsafe static CharacterMap ReadCmapFormat4(DataReader reader)
        {
            // skip over length and language
            reader.Skip(sizeof(short) * 2);

            // figure out how many segments we have
            var segmentCount = reader.ReadUInt16BE() / 2;

            if (segmentCount > MaxSegments)
            {
                throw new Exception("Too many cmap segments.");
            }

            // skip over searchRange, entrySelector, and rangeShift
            reader.Skip(sizeof(short) * 3);

            // read in segment ranges
            var endCount = stackalloc int[segmentCount];

            for (int i = 0; i < segmentCount; i++)
            {
                endCount[i] = reader.ReadUInt16BE();
            }

            reader.Skip(sizeof(short));     // padding

            var startCount = stackalloc int[segmentCount];

            for (int i = 0; i < segmentCount; i++)
            {
                startCount[i] = reader.ReadUInt16BE();
            }

            var idDelta = stackalloc int[segmentCount];

            for (int i = 0; i < segmentCount; i++)
            {
                idDelta[i] = reader.ReadInt16BE();
            }

            // build table from each segment
            var table = new Dictionary <CodePoint, int>();

            for (int i = 0; i < segmentCount; i++)
            {
                // read the "idRangeOffset" for the current segment
                // if nonzero, we need to jump into the glyphIdArray to figure out the mapping
                // the layout is bizarre; see the OpenType spec for details
                var idRangeOffset = reader.ReadUInt16BE();
                if (idRangeOffset != 0)
                {
                    var currentOffset = reader.Position;
                    reader.Seek(currentOffset + idRangeOffset - sizeof(ushort));

                    var end   = endCount[i];
                    var delta = idDelta[i];
                    for (var codepoint = startCount[i]; codepoint <= end; codepoint++)
                    {
                        var glyphId = reader.ReadUInt16BE();
                        if (glyphId != 0)
                        {
                            var glyphIndex = (glyphId + delta) & 0xFFFF;
                            if (glyphIndex != 0)
                            {
                                table.Add((CodePoint)codepoint, glyphIndex);
                            }
                        }
                    }

                    reader.Seek(currentOffset);
                }
                else
                {
                    // otherwise, do a straight iteration through the segment
                    var end   = endCount[i];
                    var delta = idDelta[i];
                    for (var codepoint = startCount[i]; codepoint <= end; codepoint++)
                    {
                        var glyphIndex = (codepoint + delta) & 0xFFFF;
                        if (glyphIndex != 0)
                        {
                            table.Add((CodePoint)codepoint, glyphIndex);
                        }
                    }
                }
            }

            return(new CharacterMap(table));
        }
예제 #14
0
        public static KerningTable ReadKern(DataReader reader, TableRecord[] tables)
        {
            // kern table is optional
            if (!SfntTables.SeekToTable(reader, tables, FourCC.Kern))
                return null;

            // skip version
            reader.Skip(sizeof(short));

            // read each subtable and accumulate kerning values
            var tableData = new Dictionary<uint, int>();
            var subtableCount = reader.ReadUInt16BE();
            for (int i = 0; i < subtableCount; i++)
            {
                // skip version
                var currentOffset = reader.Position;
                reader.Skip(sizeof(short));

                var length = reader.ReadUInt16BE();
                var coverage = reader.ReadUInt16BE();

                // we (and Windows) only support Format 0 tables
                // only care about tables with horizontal kerning data
                var kc = (KernCoverage)coverage;
                if ((coverage & FormatMask) == 0 && (kc & KernCoverage.Horizontal) != 0 && (kc & KernCoverage.CrossStream) == 0)
                {
                    // read the number of entries; skip over the rest of the header
                    var entryCount = reader.ReadUInt16BE();
                    reader.Skip(sizeof(short) * 3);

                    var isMin = (kc & KernCoverage.Minimum) != 0;
                    var isOverride = (kc & KernCoverage.Override) != 0;

                    // read in each entry and accumulate its kerning data
                    for (int j = 0; j < entryCount; j++)
                    {
                        var left = reader.ReadUInt16BE();
                        var right = reader.ReadUInt16BE();
                        var value = reader.ReadInt16BE();

                        // look up the current value, if we have one; if not, start at zero
                        int current = 0;
                        var key = ((uint)left << 16) | right;
                        tableData.TryGetValue(key, out current);

                        if (isMin)
                        {
                            if (current < value)
                                tableData[key] = value;
                        }
                        else if (isOverride)
                            tableData[key] = value;
                        else
                            tableData[key] = current + value;
                    }
                }

                // jump to the next subtable
                reader.Seek(currentOffset + length);
            }

            return new KerningTable(tableData);
        }
예제 #15
0
        public static void ReadPost(DataReader reader, TableRecord[] tables, ref FaceHeader header)
        {
            if (!SeekToTable(reader, tables, FourCC.Post))
                return;

            // skip over version and italicAngle
            reader.Skip(sizeof(int) * 2);

            header.UnderlinePosition = reader.ReadInt16BE();
            header.UnderlineThickness = reader.ReadInt16BE();
            header.IsFixedPitch = reader.ReadUInt32BE() != 0;
        }
예제 #16
0
        static unsafe CharacterMap ReadCmapFormat4(DataReader reader)
        {
            // skip over length and language
            reader.Skip(sizeof(short) * 2);

            // figure out how many segments we have
            var segmentCount = reader.ReadUInt16BE() / 2;
            if (segmentCount > MaxSegments)
                throw new Exception("Too many cmap segments.");

            // skip over searchRange, entrySelector, and rangeShift
            reader.Skip(sizeof(short) * 3);

            // read in segment ranges
            var endCount = stackalloc int[segmentCount];
            for (int i = 0; i < segmentCount; i++)
                endCount[i] = reader.ReadUInt16BE();

            reader.Skip(sizeof(short));     // padding

            var startCount = stackalloc int[segmentCount];
            for (int i = 0; i < segmentCount; i++)
                startCount[i] = reader.ReadUInt16BE();

            var idDelta = stackalloc int[segmentCount];
            for (int i = 0; i < segmentCount; i++)
                idDelta[i] = reader.ReadInt16BE();

            // build table from each segment
            var table = new Dictionary<CodePoint, int>();
            for (int i = 0; i < segmentCount; i++)
            {
                // read the "idRangeOffset" for the current segment
                // if nonzero, we need to jump into the glyphIdArray to figure out the mapping
                // the layout is bizarre; see the OpenType spec for details
                var idRangeOffset = reader.ReadUInt16BE();
                if (idRangeOffset != 0)
                {
                    var currentOffset = reader.Position;
                    reader.Seek(currentOffset + idRangeOffset - sizeof(ushort));

                    var end = endCount[i];
                    var delta = idDelta[i];
                    for (var codepoint = startCount[i]; codepoint <= end; codepoint++)
                    {
                        var glyphId = reader.ReadUInt16BE();
                        if (glyphId != 0)
                        {
                            var glyphIndex = (glyphId + delta) & 0xFFFF;
                            if (glyphIndex != 0)
                                table.Add((CodePoint)codepoint, glyphIndex);
                        }
                    }

                    reader.Seek(currentOffset);
                }
                else
                {
                    // otherwise, do a straight iteration through the segment
                    var end = endCount[i];
                    var delta = idDelta[i];
                    for (var codepoint = startCount[i]; codepoint <= end; codepoint++)
                    {
                        var glyphIndex = (codepoint + delta) & 0xFFFF;
                        if (glyphIndex != 0)
                            table.Add((CodePoint)codepoint, glyphIndex);
                    }
                }
            }

            return new CharacterMap(table);
        }
예제 #17
0
        public static MetricsHeader ReadMetricsHeader(DataReader reader)
        {
            // skip over version
            reader.Skip(sizeof(int));

            var header = new MetricsHeader
            {
                Ascender = reader.ReadInt16BE(),
                Descender = reader.ReadInt16BE(),
                LineGap = reader.ReadInt16BE()
            };

            // skip over advanceWidthMax, minLsb, minRsb, xMaxExtent, caretSlopeRise,
            // caretSlopeRun, caretOffset, 4 reserved entries, and metricDataFormat
            reader.Skip(sizeof(short) * 12);

            header.MetricCount = reader.ReadUInt16BE();
            return header;
        }
예제 #18
0
        public static FUnit[] ReadCvt(DataReader reader, TableRecord[] tables)
        {
            var index = FindTable(tables, FourCC.Cvt);
            if (index == -1)
                return null;

            reader.Seek(tables[index].Offset);

            var results = new FUnit[tables[index].Length / sizeof(short)];
            for (int i = 0; i < results.Length; i++)
                results[i] = (FUnit)reader.ReadInt16BE();

            return results;
        }
예제 #19
0
        static CompositeGlyph ReadCompositeGlyph(DataReader reader)
        {
            // we need to keep reading glyphs for as long as
            // our flags tell us that there are more to read
            var subglyphs = new List<Subglyph>();

            CompositeGlyphFlags flags;
            do
            {
                flags = (CompositeGlyphFlags)reader.ReadUInt16BE();

                var subglyph = new Subglyph { Flags = flags };
                subglyph.Index = reader.ReadUInt16BE();

                // read in args; they vary in size based on flags
                if ((flags & CompositeGlyphFlags.ArgsAreWords) != 0)
                {
                    subglyph.Arg1 = reader.ReadInt16BE();
                    subglyph.Arg2 = reader.ReadInt16BE();
                }
                else
                {
                    subglyph.Arg1 = reader.ReadSByte();
                    subglyph.Arg2 = reader.ReadSByte();
                }

                // figure out the transform; we can either have no scale, a uniform
                // scale, two independent scales, or a full 2x2 transform matrix
                // transform components are in 2.14 fixed point format
                var transform = Matrix3x2.Identity;
                if ((flags & CompositeGlyphFlags.HaveScale) != 0)
                {
                    var scale = reader.ReadInt16BE() / F2Dot14ToFloat;
                    transform.M11 = scale;
                    transform.M22 = scale;
                }
                else if ((flags & CompositeGlyphFlags.HaveXYScale) != 0)
                {
                    transform.M11 = reader.ReadInt16BE() / F2Dot14ToFloat;
                    transform.M22 = reader.ReadInt16BE() / F2Dot14ToFloat;
                }
                else if ((flags & CompositeGlyphFlags.HaveTransform) != 0)
                {
                    transform.M11 = reader.ReadInt16BE() / F2Dot14ToFloat;
                    transform.M12 = reader.ReadInt16BE() / F2Dot14ToFloat;
                    transform.M21 = reader.ReadInt16BE() / F2Dot14ToFloat;
                    transform.M22 = reader.ReadInt16BE() / F2Dot14ToFloat;
                }

                subglyph.Transform = transform;
                subglyphs.Add(subglyph);

            } while ((flags & CompositeGlyphFlags.MoreComponents) != 0);

            var result = new CompositeGlyph { Subglyphs = subglyphs.ToArray() };

            // if we have instructions, read them now
            if ((flags & CompositeGlyphFlags.HaveInstructions) != 0)
            {
                var instructionLength = reader.ReadUInt16BE();
                result.Instructions = reader.ReadBytes(instructionLength);
            }

            return result;
        }
예제 #20
0
        static SimpleGlyph ReadSimpleGlyph(DataReader reader, int contourCount)
        {
            // read contour endpoints
            var contours = new int[contourCount];
            var lastEndpoint = reader.ReadUInt16BE();
            contours[0] = lastEndpoint;
            for (int i = 1; i < contours.Length; i++)
            {
                var endpoint = reader.ReadUInt16BE();
                contours[i] = endpoint;
                if (contours[i] <= lastEndpoint)
                    throw new InvalidFontException("Glyph contour endpoints are unordered.");

                lastEndpoint = endpoint;
            }

            // the last contour's endpoint is the number of points in the glyph
            var pointCount = lastEndpoint + 1;
            var points = new Point[pointCount];

            // read instruction data
            var instructionLength = reader.ReadUInt16BE();
            var instructions = reader.ReadBytes(instructionLength);

            // read flags
            var flags = new SimpleGlyphFlags[pointCount];
            int flagIndex = 0;
            while (flagIndex < flags.Length)
            {
                var f = (SimpleGlyphFlags)reader.ReadByte();
                flags[flagIndex++] = f;

                // if Repeat is set, this flag data is repeated n more times
                if ((f & SimpleGlyphFlags.Repeat) != 0)
                {
                    var count = reader.ReadByte();
                    for (int i = 0; i < count; i++)
                        flags[flagIndex++] = f;
                }
            }

            // Read points, first doing all X coordinates and then all Y coordinates.
            // The point packing is insane; coords are either 1 byte or 2; they're
            // deltas from previous point, and flags let you repeat identical points.
            var x = 0;
            for (int i = 0; i < points.Length; i++)
            {
                var f = flags[i];
                var delta = 0;

                if ((f & SimpleGlyphFlags.ShortX) != 0)
                {
                    delta = reader.ReadByte();
                    if ((f & SimpleGlyphFlags.SameX) == 0)
                        delta = -delta;
                }
                else if ((f & SimpleGlyphFlags.SameX) == 0)
                    delta = reader.ReadInt16BE();

                x += delta;
                points[i].X = (FUnit)x;
            }

            var y = 0;
            for (int i = 0; i < points.Length; i++)
            {
                var f = flags[i];
                var delta = 0;

                if ((f & SimpleGlyphFlags.ShortY) != 0)
                {
                    delta = reader.ReadByte();
                    if ((f & SimpleGlyphFlags.SameY) == 0)
                        delta = -delta;
                }
                else if ((f & SimpleGlyphFlags.SameY) == 0)
                    delta = reader.ReadInt16BE();

                y += delta;
                points[i].Y = (FUnit)y;
                points[i].Type = (f & SimpleGlyphFlags.OnCurve) != 0 ? PointType.OnCurve : PointType.Quadratic;
            }

            return new SimpleGlyph
            {
                Points = points,
                ContourEndpoints = contours,
                Instructions = instructions
            };
        }
예제 #21
0
        public static void ReadGlyph(
            DataReader reader, int glyphIndex, int recursionDepth,
            BaseGlyph[] glyphTable, uint glyfOffset, uint glyfLength, uint* loca
        )
        {
            // check if this glyph has already been loaded; this can happen
            // if we're recursively loading subglyphs as part of a composite
            if (glyphTable[glyphIndex] != null)
                return;

            // prevent bad font data from causing infinite recursion
            if (recursionDepth > MaxRecursion)
                throw new InvalidFontException("Bad font data; infinite composite recursion.");

            // check if this glyph doesn't have any actual data
            GlyphHeader header;
            var offset = loca[glyphIndex];
            if ((glyphIndex < glyphTable.Length - 1 && offset == loca[glyphIndex + 1]) || offset >= glyfLength)
            {
                // this is an empty glyph, so synthesize a header
                header = default(GlyphHeader);
            }
            else
            {
                // seek to the right spot and load the header
                reader.Seek(glyfOffset + loca[glyphIndex]);
                header = new GlyphHeader
                {
                    ContourCount = reader.ReadInt16BE(),
                    MinX = reader.ReadInt16BE(),
                    MinY = reader.ReadInt16BE(),
                    MaxX = reader.ReadInt16BE(),
                    MaxY = reader.ReadInt16BE()
                };

                if (header.ContourCount < -1 || header.ContourCount > MaxContours)
                    throw new InvalidFontException("Invalid number of contours for glyph.");
            }

            if (header.ContourCount > 0)
            {
                // positive contours means a simple glyph
                glyphTable[glyphIndex] = ReadSimpleGlyph(reader, header.ContourCount);
            }
            else if (header.ContourCount == -1)
            {
                // -1 means composite glyph
                var composite = ReadCompositeGlyph(reader);
                var subglyphs = composite.Subglyphs;

                // read each subglyph recrusively
                for (int i = 0; i < subglyphs.Length; i++)
                    ReadGlyph(reader, subglyphs[i].Index, recursionDepth + 1, glyphTable, glyfOffset, glyfLength, loca);

                glyphTable[glyphIndex] = composite;
            }
            else
            {
                // no data, so synthesize an empty glyph
                glyphTable[glyphIndex] = new SimpleGlyph
                {
                    Points = new Point[0],
                    ContourEndpoints = new int[0]
                };
            }

            // save bounding box
            var glyph = glyphTable[glyphIndex];
            glyph.MinX = header.MinX;
            glyph.MinY = header.MinY;
            glyph.MaxX = header.MaxX;
            glyph.MaxY = header.MaxY;
        }