Exemplo n.º 1
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));
        }
Exemplo n.º 2
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));
        }
Exemplo n.º 3
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));
        }
Exemplo n.º 4
0
        public static GlyphDataTable Load(TrueTypeDataBytes data, TrueTypeHeaderTable table, TableRegister.Builder tableRegister)
        {
            data.Seek(table.Offset);

            var bytes = data.ReadByteArray((int)table.Length);

            return(new GlyphDataTable(table, tableRegister.IndexToLocationTable.GlyphOffsets,
                                      tableRegister.HeaderTable.Bounds,
                                      new TrueTypeDataBytes(bytes)));
        }
Exemplo n.º 5
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));
        }
Exemplo n.º 6
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));
        }
Exemplo n.º 7
0
        /// <summary>
        /// Creates a new glyph table from the input font which contains only the glyphs required by the input mapping.
        /// </summary>
        /// <param name="font">The font used to create this subset.</param>
        /// <param name="fontBytes">The raw bytes of the input font.</param>
        /// <param name="mapping">The mapping of old glyph indices to new glyph indices.</param>
        /// <returns>A new glyph table and associated information for use in creating a valid TrueType font file.</returns>
        public static TrueTypeSubsetGlyphTable SubsetGlyphTable(TrueTypeFont font, byte[] fontBytes, IndexMap[] mapping)
        {
            if (font == null)
            {
                throw new ArgumentNullException(nameof(font));
            }

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

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

            var data = new TrueTypeDataBytes(fontBytes);
            var advanceWidthTable = font.TableRegister.HorizontalMetricsTable;

            if (advanceWidthTable == null)
            {
                throw new InvalidFontFormatException($"Font: {font} did not contain a horizontal metrics table, cannot subset.");
            }

            var existingGlyphs = GetGlyphRecordsInFont(font, data);

            var glyphsToCopy = new List <GlyphRecord>(mapping.Length);
            var glyphsToCopyOriginalIndex = new List <int>(mapping.Length);

            // Extract the glyphs required for this subset from the original table.
            for (var i = 0; i < mapping.Length; i++)
            {
                var map    = mapping[i];
                var record = existingGlyphs[map.OldIndex];

                glyphsToCopy.Add(record);
                glyphsToCopyOriginalIndex.Add(map.OldIndex);
            }

            var glyphLocations = new List <uint>();
            var advanceWidths  = new List <HorizontalMetric>();

            var compositeIndicesToReplace = new List <(uint offset, ushort newIndex)>();

            using (var stream = new MemoryStream())
            {
                for (var i = 0; i < glyphsToCopy.Count; i++)
                {
                    compositeIndicesToReplace.Clear();

                    var newRecord = glyphsToCopy[i];

                    if (newRecord.Type == GlyphType.Composite)
                    {
                        // Any glyphs a composite glyph depends on must also be included.
                        for (var j = 0; j < newRecord.DependencyIndices.Count; j++)
                        {
                            // Get the indices of the dependency glyphs from the original font file.
                            var dependency = newRecord.DependencyIndices[j];

                            // If the dependency has already been included we can skip copying it again.
                            var newDependencyIndex = GetAlreadyCopiedDependencyIndex(dependency, glyphsToCopyOriginalIndex);

                            if (!newDependencyIndex.HasValue)
                            {
                                // Else we need to copy the dependency glyph from the original.
                                var actualDependencyRecord = existingGlyphs[dependency.Index];

                                // We need to add it to the set of glyphs to copy.
                                newDependencyIndex = glyphsToCopy.Count;
                                glyphsToCopy.Add(actualDependencyRecord);
                                glyphsToCopyOriginalIndex.Add((int)dependency.Index);
                            }

                            var withinGlyphDataIndexOffset = dependency.OffsetOfIndexWithinData - newRecord.Offset;

                            compositeIndicesToReplace.Add(((uint)withinGlyphDataIndexOffset, (ushort)newDependencyIndex));
                        }
                    }

                    // Record the glyph location.
                    glyphLocations.Add((uint)stream.Position);

                    var advanceWidth = advanceWidthTable.HorizontalMetrics[glyphsToCopyOriginalIndex[i]];
                    advanceWidths.Add(advanceWidth);

                    if (newRecord.Type == GlyphType.Empty)
                    {
                        // TODO: if this is the last glyph this might be a problem.
                        continue;
                    }

                    data.Seek(newRecord.Offset);

                    var glyphBytes = data.ReadByteArray(newRecord.DataLength);

                    // Update any indices referenced by composite glyphs to match the new index of the dependency.
                    foreach (var(offset, newIndex) in compositeIndicesToReplace)
                    {
                        glyphBytes[offset]     = (byte)(newIndex >> 8);
                        glyphBytes[offset + 1] = (byte)newIndex;
                    }

                    stream.Write(glyphBytes, 0, glyphBytes.Length);

                    // Each glyph description must start at a 4 byte boundary.
                    var remainder  = glyphBytes.Length % 4;
                    var bytesToPad = remainder == 0 ? 0 : 4 - remainder;
                    for (var j = 0; j < bytesToPad; j++)
                    {
                        stream.WriteByte(0);
                    }
                }

                var output = stream.ToArray();

                glyphLocations.Add((uint)output.Length);
                var offsets = glyphLocations.ToArray();

                return(new TrueTypeSubsetGlyphTable(output, offsets, advanceWidths.ToArray()));
            }
        }