Exemple #1
0
        internal static NameTable GetNameTable(TrueTypeDataBytes data)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            // Read these data points to move to the correct data location.
            data.Read32Fixed();
            int numberOfTables = data.ReadUnsignedShort();

            data.ReadUnsignedShort();
            data.ReadUnsignedShort();
            data.ReadUnsignedShort();

            TrueTypeHeaderTable?name = null;

            for (var i = 0; i < numberOfTables; i++)
            {
                var tableHeader = ReadTable(data);

                if (tableHeader.HasValue && tableHeader.Value.Tag == TrueTypeHeaderTable.Name)
                {
                    name = tableHeader;
                    break;
                }
            }

            if (!name.HasValue)
            {
                return(null);
            }

            return(TableParser.Parse <NameTable>(name.Value, data, new TableRegister.Builder()));
        }
Exemple #2
0
        public static KerningTable Load(TrueTypeDataBytes data, TrueTypeHeaderTable headerTable)
        {
            data.Seek(headerTable.Offset);

            var version = data.ReadUnsignedShort();

            var numberOfSubtables = data.ReadUnsignedShort();

            var subTables = new KerningSubTable[numberOfSubtables];

            for (var i = 0; i < numberOfSubtables; i++)
            {
                var currentOffset = data.Position;

                var subtableVersion = data.ReadUnsignedShort();
                var subtableLength  = data.ReadUnsignedShort();
                var coverage        = data.ReadUnsignedShort();

                var kernCoverage = (KernCoverage)coverage;
                var format       = ((coverage & 255) >> 8);

                switch (format)
                {
                case 0:
                    subTables[i] = ReadFormat0Table(subtableVersion, data, kernCoverage);
                    break;

                case 2:
                    subTables[i] = ReadFormat2Table(subtableVersion, data, kernCoverage, currentOffset);
                    break;
                }
            }

            return(new KerningTable(subTables));
        }
Exemple #3
0
        public NameTable Parse(TrueTypeHeaderTable header, TrueTypeDataBytes data, TableRegister.Builder register)
        {
            data.Seek(header.Offset);
            // ReSharper disable once UnusedVariable
            var format       = data.ReadUnsignedShort();
            var count        = data.ReadUnsignedShort();
            var stringOffset = data.ReadUnsignedShort();

            var names = new NameRecordBuilder[count];

            for (var i = 0; i < count; i++)
            {
                names[i] = NameRecordBuilder.Read(data);
            }

            var strings = new TrueTypeNameRecord[count];
            var offset  = header.Offset + stringOffset;

            for (var i = 0; i < count; i++)
            {
                strings[i] = GetTrueTypeNameRecord(names[i], data, offset);
            }

            return(new NameTable(header, GetName(4, strings), GetName(1, strings), GetName(2, strings), strings));
        }
Exemple #4
0
        public TrueTypeFont Parse(TrueTypeDataBytes data)
        {
            var version        = (decimal)data.Read32Fixed();
            int numberOfTables = data.ReadUnsignedShort();

            // Read these data points to move to the correct data location.
            // ReSharper disable UnusedVariable
            int searchRange   = data.ReadUnsignedShort();
            int entrySelector = data.ReadUnsignedShort();
            int rangeShift    = data.ReadUnsignedShort();
            // ReSharper restore UnusedVariable

            var tables = new Dictionary <string, TrueTypeHeaderTable>();

            for (var i = 0; i < numberOfTables; i++)
            {
                var table = ReadTable(data);

                if (table.HasValue)
                {
                    tables[table.Value.Tag] = table.Value;
                }
            }

            var result = ParseTables(version, tables, data);

            return(result);
        }
Exemple #5
0
        /// <summary>
        /// Read the header table from the data stream.
        /// </summary>
        public static HeaderTable Load(TrueTypeDataBytes data, TrueTypeHeaderTable table)
        {
            data.Seek(table.Offset);
            var version            = data.Read32Fixed();
            var fontRevision       = data.Read32Fixed();
            var checkSumAdjustment = data.ReadUnsignedInt();
            var magicNumber        = data.ReadUnsignedInt();

            if (magicNumber != 0x5F0F3CF5)
            {
                throw new InvalidOperationException("The magic number for this TrueType font was incorrect. Value was: " + magicNumber);
            }

            var flags      = data.ReadUnsignedShort();
            var unitsPerEm = data.ReadUnsignedShort();

            if (unitsPerEm < 16 || unitsPerEm > 16384)
            {
                throw new InvalidOperationException($"The units per em for this TrueType font was incorrect, value should be between 16 and 16384 but found {unitsPerEm} istead.");
            }

            DateTime created;

            try
            {
                created = data.ReadInternationalDate();
            }
            catch (InvalidFontFormatException)
            {
                created = DateTime.MinValue;
            }

            DateTime modified;

            try
            {
                modified = data.ReadInternationalDate();
            }
            catch (InvalidFontFormatException)
            {
                modified = DateTime.MinValue;
            }

            var xMin              = data.ReadSignedShort();
            var yMin              = data.ReadSignedShort();
            var xMax              = data.ReadSignedShort();
            var yMax              = data.ReadSignedShort();
            var macStyle          = data.ReadUnsignedShort();
            var lowestRecPpem     = data.ReadUnsignedShort();
            var fontDirectionHint = data.ReadSignedShort();
            var indexToLocFormat  = (IndexToLocationTable.EntryFormat)data.ReadSignedShort();
            var glyphDataFormat   = data.ReadSignedShort();

            return(new HeaderTable(table, version, fontRevision, checkSumAdjustment,
                                   magicNumber, flags, unitsPerEm, created, modified,
                                   xMin, yMin, xMax, yMax, macStyle, lowestRecPpem,
                                   fontDirectionHint, indexToLocFormat, glyphDataFormat));
        }
        private static void ReadSimpleGlyph(TrueTypeDataBytes data, int numberOfContours)
        {
            bool HasFlag(SimpleGlyphFlags flags, SimpleGlyphFlags value)
            {
                return((flags & value) != 0);
            }

            if (numberOfContours == 0)
            {
                return;
            }

            var endPointsOfContours = new ushort[numberOfContours];

            for (var i = 0; i < numberOfContours; i++)
            {
                endPointsOfContours[i] = data.ReadUnsignedShort();
            }

            var instructionLength = data.ReadUnsignedShort();

            var instructions = new byte[instructionLength];

            for (var i = 0; i < instructionLength; i++)
            {
                instructions[i] = data.ReadByte();
            }

            var lastPointIndex = endPointsOfContours[numberOfContours - 1];

            var pointCount = lastPointIndex + 1;

            var perPointFlags = new SimpleGlyphFlags[pointCount];

            for (var i = 0; i < pointCount; i++)
            {
                var flags = (SimpleGlyphFlags)data.ReadByte();

                perPointFlags[i] = flags;
                if (!HasFlag(flags, SimpleGlyphFlags.Repeat))
                {
                    continue;
                }

                var numberOfRepeats = data.ReadByte();
                for (var r = 0; r < numberOfRepeats; r++)
                {
                    i++;
                    perPointFlags[i] = flags;
                }
            }

            // ReSharper disable UnusedVariable
            var xCoordinates = ReadCoordinates(perPointFlags, data, SimpleGlyphFlags.XSingleByte, SimpleGlyphFlags.ThisXIsTheSame);

            var yCoordinates = ReadCoordinates(perPointFlags, data, SimpleGlyphFlags.YSingleByte, SimpleGlyphFlags.ThisYIsTheSame);
            // ReSharper restore UnusedVariable
        }
