Example #1
0
        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));
        }
        private static GlyphRecord[] GetGlyphRecordsInFont(TrueTypeFont font, TrueTypeDataBytes data)
        {
            var indexToLocationTable = font.TableRegister.IndexToLocationTable;

            var numGlyphs = indexToLocationTable.GlyphOffsets.Count - 1;

            var glyphDirectory = font.TableRegister.GlyphTable.DirectoryTable;

            data.Seek(glyphDirectory.Offset);

            var glyphRecords = new GlyphRecord[numGlyphs];

            for (var i = 0; i < numGlyphs; i++)
            {
                var glyphOffset = (int)(glyphDirectory.Offset + indexToLocationTable.GlyphOffsets[i]);

                if (indexToLocationTable.GlyphOffsets[i + 1] <= indexToLocationTable.GlyphOffsets[i])
                {
                    glyphRecords[i] = new GlyphRecord(glyphOffset);
                    continue;
                }

                data.Seek(glyphOffset);

                if (glyphOffset >= glyphDirectory.Offset + glyphDirectory.Length)
                {
                    throw new InvalidOperationException($"Failed to read expected number of glyphs {numGlyphs}, only got to index {i} before reaching end of input.");
                }

                var numberOfContours = data.ReadSignedShort();
                var type             = numberOfContours >= 0 ? GlyphType.Simple : GlyphType.Composite;

                // Read bounds.
                data.ReadSignedShort();
                data.ReadSignedShort();
                data.ReadSignedShort();
                data.ReadSignedShort();

                if (type == GlyphType.Simple)
                {
                    ReadSimpleGlyph(data, numberOfContours);
                    glyphRecords[i] = new GlyphRecord(glyphOffset, type, (int)(data.Position - glyphOffset));
                }
                else
                {
                    var glyphIndices = ReadCompositeGlyph(data);

                    // Skip hinting instructions but include them in the output.
                    var next = indexToLocationTable.GlyphOffsets[i + 1];
                    data.Seek(glyphDirectory.Offset + next - 1);

                    glyphRecords[i] = new GlyphRecord(glyphOffset, type, (int)(data.Position - glyphOffset), glyphIndices);
                }
            }

            return(glyphRecords);
        }
Example #3
0
        public static GlyphDataTable Load(TrueTypeDataBytes data, TrueTypeHeaderTable table, TableRegister.Builder tableRegister)
        {
            data.Seek(table.Offset);

            var indexToLocationTable = tableRegister.IndexToLocationTable;

            var offsets = indexToLocationTable.GlyphOffsets;

            var entryCount = offsets.Length;

            var glyphCount = entryCount - 1;

            var glyphs = new IGlyphDescription[glyphCount];

            var emptyGlyph = Glyph.Empty(tableRegister.HeaderTable.Bounds);

            var compositeLocations = new Dictionary <int, TemporaryCompositeLocation>();

            for (var i = 0; i < glyphCount; i++)
            {
                if (offsets[i] == offsets[i + 1])
                {
                    // empty glyph
                    glyphs[i] = emptyGlyph;
                    continue;
                }

                data.Seek(offsets[i] + table.Offset);

                var contourCount = data.ReadSignedShort();

                var minX = data.ReadSignedShort();
                var minY = data.ReadSignedShort();
                var maxX = data.ReadSignedShort();
                var maxY = data.ReadSignedShort();

                var bounds = new PdfRectangle(minX, minY, maxX, maxY);

                // If the number of contours is greater than or equal zero it's a simple glyph.
                if (contourCount >= 0)
                {
                    glyphs[i] = ReadSimpleGlyph(data, contourCount, bounds);
                }
                else
                {
                    compositeLocations.Add(i, new TemporaryCompositeLocation(data.Position, bounds, contourCount));
                }
            }

            // Build composite glyphs by combining simple and other composite glyphs.
            foreach (var compositeLocation in compositeLocations)
            {
                glyphs[compositeLocation.Key] = ReadCompositeGlyph(data, compositeLocation.Value, compositeLocations, glyphs, emptyGlyph);
            }

            return(new GlyphDataTable(table, glyphs));
        }
Example #4
0
        private static short[] ReadCoordinates(TrueTypeDataBytes data, int pointCount, SimpleGlyphFlags[] flags, SimpleGlyphFlags isByte, SimpleGlyphFlags signOrSame)
        {
            var xs = new short[pointCount];
            var x  = 0;

            for (var i = 0; i < pointCount; i++)
            {
                int dx;
                if (flags[i].HasFlag(isByte))
                {
                    var b = data.ReadByte();
                    dx = flags[i].HasFlag(signOrSame) ? b : -b;
                }
                else
                {
                    if (flags[i].HasFlag(signOrSame))
                    {
                        dx = 0;
                    }
                    else
                    {
                        dx = data.ReadSignedShort();
                    }
                }

                x += dx;

                xs[i] = (short)x;
            }

            return(xs);
        }
Example #5
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));
        }