Exemple #7
0
        public static ByteEncodingCMapTable Load(TrueTypeDataBytes data, TrueTypeCMapPlatform platformId, ushort encodingId)
        {
            var length   = data.ReadUnsignedShort();
            var language = data.ReadUnsignedShort();

            var glyphMapping = data.ReadByteArray(length - (SizeOfShort * 3));

            return(new ByteEncodingCMapTable(platformId, encodingId, language, glyphMapping));
        }
Exemple #8
0
        private static KerningSubTable ReadFormat2Table(int version, TrueTypeDataBytes data, KernCoverage coverage, long tableStartOffset)
        {
            // TODO: Implement and test this;
            return(null);

#pragma warning disable 162
            var rowWidth = data.ReadUnsignedShort();

            var leftClassTableOffset  = data.ReadUnsignedShort();
            var rightClassTableOffset = data.ReadUnsignedShort();

            var kerningArrayOffset = data.ReadUnsignedShort();

            data.Seek(tableStartOffset + leftClassTableOffset);

            var leftTableFirstGlyph = data.ReadUnsignedShort();
            var numberOfLeftGlyphs  = data.ReadUnsignedShort();

            var leftGlyphClassValues = new int[numberOfLeftGlyphs];

            for (var i = 0; i < numberOfLeftGlyphs; i++)
            {
                leftGlyphClassValues[i] = data.ReadUnsignedShort();
            }

            data.Seek(tableStartOffset + rightClassTableOffset);

            var rightTableFirstGlyph = data.ReadUnsignedShort();
            var numberOfRightGlyphs  = data.ReadUnsignedShort();

            var rightGlyphClassValues = new int[numberOfRightGlyphs];

            for (var i = 0; i < numberOfRightGlyphs; i++)
            {
                rightGlyphClassValues[i] = data.ReadUnsignedShort();
            }

            data.Seek(tableStartOffset + kerningArrayOffset);

            var pairs = new List <KernPair>(numberOfRightGlyphs * numberOfLeftGlyphs);

            // Data is a [left glyph count, right glyph count] array:
            for (int i = 0; i < numberOfLeftGlyphs; i++)
            {
                var leftClassValue = leftGlyphClassValues[i];
                for (int j = 0; j < numberOfRightGlyphs; j++)
                {
                    var rightClassValue = rightGlyphClassValues[j];

                    pairs.Add(new KernPair(leftClassValue, rightClassValue, data.ReadSignedShort()));
                }
            }

            return(new KerningSubTable(version, coverage, pairs));

#pragma warning restore 162
        }
Exemple #9
0
        public static ByteEncodingCMapTable Load(TrueTypeDataBytes data, TrueTypeCMapPlatform platformId, int encodingId)
        {
            // ReSharper disable UnusedVariable
            var length  = data.ReadUnsignedShort();
            var version = data.ReadUnsignedShort();
            // ReSharper restore UnusedVariable

            var glyphMapping = data.ReadByteArray(GlyphMappingLength);

            return(new ByteEncodingCMapTable(platformId, encodingId, glyphMapping));
        }
        private static IReadOnlyList <CompositeGlyphIndexReference> ReadCompositeGlyph(TrueTypeDataBytes data)
        {
            bool HasFlag(CompositeGlyphFlags actual, CompositeGlyphFlags value)
            {
                return((actual & value) != 0);
            }

            var glyphIndices = new List <CompositeGlyphIndexReference>();
            CompositeGlyphFlags flags;

            do
            {
                flags = (CompositeGlyphFlags)data.ReadUnsignedShort();
                var indexOffset = data.Position;
                var glyphIndex  = data.ReadUnsignedShort();
                glyphIndices.Add(new CompositeGlyphIndexReference(glyphIndex, (uint)indexOffset));

                if (HasFlag(flags, CompositeGlyphFlags.Args1And2AreWords))
                {
                    data.ReadSignedShort();
                    data.ReadSignedShort();
                }
                else
                {
                    data.ReadByte();
                    data.ReadByte();
                }

                if (HasFlag(flags, CompositeGlyphFlags.WeHaveAScale))
                {
                    data.ReadSignedShort();
                }
                else if (HasFlag(flags, CompositeGlyphFlags.WeHaveAnXAndYScale))
                {
                    data.ReadSignedShort();
                    data.ReadSignedShort();
                }
                else if (HasFlag(flags, CompositeGlyphFlags.WeHaveATwoByTwo))
                {
                    data.ReadSignedShort();
                    data.ReadSignedShort();
                    data.ReadSignedShort();
                    data.ReadSignedShort();
                }
            } while (HasFlag(flags, CompositeGlyphFlags.MoreComponents));

            // TODO: Read hinting instructions (PDFBox GlyfCompositeDescript.java line 76).

            return(glyphIndices);
        }