Example #6
0
        public static GlyphDataTable Load(TrueTypeDataBytes data, TrueTypeHeaderTable table, TableRegister tableRegister)
        {
            data.Seek(table.Offset);

            var headerTable          = tableRegister.HeaderTable;
            var indexToLocationTable = tableRegister.IndexToLocationTable;

            var offsets = indexToLocationTable.GlyphOffsets;

            var entryCount = offsets.Length;

            var glyphCount = entryCount - 1;

            var glyphs = new IGlyphDescription[glyphCount];

            for (var i = 0; i < glyphCount; i++)
            {
                if (offsets[i] == offsets[i + 1])
                {
                    // empty glyph
                    continue;
                }

                data.Seek(offsets[i] + table.Offset);

                var contourCount = data.ReadSignedShort();

                var minX = data.ReadSignedShort();
                var minY = data.ReadSignedShort();
                var maxX = data.ReadSignedShort();
                var maxY = data.ReadSignedShort();

                var bounds = new PdfRectangle(minX, minY, maxX, maxY);

                // If the number of contours is greater than or equal zero it's a simple glyph.
                if (contourCount >= 0)
                {
                    glyphs[i] = ReadSimpleGlyph(data, contourCount, bounds);
                }
                else
                {
                }
            }

            return(new GlyphDataTable(table, glyphs));
        }
        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);
        }
Example #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
        }
Example #9
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));
        }
Example #10
0
        public static PostScriptTable Load(TrueTypeDataBytes data, TrueTypeHeaderTable table, BasicMaximumProfileTable maximumProfileTable)
        {
            data.Seek(table.Offset);
            var formatType         = data.Read32Fixed();
            var italicAngle        = data.Read32Fixed();
            var underlinePosition  = data.ReadSignedShort();
            var underlineThickness = data.ReadSignedShort();
            var isFixedPitch       = data.ReadUnsignedInt();
            var minMemType42       = data.ReadUnsignedInt();
            var maxMemType42       = data.ReadUnsignedInt();
            var mimMemType1        = data.ReadUnsignedInt();
            var maxMemType1        = data.ReadUnsignedInt();

            var glyphNames = GetGlyphNamesByFormat(data, maximumProfileTable, formatType);

            return(new PostScriptTable(table, (decimal)formatType, (decimal)italicAngle,
                                       underlinePosition, underlineThickness, isFixedPitch,
                                       minMemType42, maxMemType42, mimMemType1,
                                       maxMemType1, glyphNames));
        }
        private static short[] ReadCoordinates(SimpleGlyphFlags[] flags, TrueTypeDataBytes data,
                                               SimpleGlyphFlags isSingleByte, SimpleGlyphFlags isTheSameAsPrevious)
        {
            bool HasFlag(SimpleGlyphFlags set, SimpleGlyphFlags f)
            {
                return((set & f) != 0);
            }

            var coordinates = new short[flags.Length];
            var value       = 0;

            for (var i = 0; i < flags.Length; i++)
            {
                var flag = flags[i];
                if (HasFlag(flag, isSingleByte))
                {
                    var b = data.ReadByte();

                    // If SingleByte is set, the IsTheSame flag describes the sign of the value,
                    // with a value of 1 equalling positive and a zero value negative.
                    if (HasFlag(flag, isTheSameAsPrevious))
                    {
                        value += b;
                    }
                    else
                    {
                        value -= b;
                    }
                }
                else
                {
                    short delta;

                    // If this flag is set, then the current coordinate is the same as the previous coordinate.
                    if (HasFlag(flag, isTheSameAsPrevious))
                    {
                        delta = 0;
                    }
                    else
                    {
                        // If the IsTheSame flag is not set, the current coordinate is a signed 16-bit delta vector.
                        delta = data.ReadSignedShort();
                    }

                    value += delta;
                }

                coordinates[i] = (short)value;
            }

            return(coordinates);
        }
Example #12
0
        private static short[] ReadCoordinates(TrueTypeDataBytes data, int pointCount, SimpleGlyphFlags[] flags, SimpleGlyphFlags isByte, SimpleGlyphFlags signOrSame)
        {
            bool HasFlag(SimpleGlyphFlags value, SimpleGlyphFlags target)
            {
                return((value & target) == target);
            }

            var xs = new short[pointCount];
            var x  = 0;

            for (var i = 0; i < pointCount; i++)
            {
                var flag = flags[i];
                int dx;
                if (HasFlag(flag, isByte))
                {
                    var b = data.ReadByte();
                    dx = HasFlag(flag, signOrSame) ? b : -b;
                }
                else
                {
                    if (HasFlag(flag, signOrSame))
                    {
                        dx = 0;
                    }
                    else
                    {
                        dx = data.ReadSignedShort();
                    }
                }

                x += dx;

                xs[i] = (short)x;
            }

            return(xs);
        }
Example #13
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));
        }
Example #14
0
        private static decimal ReadTwoFourteenFormat(TrueTypeDataBytes data)
        {
            const decimal divisor = 1 << 14;

            return(data.ReadSignedShort() / divisor);
        }
Example #15
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));
        }
Example #16
0
        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 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));
        }
Example #18
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));
        }