Exemple #11
0
        public static TrimmedTableMappingCMapTable Load(TrueTypeDataBytes data, TrueTypeCMapPlatform platformId, ushort encodingId)
        {
            var length   = data.ReadUnsignedShort();
            var language = data.ReadUnsignedShort();

            // First character code in the range.
            var firstCode = data.ReadUnsignedShort();

            // Number of character codes in the range.
            var entryCount = data.ReadUnsignedShort();

            var glyphIndices = data.ReadUnsignedShortArray(entryCount);

            return(new TrimmedTableMappingCMapTable(platformId, encodingId, firstCode, entryCount, glyphIndices));
        }
        public static HorizontalMetricsTable Load(TrueTypeDataBytes data, TrueTypeHeaderTable table, TableRegister.Builder tableRegister)
        {
            var glyphCount  = tableRegister.MaximumProfileTable.NumberOfGlyphs;
            var metricCount = tableRegister.HorizontalHeaderTable.NumberOfHeaderMetrics;

            data.Seek(table.Offset);

            // The number of entries in the left side bearing field per entry is number of glyphs - number of metrics
            var additionalLeftSideBearingLength = glyphCount - metricCount;

            var advancedWidths = new int[metricCount];

            // For bearings over the metric count, the width is the same as the last width in advanced widths.
            var leftSideBearings = new short[glyphCount];

            for (var i = 0; i < metricCount; i++)
            {
                advancedWidths[i]   = data.ReadUnsignedShort();
                leftSideBearings[i] = data.ReadSignedShort();
            }

            for (var i = 0; i < additionalLeftSideBearingLength; i++)
            {
                leftSideBearings[metricCount + i] = data.ReadSignedShort();
            }

            return(new HorizontalMetricsTable(table, advancedWidths, leftSideBearings, metricCount));
        }
Exemple #13
0
        /// <summary>
        /// Load the index to location (loca) table from the TrueType font. Requires the maximum profile (maxp) and header (head) table
        /// to have been parsed.
        /// </summary>
        internal static IndexToLocationTable Load(TrueTypeDataBytes data, TrueTypeHeaderTable table, TableRegister.Builder tableRegister)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            if (tableRegister == null)
            {
                throw new ArgumentNullException(nameof(tableRegister));
            }

            data.Seek(table.Offset);

            var headerTable         = tableRegister.HeaderTable;
            var maximumProfileTable = tableRegister.MaximumProfileTable;

            if (headerTable == null)
            {
                throw new InvalidFontFormatException("No header (head) table was defined in this font.");
            }

            if (maximumProfileTable == null)
            {
                throw new InvalidFontFormatException("No maximum profile (maxp) table was defined in this font.");
            }

            var format = (EntryFormat)headerTable.IndexToLocFormat;

            var glyphCount = maximumProfileTable.NumberOfGlyphs + 1;

            uint[] offsets;

            switch (format)
            {
            case EntryFormat.Short:
            {
                // The local offset divided by 2 is stored.
                offsets = new uint[glyphCount];
                for (var i = 0; i < glyphCount; i++)
                {
                    offsets[i] = (uint)(data.ReadUnsignedShort() * 2);
                }
                break;
            }

            case EntryFormat.Long:
            {
                // The actual offset is stored.
                offsets = data.ReadUnsignedIntArray(glyphCount);
                break;
            }

            default:
                throw new InvalidOperationException($"The format {format} was invalid for the index to location (loca) table.");
            }


            return(new IndexToLocationTable(table, format, offsets));
        }
Exemple #14
0
        private static SimpleGlyphDescription ReadSimpleGlyph(TrueTypeDataBytes data, short contourCount, TrueTypeGlyphBounds bounds)
        {
            var endPointsOfContours = data.ReadUnsignedShortArray(contourCount);

            var instructionLength = data.ReadUnsignedShort();

            data.ReadByteArray(instructionLength);

            var pointCount = 0;

            if (contourCount > 0)
            {
                pointCount = endPointsOfContours[contourCount - 1] + 1;
            }

            var flags = ReadFlags(data, pointCount);

            var xCoordinates = ReadCoordinates(data, pointCount, flags, SimpleGlyphFlags.XShortVector,
                                               SimpleGlyphFlags.XSignOrSame);

            var yCoordinates = ReadCoordinates(data, pointCount, flags, SimpleGlyphFlags.YShortVector,
                                               SimpleGlyphFlags.YSignOrSame);

            return(new SimpleGlyphDescription(instructionLength, endPointsOfContours, flags, xCoordinates, yCoordinates));
        }
Exemple #15
0
        private static Glyph ReadSimpleGlyph(TrueTypeDataBytes data, short contourCount, PdfRectangle bounds)
        {
            var endPointsOfContours = data.ReadUnsignedShortArray(contourCount);

            var instructionLength = data.ReadUnsignedShort();

            var instructions = data.ReadByteArray(instructionLength);

            var pointCount = 0;

            if (contourCount > 0)
            {
                pointCount = endPointsOfContours[contourCount - 1] + 1;
            }

            var flags = ReadFlags(data, pointCount);

            var xCoordinates = ReadCoordinates(data, pointCount, flags, SimpleGlyphFlags.XShortVector,
                                               SimpleGlyphFlags.XSignOrSame);

            var yCoordinates = ReadCoordinates(data, pointCount, flags, SimpleGlyphFlags.YShortVector,
                                               SimpleGlyphFlags.YSignOrSame);

            var points = new GlyphPoint[xCoordinates.Length];

            for (var i = xCoordinates.Length - 1; i >= 0; i--)
            {
                var isOnCurve = (flags[i] & SimpleGlyphFlags.OnCurve) == SimpleGlyphFlags.OnCurve;
                points[i] = new GlyphPoint(xCoordinates[i], yCoordinates[i], isOnCurve);
            }

            return(new Glyph(true, instructions, endPointsOfContours, points, bounds));
        }
        public HorizontalHeaderTable Parse(TrueTypeHeaderTable header, TrueTypeDataBytes data, TableRegister.Builder register)
        {
            data.Seek(header.Offset);
            var majorVersion = data.ReadUnsignedShort();
            var minorVersion = data.ReadUnsignedShort();

            var ascender  = data.ReadSignedShort();
            var descender = data.ReadSignedShort();
            var lineGap   = data.ReadSignedShort();

            var advancedWidthMax = data.ReadUnsignedShort();

            var minLeftSideBearing  = data.ReadSignedShort();
            var minRightSideBearing = data.ReadSignedShort();
            var xMaxExtent          = data.ReadSignedShort();

            var caretSlopeRise = data.ReadSignedShort();
            var caretSlopeRun  = data.ReadSignedShort();
            var caretOffset    = data.ReadSignedShort();

            // Reserved section
            data.ReadSignedShort();
            data.ReadSignedShort();
            data.ReadSignedShort();
            data.ReadSignedShort();

            var metricDataFormat = data.ReadSignedShort();

            if (metricDataFormat != 0)
            {
                throw new NotSupportedException("The metric data format for a horizontal header table should be 0.");
            }

            var numberOfHeaderMetrics = data.ReadUnsignedShort();

            return(new HorizontalHeaderTable(header, majorVersion, minorVersion, ascender,
                                             descender, lineGap, advancedWidthMax,
                                             minLeftSideBearing,
                                             minRightSideBearing,
                                             xMaxExtent,
                                             caretSlopeRise,
                                             caretSlopeRun,
                                             caretOffset,
                                             metricDataFormat,
                                             numberOfHeaderMetrics));
        }
Exemple #17
0
        public static Format4CMapTable Load(TrueTypeDataBytes data, int platformId, int encodingId)
        {
            // Length in bytes.
            var length = data.ReadUnsignedShort();

            // Used for sub-tables with a Macintosh platform ID.
            var version = data.ReadUnsignedShort();

            var doubleSegmentCount = data.ReadUnsignedShort();

            // Defines the number of contiguous segments.
            var segmentCount = doubleSegmentCount / 2;

            // Some crazy sum.
            var searchRange   = data.ReadUnsignedShort();
            var entrySelector = data.ReadUnsignedShort();
            var rangeShift    = data.ReadUnsignedShort();

            // End character codes for each segment.
            var endCounts = data.ReadUnsignedShortArray(segmentCount);

            // Should be zero.
            var reservedPad = data.ReadUnsignedShort();

            // Start character codes for each segment.
            var startCounts = data.ReadUnsignedShortArray(segmentCount);

            // Delta for all character codes in the segment. Contrary to the spec this is actually a short[].
            var idDeltas = data.ReadShortArray(segmentCount);

            var idRangeOffsets = data.ReadUnsignedShortArray(segmentCount);

            const int singleIntsRead = 8;
            const int intArraysRead  = 8;

            // ReSharper disable once ArrangeRedundantParentheses
            var remainingBytes = length - ((singleIntsRead * 2) + intArraysRead * segmentCount);

            var remainingInts = remainingBytes / 2;

            var glyphIndices = data.ReadUnsignedShortArray(remainingInts);

            var segments = new Segment[endCounts.Length];

            for (int i = 0; i < endCounts.Length; i++)
            {
                var start = startCounts[i];
                var end   = endCounts[i];

                var delta   = idDeltas[i];
                var offsets = idRangeOffsets[i];

                segments[i] = new Segment(start, end, delta, offsets);
            }

            return(new Format4CMapTable(platformId, encodingId, version, segments, glyphIndices));
        }
Exemple #18
0
 public static NameRecordBuilder Read(TrueTypeDataBytes data)
 {
     return(new NameRecordBuilder(data.ReadUnsignedShort(),
                                  data.ReadUnsignedShort(),
                                  data.ReadUnsignedShort(),
                                  data.ReadUnsignedShort(),
                                  data.ReadUnsignedShort(),
                                  data.ReadUnsignedShort()));
 }
Exemple #19
0
        public TrueTypeFont Parse(TrueTypeDataBytes data)
        {
            var version        = (decimal)data.Read32Fixed();
            int numberOfTables = data.ReadUnsignedShort();
            int searchRange    = data.ReadUnsignedShort();
            int entrySelector  = data.ReadUnsignedShort();
            int rangeShift     = data.ReadUnsignedShort();

            var tables = new Dictionary <string, TrueTypeHeaderTable>();

            for (var i = 0; i < numberOfTables; i++)
            {
                var table = ReadTable(data);

                if (table.HasValue)
                {
                    tables[table.Value.Tag] = table.Value;
                }
            }

            var result = ParseTables(version, tables, data);

            return(result);
        }
Exemple #20
0
        public HorizontalMetricsTable Parse(TrueTypeHeaderTable header, TrueTypeDataBytes data, TableRegister.Builder register)
        {
            var glyphCount  = register.MaximumProfileTable.NumberOfGlyphs;
            var metricCount = register.HorizontalHeaderTable.NumberOfHeaderMetrics;

            data.Seek(header.Offset);
            var bytesRead = 0;


            var horizontalMetrics = new HorizontalMetric[metricCount];


            for (var i = 0; i < metricCount; i++)
            {
                var width = data.ReadUnsignedShort();
                var lsb   = data.ReadSignedShort();

                horizontalMetrics[i] = new HorizontalMetric(width, lsb);

                bytesRead += 4;
            }

            int numberNonHorizontal = glyphCount - metricCount;

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

            // The number of entries in the left side bearing field per entry is number of glyphs - number of metrics
            // For bearings over the metric count, the width is the same as the last width in advanced widths.
            var additionalLeftSideBearings = new short[numberNonHorizontal];

            for (var i = 0; i < additionalLeftSideBearings.Length; i++)
            {
                if (bytesRead >= header.Length)
                {
                    break;
                }

                additionalLeftSideBearings[i] = data.ReadSignedShort();
                bytesRead += 2;
            }

            return(new HorizontalMetricsTable(header, horizontalMetrics, additionalLeftSideBearings));
        }
        public static IndexToLocationTable Load(TrueTypeDataBytes data, TrueTypeHeaderTable table, TableRegister.Builder tableRegister)
        {
            const short shortFormat = 0;
            const short longFormat  = 1;

            data.Seek(table.Offset);

            var headerTable         = tableRegister.HeaderTable;
            var maximumProfileTable = tableRegister.MaximumProfileTable;

            var format = headerTable.IndexToLocFormat;

            var glyphCount = maximumProfileTable.NumberOfGlyphs + 1;

            var offsets = new long[glyphCount];

            switch (format)
            {
            case shortFormat:
            {         // The local offset divided by 2 is stored.
                for (int i = 0; i < glyphCount; i++)
                {
                    offsets[i] = data.ReadUnsignedShort() * 2;
                }
                break;
            }

            case longFormat:
            {
                // The actual offset is stored.
                data.ReadUnsignedIntArray(offsets, glyphCount);
                break;
            }

            default:
                throw new InvalidOperationException($"The format {format} was invalid for the index to location (loca) table.");
            }


            return(new IndexToLocationTable(table, offsets));
        }
Exemple #22
0
        private static KerningSubTable ReadFormat0Table(int version, TrueTypeDataBytes data, KernCoverage coverage)
        {
            var numberOfPairs = data.ReadUnsignedShort();
            // ReSharper disable once UnusedVariable
            var searchRange = data.ReadUnsignedShort();
            // ReSharper disable once UnusedVariable
            var entrySelector = data.ReadUnsignedShort();
            // ReSharper disable once UnusedVariable
            var rangeShift = data.ReadUnsignedShort();

            var pairs = new KernPair[numberOfPairs];

            for (int i = 0; i < numberOfPairs; i++)
            {
                var leftGlyphIndex  = data.ReadUnsignedShort();
                var rightGlyphIndex = data.ReadUnsignedShort();

                var value = data.ReadSignedShort();

                pairs[i] = new KernPair(leftGlyphIndex, rightGlyphIndex, value);
            }

            return(new KerningSubTable(version, coverage, pairs));
        }
Exemple #23
0
        public CMapTable Parse(TrueTypeHeaderTable header, TrueTypeDataBytes data, TableRegister.Builder register)
        {
            data.Seek(header.Offset);

            var tableVersionNumber = data.ReadUnsignedShort();

            var numberOfEncodingTables = data.ReadUnsignedShort();

            var subTableHeaders = new SubTableHeaderEntry[numberOfEncodingTables];

            for (int i = 0; i < numberOfEncodingTables; i++)
            {
                var platformId = (TrueTypeCMapPlatform)data.ReadUnsignedShort();
                var encodingId = data.ReadUnsignedShort();
                var offset     = data.ReadUnsignedInt();

                subTableHeaders[i] = new SubTableHeaderEntry(platformId, encodingId, offset);
            }

            var tables = new List <ICMapSubTable>(numberOfEncodingTables);

            var numberofGlyphs = register.MaximumProfileTable.NumberOfGlyphs;

            for (var i = 0; i < subTableHeaders.Length; i++)
            {
                var subTableHeader = subTableHeaders[i];

                data.Seek(header.Offset + subTableHeader.Offset);

                var format = data.ReadUnsignedShort();

                /*
                 * There are 9 currently available formats:
                 * 0: Character code and glyph indices are restricted to a single byte. Rare.
                 * 2: Suitable for CJK characters. Contain mixed 8/16 byte encoding.
                 * 4: 2 byte encoding format. Used when character codes fall into (gappy) contiguous ranges.
                 * 6: 'Trimmed table mapping', used when character codes fall into a single contiguous range. This is dense mapping.
                 * 8: 16/32 bit coverage. Uses mixed length character codes.
                 * 10: Similar to format 6, trimmed table/array for 32 bits.
                 * 12: Segmented coverage, similar to format 4 but for 32 bit/4 byte.
                 * 13: Many to one mappings. Used by Apple for the LastResort font.
                 * 14: Unicode variation sequences.
                 *
                 * Many of the formats are obsolete or not really used. Modern fonts will tend to use formats 4, 6 and 12.
                 * For PDF we will support 0, 2 and 4 since these are in the original TrueType spec.
                 */
                switch (format)
                {
                case 0:
                {
                    // Simple 1 to 1 mapping of character codes to glyph codes.
                    var item = ByteEncodingCMapTable.Load(data, subTableHeader.PlatformId, subTableHeader.EncodingId);
                    tables.Add(item);
                    break;
                }

                case 2:
                {
                    // Useful for CJK characters. Use mixed 8/16 bit encoding.
                    var item = HighByteMappingCMapTable.Load(data, numberofGlyphs, subTableHeader.PlatformId, subTableHeader.EncodingId);
                    tables.Add(item);
                    break;
                }

                case 4:
                {
                    // Microsoft's standard mapping table.
                    var item = Format4CMapTable.Load(data, subTableHeader.PlatformId, subTableHeader.EncodingId);
                    tables.Add(item);
                    break;
                }

                case 6:
                {
                    var item = TrimmedTableMappingCMapTable.Load(data, subTableHeader.PlatformId, subTableHeader.EncodingId);
                    tables.Add(item);
                    break;
                }
                }
            }

            return(new CMapTable(tableVersionNumber, header, tables));
        }
Exemple #24
0
        private static IGlyphDescription ReadCompositeGlyph(TrueTypeDataBytes data, TemporaryCompositeLocation compositeLocation, Dictionary <int, TemporaryCompositeLocation> compositeLocations, IGlyphDescription[] glyphs,
                                                            IGlyphDescription emptyGlyph)
        {
            bool HasFlag(CompositeGlyphFlags value, CompositeGlyphFlags target)
            {
                return((value & target) == target);
            }

            data.Seek(compositeLocation.Position);

            var components = new List <CompositeComponent>();

            // First recursively find all components and ensure they are available.
            CompositeGlyphFlags flags;

            do
            {
                flags = (CompositeGlyphFlags)data.ReadUnsignedShort();
                var glyphIndex = data.ReadUnsignedShort();

                var childGlyph = glyphs[glyphIndex];

                if (childGlyph == null)
                {
                    if (!compositeLocations.TryGetValue(glyphIndex, out var missingComposite))
                    {
                        throw new InvalidOperationException($"The composite glyph required a contour at index {glyphIndex} but there was no simple or composite glyph at this location.");
                    }

                    var position = data.Position;
                    childGlyph = ReadCompositeGlyph(data, missingComposite, compositeLocations, glyphs, emptyGlyph);
                    data.Seek(position);

                    glyphs[glyphIndex] = childGlyph;
                }

                short arg1, arg2;
                if (HasFlag(flags, CompositeGlyphFlags.Args1And2AreWords))
                {
                    arg1 = data.ReadSignedShort();
                    arg2 = data.ReadSignedShort();
                }
                else
                {
                    arg1 = data.ReadByte();
                    arg2 = data.ReadByte();
                }

                decimal xscale  = 1;
                decimal scale01 = 0;
                decimal scale10 = 0;
                decimal yscale  = 1;

                if (HasFlag(flags, CompositeGlyphFlags.WeHaveAScale))
                {
                    xscale = ReadTwoFourteenFormat(data);
                    yscale = xscale;
                }
                else if (HasFlag(flags, CompositeGlyphFlags.WeHaveAnXAndYScale))
                {
                    xscale = ReadTwoFourteenFormat(data);
                    yscale = ReadTwoFourteenFormat(data);
                }
                else if (HasFlag(flags, CompositeGlyphFlags.WeHaveATwoByTwo))
                {
                    xscale  = ReadTwoFourteenFormat(data);
                    scale01 = ReadTwoFourteenFormat(data);
                    scale10 = ReadTwoFourteenFormat(data);
                    yscale  = ReadTwoFourteenFormat(data);
                }

                if (HasFlag(flags, CompositeGlyphFlags.ArgsAreXAndYValues))
                {
                    components.Add(new CompositeComponent(glyphIndex, new PdfMatrix3By2(xscale, scale01, scale10, yscale, arg1, arg2)));
                }
                else
                {
                    // TODO: Not implemented, it is unclear how to do this.
                }
            } while (HasFlag(flags, CompositeGlyphFlags.MoreComponents));

            // Now build the final glyph from the components.
            IGlyphDescription builderGlyph = null;

            foreach (var component in components)
            {
                var glyph = glyphs[component.Index];

                var transformed = glyph.Transform(component.Transformation);

                if (builderGlyph == null)
                {
                    builderGlyph = transformed;
                }
                else
                {
                    builderGlyph = builderGlyph.Merge(transformed);
                }
            }

            builderGlyph = builderGlyph ?? emptyGlyph;

            return(new Glyph(false, builderGlyph.Instructions, builderGlyph.EndPointsOfContours, builderGlyph.Points, compositeLocation.Bounds));
        }
Exemple #25
0
        public Os2Table Parse(TrueTypeHeaderTable header, TrueTypeDataBytes data, TableRegister.Builder register)
        {
            data.Seek(header.Offset);

            var version = data.ReadUnsignedShort();

            var xAvgCharWidth       = data.ReadSignedShort();
            var weightClass         = data.ReadUnsignedShort();
            var widthClass          = data.ReadUnsignedShort();
            var typeFlags           = data.ReadUnsignedShort();
            var ySubscriptXSize     = data.ReadSignedShort();
            var ySubscriptYSize     = data.ReadSignedShort();
            var ySubscriptXOffset   = data.ReadSignedShort();
            var ySubscriptYOffset   = data.ReadSignedShort();
            var ySuperscriptXSize   = data.ReadSignedShort();
            var ySuperscriptYSize   = data.ReadSignedShort();
            var ySuperscriptXOffset = data.ReadSignedShort();
            var ySuperscriptYOffset = data.ReadSignedShort();
            var yStrikeoutSize      = data.ReadSignedShort();
            var yStrikeoutPosition  = data.ReadSignedShort();
            var familyClass         = data.ReadSignedShort();
            var panose              = data.ReadByteArray(10);
            var ulCharRange1        = data.ReadUnsignedInt();
            var ulCharRange2        = data.ReadUnsignedInt();
            var ulCharRange3        = data.ReadUnsignedInt();
            var ulCharRange4        = data.ReadUnsignedInt();
            var vendorIdBytes       = data.ReadByteArray(4);
            var selectionFlags      = data.ReadUnsignedShort();
            var firstCharacterIndex = data.ReadUnsignedShort();
            var lastCharacterIndex  = data.ReadUnsignedShort();
            var unicodeCharRange    = new[] { ulCharRange1, ulCharRange2, ulCharRange3, ulCharRange4 };

            var vendorId = Encoding.ASCII.GetString(vendorIdBytes);

            /*
             * Documentation for OS/2 version 0 in Apple’s TrueType Reference Manual stops at the usLastCharIndex field
             * and does not include the last five fields of the table as it was defined by Microsoft.
             * Some legacy TrueType fonts may have been built with a shortened version 0 OS/2 table.
             * Applications should check the table length for a version 0 OS/2 table before reading these fields.
             */
            if (version == 0 && header.Length == 68)
            {
                return(new Os2Table(header, version, xAvgCharWidth,
                                    weightClass, widthClass, typeFlags, ySubscriptXSize,
                                    ySubscriptYSize,
                                    ySubscriptXOffset,
                                    ySubscriptYOffset,
                                    ySuperscriptXSize,
                                    ySuperscriptYSize,
                                    ySuperscriptXOffset,
                                    ySuperscriptYOffset,
                                    yStrikeoutSize,
                                    yStrikeoutPosition,
                                    familyClass,
                                    panose,
                                    unicodeCharRange,
                                    vendorId,
                                    selectionFlags,
                                    firstCharacterIndex,
                                    lastCharacterIndex));
            }

            short  sTypoAscender;
            short  sTypoDescender;
            short  sTypoLineGap;
            ushort usWinAscent;
            ushort usWinDescent;

            try
            {
                sTypoAscender  = data.ReadSignedShort();
                sTypoDescender = data.ReadSignedShort();
                sTypoLineGap   = data.ReadSignedShort();
                usWinAscent    = data.ReadUnsignedShort();
                usWinDescent   = data.ReadUnsignedShort();
            }
            catch
            {
                // Font may be invalid. Try falling back to shorter version...
                return(new Os2Table(header, version, xAvgCharWidth,
                                    weightClass, widthClass, typeFlags, ySubscriptXSize,
                                    ySubscriptYSize,
                                    ySubscriptXOffset,
                                    ySubscriptYOffset,
                                    ySuperscriptXSize,
                                    ySuperscriptYSize,
                                    ySuperscriptXOffset,
                                    ySuperscriptYOffset,
                                    yStrikeoutSize,
                                    yStrikeoutPosition,
                                    familyClass,
                                    panose,
                                    unicodeCharRange,
                                    vendorId,
                                    selectionFlags,
                                    firstCharacterIndex,
                                    lastCharacterIndex));
            }

            if (version == 0)
            {
                return(new Os2RevisedVersion0Table(header, version, xAvgCharWidth,
                                                   weightClass, widthClass, typeFlags, ySubscriptXSize,
                                                   ySubscriptYSize,
                                                   ySubscriptXOffset,
                                                   ySubscriptYOffset,
                                                   ySuperscriptXSize,
                                                   ySuperscriptYSize,
                                                   ySuperscriptXOffset,
                                                   ySuperscriptYOffset,
                                                   yStrikeoutSize,
                                                   yStrikeoutPosition,
                                                   familyClass,
                                                   panose,
                                                   unicodeCharRange,
                                                   vendorId,
                                                   selectionFlags,
                                                   firstCharacterIndex,
                                                   lastCharacterIndex,
                                                   sTypoAscender,
                                                   sTypoDescender,
                                                   sTypoLineGap,
                                                   usWinAscent,
                                                   usWinDescent));
            }

            var ulCodePageRange1 = data.ReadUnsignedInt();
            var ulCodePageRange2 = data.ReadUnsignedInt();

            if (version == 1)
            {
                return(new Os2Version1Table(header, version, xAvgCharWidth,
                                            weightClass, widthClass, typeFlags, ySubscriptXSize,
                                            ySubscriptYSize,
                                            ySubscriptXOffset,
                                            ySubscriptYOffset,
                                            ySuperscriptXSize,
                                            ySuperscriptYSize,
                                            ySuperscriptXOffset,
                                            ySuperscriptYOffset,
                                            yStrikeoutSize,
                                            yStrikeoutPosition,
                                            familyClass,
                                            panose,
                                            unicodeCharRange,
                                            vendorId,
                                            selectionFlags,
                                            firstCharacterIndex,
                                            lastCharacterIndex,
                                            sTypoAscender,
                                            sTypoDescender,
                                            sTypoLineGap,
                                            usWinAscent,
                                            usWinDescent,
                                            ulCodePageRange1,
                                            ulCodePageRange2));
            }

            var sxHeight      = data.ReadSignedShort();
            var sCapHeight    = data.ReadSignedShort();
            var usDefaultChar = data.ReadUnsignedShort();
            var usBreakChar   = data.ReadUnsignedShort();
            var usMaxContext  = data.ReadUnsignedShort();

            if (version < 5)
            {
                return(new Os2Version2To4OpenTypeTable(header, version, xAvgCharWidth,
                                                       weightClass, widthClass, typeFlags, ySubscriptXSize,
                                                       ySubscriptYSize,
                                                       ySubscriptXOffset,
                                                       ySubscriptYOffset,
                                                       ySuperscriptXSize,
                                                       ySuperscriptYSize,
                                                       ySuperscriptXOffset,
                                                       ySuperscriptYOffset,
                                                       yStrikeoutSize,
                                                       yStrikeoutPosition,
                                                       familyClass,
                                                       panose,
                                                       unicodeCharRange,
                                                       vendorId,
                                                       selectionFlags,
                                                       firstCharacterIndex,
                                                       lastCharacterIndex,
                                                       sTypoAscender,
                                                       sTypoDescender,
                                                       sTypoLineGap,
                                                       usWinAscent,
                                                       usWinDescent,
                                                       ulCodePageRange1,
                                                       ulCodePageRange2,
                                                       sxHeight,
                                                       sCapHeight,
                                                       usDefaultChar,
                                                       usBreakChar,
                                                       usMaxContext));
            }

            var usLowerOpticalPointSize = data.ReadUnsignedShort();
            var usUpperOpticalPointSize = data.ReadUnsignedShort();

            return(new Os2Version5OpenTypeTable(header, version, xAvgCharWidth,
                                                weightClass, widthClass, typeFlags, ySubscriptXSize,
                                                ySubscriptYSize,
                                                ySubscriptXOffset,
                                                ySubscriptYOffset,
                                                ySuperscriptXSize,
                                                ySuperscriptYSize,
                                                ySuperscriptXOffset,
                                                ySuperscriptYOffset,
                                                yStrikeoutSize,
                                                yStrikeoutPosition,
                                                familyClass,
                                                panose,
                                                unicodeCharRange,
                                                vendorId,
                                                selectionFlags,
                                                firstCharacterIndex,
                                                lastCharacterIndex,
                                                sTypoAscender,
                                                sTypoDescender,
                                                sTypoLineGap,
                                                usWinAscent,
                                                usWinDescent,
                                                ulCodePageRange1,
                                                ulCodePageRange2,
                                                sxHeight,
                                                sCapHeight,
                                                usDefaultChar,
                                                usBreakChar,
                                                usMaxContext,
                                                usLowerOpticalPointSize,
                                                usUpperOpticalPointSize));
        }
Exemple #26
0
        public NameTable Parse(TrueTypeHeaderTable header, TrueTypeDataBytes data, TableRegister.Builder register)
        {
            data.Seek(header.Offset);
            // ReSharper disable once UnusedVariable
            var format       = data.ReadUnsignedShort();
            var count        = data.ReadUnsignedShort();
            var stringOffset = data.ReadUnsignedShort();

            var names = new NameRecordBuilder[count];

            for (var i = 0; i < count; i++)
            {
                names[i] = NameRecordBuilder.Read(data);
            }

            var strings = new TrueTypeNameRecord[count];

            for (var i = 0; i < count; i++)
            {
                var nameRecord = names[i];

                var encoding = OtherEncodings.Iso88591;

                switch (nameRecord.PlatformId)
                {
                case TrueTypePlatformIdentifier.Windows:
                {
                    var platformEncoding = (TrueTypeWindowsEncodingIdentifier)nameRecord.PlatformEncodingId;

                    if (platformEncoding == TrueTypeWindowsEncodingIdentifier.Symbol ||
                        platformEncoding == TrueTypeWindowsEncodingIdentifier.UnicodeBmp)
                    {
                        encoding = Encoding.BigEndianUnicode;
                    }
                    break;
                }

                case TrueTypePlatformIdentifier.Unicode:
                {
                    encoding = Encoding.BigEndianUnicode;
                    break;
                }

                case TrueTypePlatformIdentifier.Iso:
                {
                    switch (nameRecord.PlatformEncodingId)
                    {
                    case 0:
                        encoding = Encoding.GetEncoding("US-ASCII");
                        break;

                    case 1:
                        encoding = Encoding.GetEncoding("ISO-10646-UCS-2");
                        break;
                    }

                    break;
                }
                }

                var position = header.Offset + stringOffset + nameRecord.Offset;

                data.Seek(position);

                var str = data.ReadString(nameRecord.Length, encoding);

                strings[i] = nameRecord.ToNameRecord(str);
            }

            return(new NameTable(header, GetName(4, strings), GetName(1, strings), GetName(2, strings), strings));
        }
Exemple #27
0
        private static string[] GetFormat2GlyphNames(TrueTypeDataBytes data)
        {
            const int reservedIndexStart = 32768;

            var numberOfGlyphs = data.ReadUnsignedShort();

            var glyphNameIndex = new int[numberOfGlyphs];

            var glyphNames = new string[numberOfGlyphs];

            var maxIndex = int.MinValue;

            for (var i = 0; i < numberOfGlyphs; i++)
            {
                var index = data.ReadUnsignedShort();

                glyphNameIndex[i] = index;

                if (index < reservedIndexStart)
                {
                    maxIndex = Math.Max(maxIndex, index);
                }
            }

            var nameArray = default(string[]);

            if (maxIndex >= WindowsGlyphList4.NumberOfMacGlyphs)
            {
                var namesLength = maxIndex - WindowsGlyphList4.NumberOfMacGlyphs + 1;
                nameArray = new string[namesLength];

                for (var i = 0; i < namesLength; i++)
                {
                    var numberOfCharacters = data.ReadUnsignedByte();
                    nameArray[i] = data.ReadString(numberOfCharacters, Encoding.UTF8);
                }
            }

            for (int i = 0; i < numberOfGlyphs; i++)
            {
                var index = glyphNameIndex[i];
                if (index < WindowsGlyphList4.NumberOfMacGlyphs)
                {
                    glyphNames[i] = WindowsGlyphList4.MacGlyphNames[index];
                }
                else if (index >= WindowsGlyphList4.NumberOfMacGlyphs && index < reservedIndexStart)
                {
                    if (nameArray == null)
                    {
                        throw new InvalidOperationException("The name array was null despite the number of glyphs exceeding the maximum Mac Glyphs.");
                    }

                    glyphNames[i] = nameArray[index - WindowsGlyphList4.NumberOfMacGlyphs];
                }
                else
                {
                    glyphNames[i] = ".undefined";
                }
            }

            return(glyphNames);
        }
        public static HighByteMappingCMapTable Load(TrueTypeDataBytes data, int numberOfGlyphs, int platformId, int encodingId)
        {
            // ReSharper disable UnusedVariable
            var length  = data.ReadUnsignedShort();
            var version = data.ReadUnsignedShort();
            // ReSharper restore UnusedVariable

            var subHeaderKeys         = new int[256];
            var maximumSubHeaderIndex = 0;

            for (var i = 0; i < 256; i++)
            {
                var value = data.ReadUnsignedShort();
                maximumSubHeaderIndex = Math.Max(maximumSubHeaderIndex, value / 8);
                subHeaderKeys[i]      = value;
            }

            var subHeaderCount = maximumSubHeaderIndex + 1;

            var subHeaders = new SubHeader[subHeaderCount];

            for (var i = 0; i < subHeaderCount; i++)
            {
                var firstCode     = data.ReadUnsignedShort();
                var entryCount    = data.ReadUnsignedShort();
                var idDelta       = data.ReadSignedShort();
                var idRangeOffset = data.ReadUnsignedShort() - (subHeaderCount - i - 1) * 8 - 2;
                subHeaders[i] = new SubHeader(firstCode, entryCount, idDelta, idRangeOffset);
            }

            var glyphIndexArrayOffset = data.Position;

            var characterCodeToGlyphId = new Dictionary <int, int>();

            for (var i = 0; i < subHeaderCount; i++)
            {
                var subHeader = subHeaders[i];

                data.Seek(glyphIndexArrayOffset + subHeader.IdRangeOffset);

                for (int j = 0; j < subHeader.EntryCount; j++)
                {
                    int characterCode = (i << 8) + (subHeader.FirstCode + j);

                    var p = data.ReadUnsignedShort();

                    if (p > 0)
                    {
                        p = (p + subHeader.IdDelta) % 65536;
                    }

                    if (p >= numberOfGlyphs)
                    {
                        continue;
                    }

                    characterCodeToGlyphId[characterCode] = p;
                }
            }

            return(new HighByteMappingCMapTable(platformId, encodingId, characterCodeToGlyphId));
        }
        public static BasicMaximumProfileTable Load(TrueTypeDataBytes data, TrueTypeHeaderTable table)
        {
            data.Seek(table.Offset);

            var version        = data.Read32Fixed();
            var numberOfGlyphs = data.ReadUnsignedShort();

            if (Math.Abs(version - 0.5) < float.Epsilon)
            {
                return(new BasicMaximumProfileTable(table, version, numberOfGlyphs));
            }

            var maxPoints            = data.ReadUnsignedShort();
            var maxContours          = data.ReadUnsignedShort();
            var maxCompositePoints   = data.ReadUnsignedShort();
            var maxCompositeContours = data.ReadUnsignedShort();

            var maxZones              = data.ReadUnsignedShort();
            var maxTwilightPoints     = data.ReadUnsignedShort();
            var maxStorage            = data.ReadUnsignedShort();
            var maxFunctionDefs       = data.ReadUnsignedShort();
            var maxInstructionDefs    = data.ReadUnsignedShort();
            var maxStackElements      = data.ReadUnsignedShort();
            var maxSizeOfInstructions = data.ReadUnsignedShort();
            var maxComponentElements  = data.ReadUnsignedShort();
            var maxComponentDepth     = data.ReadUnsignedShort();

            return(new MaximumProfileTable(table, version, numberOfGlyphs, maxPoints,
                                           maxContours, maxCompositePoints, maxCompositeContours, maxZones,
                                           maxTwilightPoints, maxStorage, maxFunctionDefs, maxInstructionDefs,
                                           maxStackElements, maxSizeOfInstructions, maxComponentElements,
                                           maxComponentDepth));
        }