예제 #1
0
        private static uint[] LoadIndexToLocation(FontSource p, uint tableOffset, ushort glyphCount, short indexToLocFormat)
        {
            p.Offset = tableOffset;

            var isLong = indexToLocFormat == 1;

            var n            = glyphCount + 1;
            var localOffsets = new uint[n];

            if (isLong)
            {
                for (var i = 0; i < n; i++)
                {
                    localOffsets[i] = p.ReadUInt();
                }
            }
            else
            {
                for (var i = 0; i < n; i++)
                {
                    localOffsets[i] = (uint)p.ReadUShort() * 2;
                }
            }

            return(localOffsets);
        }
예제 #2
0
        private static Dictionary <uint, uint> LoadSegmentedCoverage(FontSource p)
        {
            var dic = new Dictionary <uint, uint>();

            // Skip format
            p.ReadUShort();

            // Skip reserved
            p.ReadUShort();

            var length = p.ReadUInt();

            var language = p.ReadUInt();

            var numGroups = p.ReadUInt();

            for (var i = 0; i < numGroups; i++)
            {
                var startCharCode = p.ReadUInt();
                var endCharCode   = p.ReadUInt();
                var startGlyphId  = p.ReadUInt();

                for (var c = startCharCode; c <= endCharCode; c++)
                {
                    dic[c] = startGlyphId;
                    startGlyphId++;
                }
            }

            return(dic);
        }
예제 #3
0
        public static FontModel Load(FontSource source)
        {
            var font = new FontModel();

            var offsetTable = OffsetTable.Load(source);

            if (offsetTable.SfntVersion == 0x10000)
            {
                font.Type = FontType.TrueType;
            }
            else if (offsetTable.SfntVersion == 0x4f54544f)
            {
                font.Type = FontType.OpenType;
            }
            else
            {
                throw new FontException($"Unknown sfntVersion: {offsetTable.SfntVersion:X4}");
            }

            var tableOffsets = new Dictionary <string, uint>(offsetTable.NumTables);

            for (var i = 0; i < offsetTable.NumTables; i++)
            {
                var table = TableRecord.Load(source);
                tableOffsets[table.Tag] = table.Offset;
            }

            LoadFontHeader(source, tableOffsets["head"], out var indexToLocFormat, out var unitsPerEm);
            font.BaseScale  = 1f / unitsPerEm;
            font.UnitsPerEm = unitsPerEm;

            var glyphCount = LoadGlyphCount(source, tableOffsets["maxp"]);

            font._glyphs = new GlyphInfo[glyphCount];

            var numberOfHMetrics = LoadNumberOfHMetrics(source, tableOffsets["hhea"]);

            LoadHorizontalMetrics(font, source, tableOffsets["hmtx"], numberOfHMetrics, glyphCount);

            font._glyphIndexMap = LoadCharacterToGlyphIndexMappingTable(source, tableOffsets["cmap"]);

            if (tableOffsets.ContainsKey("loca"))
            {
                font._localOffsets = LoadIndexToLocation(source, tableOffsets["loca"], glyphCount, indexToLocFormat);
            }

            if (tableOffsets.ContainsKey("glyf"))
            {
                font._glyph = new GlyphTable(tableOffsets["glyf"]);
            }

            if (tableOffsets.ContainsKey("CFF"))
            {
                font._cff = CompactFontFormatTable.Load(tableOffsets["CFF"], source, font);
            }

            return(font);
        }
예제 #4
0
        private static Dictionary <uint, uint> LoadSegmentMappingToDeltaValues(FontSource p)
        {
            var dic = new Dictionary <uint, uint>();

            var offset = p.Offset;

            // Skip format
            p.ReadUShort();

            var length        = p.ReadUShort();
            var language      = p.ReadUShort();
            var segCount      = p.ReadUShort() >> 1;
            var searchRange   = p.ReadUShort();
            var entrySelector = p.ReadUShort();
            var rangeShift    = p.ReadUShort();

            var endCodeParser       = new FontSource(p.Bytes, offset + 14);
            var startCodeParser     = new FontSource(p.Bytes, (uint)(offset + 16 + segCount * 2));
            var idDeltaParser       = new FontSource(p.Bytes, (uint)(offset + 16 + segCount * 4));
            var idRangeOffsetParser = new FontSource(p.Bytes, (uint)(offset + 16 + segCount * 6));

            for (var i = 0; i < segCount - 1; i++)
            {
                var endCode       = endCodeParser.ReadUShort();
                var startCode     = startCodeParser.ReadUShort();
                var idDelta       = idDeltaParser.ReadShort();
                var idRangeOffset = idRangeOffsetParser.ReadUShort();

                for (var c = startCode; c <= endCode; c++)
                {
                    uint glyphIndex;
                    if (idRangeOffset != 0)
                    {
                        var glyphIndexOffset = idRangeOffsetParser.Offset - 2;
                        glyphIndexOffset += idRangeOffset;
                        glyphIndexOffset += (uint)(c - startCode) * 2;
                        p.Offset          = glyphIndexOffset;
                        glyphIndex        = p.ReadUShort();
                        if (glyphIndex != 0)
                        {
                            glyphIndex = (uint)(glyphIndex + idDelta) & 0xFFFF;
                        }
                    }
                    else
                    {
                        glyphIndex = (uint)(c + idDelta) & 0xFFFF;
                    }

                    dic[c] = glyphIndex;
                }
            }

            return(dic);
        }
예제 #5
0
                public static GlyphComponent Load(FontSource p)
                {
                    var gc    = new GlyphComponent();
                    var flags = p.ReadUShort();

                    gc.GlyphId = p.ReadUShort();

                    if ((flags & 1) > 0)
                    {
                        if ((flags & 2) > 0)
                        {
                            gc.DX = p.ReadShort();
                            gc.DY = p.ReadShort();
                        }
                        else
                        {
                            gc.MatchedPoints = new[] { p.ReadUShort(), p.ReadUShort() };
                        }
                    }
                    else
                    {
                        if ((flags & 2) > 0)
                        {
                            gc.DX = p.ReadSByte();
                            gc.DY = p.ReadSByte();
                        }
                        else
                        {
                            gc.MatchedPoints = new[] { p.ReadUShort(), p.ReadUShort() };
                        }
                    }

                    if ((flags & 8) > 0)
                    {
                        gc.XScale = gc.YScale = p.ReadF2Dot14();
                    }
                    else if ((flags & 64) > 0)
                    {
                        gc.XScale = p.ReadF2Dot14();
                        gc.YScale = p.ReadF2Dot14();
                    }
                    else if ((flags & 128) > 0)
                    {
                        gc.XScale  = p.ReadF2Dot14();
                        gc.Scale01 = p.ReadF2Dot14();
                        gc.Scale10 = p.ReadF2Dot14();
                        gc.YScale  = p.ReadF2Dot14();
                    }

                    gc.MoreComponents = (flags & 32) > 0;

                    return(gc);
                }
예제 #6
0
        public GlyphHeader LoadHeader(uint localOffset, FontSource source)
        {
            source.Offset = _offset + localOffset;

            return(new GlyphHeader(
                       source.ReadShort(),
                       source.ReadShort(),
                       source.ReadShort(),
                       source.ReadShort(),
                       source.ReadShort(),
                       source.Offset
                       ));
        }
예제 #7
0
        private static void LoadHorizontalMetrics(FontModel font, FontSource p, uint tableOffset, int numberOfHMetrics, int numGlyphs)
        {
            ushort advancedWidth   = 0;
            short  leftSideBearing = 0;

            p.Offset = tableOffset;
            for (var i = 0; i < numGlyphs; i++)
            {
                if (i < numberOfHMetrics)
                {
                    advancedWidth   = p.ReadUShort();
                    leftSideBearing = p.ReadShort();
                }
                font._glyphs[i] = new GlyphInfo(advancedWidth, leftSideBearing);
            }
        }
예제 #8
0
        public GlyphContourInfo GetGlyph(uint code, FontSource fontSource)
        {
            if (UnicodeUtility.IsControl(code))
            {
                throw new FontException($"Cannot get control characters: U+{code:X2}");
            }

            if (!_glyphIndexMap.ContainsKey(code))
            {
                throw new FontException($"Glyph not found: U+{code:X4}");
            }

            var gid = _glyphIndexMap[code];

            return(GetGlyphFromGid(gid, fontSource));
        }
예제 #9
0
        private static Dictionary <uint, uint> LoadCharacterToGlyphIndexMappingTable(FontSource p, uint tableOffset)
        {
            p.Offset = tableOffset;

            var version         = p.ReadUShort();
            var numTables       = p.ReadUShort();
            var encodingRecords = new EncodingRecord[numTables];

            for (var i = 0; i < numTables; i++)
            {
                encodingRecords[i] = EncodingRecord.Load(p, tableOffset);
            }

            var targetRecordId = -1;

            for (var i = numTables - 1; i >= 0; i--)
            {
                var r = encodingRecords[i];
                if (r.PlatformID == 3 && (r.EncodingID == 0 || r.EncodingID == 1 || r.EncodingID == 10) ||
                    r.PlatformID == 0 && (r.EncodingID == 0 || r.EncodingID == 1 || r.EncodingID == 2 || r.EncodingID == 3 || r.EncodingID == 4))
                {
                    targetRecordId = i;
                    break;
                }
            }

            if (targetRecordId < 0)
            {
                throw new FontException("Encoding record not found.");
            }

            var targetRecord = encodingRecords[targetRecordId];

            if (targetRecord.SubTableFormat == 12)
            {
                p.Offset = tableOffset + targetRecord.Offset;
                return(LoadSegmentedCoverage(p));
            }

            if (targetRecord.SubTableFormat == 4)
            {
                p.Offset = tableOffset + targetRecord.Offset;
                return(LoadSegmentMappingToDeltaValues(p));
            }

            throw new FontException($"Unsupported format: {targetRecord.SubTableFormat}");
        }
예제 #10
0
            public GlyphComponent[] GetGlyphComponents(FontSource source)
            {
                source.Offset = Offset;

                var list = new List <GlyphComponent>();

                var limit = 10;

                while (limit-- > 0)
                {
                    var component = GlyphComponent.Load(source);
                    list.Add(component);
                    if (!component.MoreComponents)
                    {
                        break;
                    }
                }

                return(list.ToArray());
            }
예제 #11
0
        public GlyphContourInfo GetGlyphFromGid(uint gid, FontSource fontSource)
        {
            if (Type == FontType.TrueType)
            {
                var gcd = new GlyphContourInfo(_glyphs[gid]);
                if (HasLocalOffset(gid))
                {
                    gcd.Header = _glyph.LoadHeader(_localOffsets[gid], fontSource);
                }
                return(gcd);
            }

            if (Type == FontType.OpenType)
            {
                var gcd = new GlyphContourInfo(_glyphs[gid]);
                gcd.Path = CffCharString.Load(gid, this, fontSource.Bytes);
                return(gcd);
            }

            return(null);
        }
예제 #12
0
        private static void LoadFontHeader(FontSource p, uint tableOffset, out short indexToLocFormat, out ushort unitsPerEm)
        {
            p.Offset = tableOffset;

            p.ReadUShort();
            p.ReadUShort();
            p.ReadFixed();
            p.ReadUInt();
            p.ReadUInt();
            p.ReadUShort();
            unitsPerEm = p.ReadUShort();
            p.Offset  += 8 * 2;
            p.ReadShort();
            p.ReadShort();
            p.ReadShort();
            p.ReadShort();
            p.ReadUShort();
            p.ReadUShort();
            p.ReadShort();
            indexToLocFormat = p.ReadShort();
            p.ReadShort();
        }
예제 #13
0
            public       GlyphPoint[] GetGlyphPoints(FontSource source)
            {
                source.Offset = Offset;
                var endPtsOfContours = new ushort[NumberOfContours];

                for (var i = 0; i < NumberOfContours; i++)
                {
                    endPtsOfContours[i] = source.ReadUShort();
                }

                //instructionLength = data.ReadUShort();
                //instructions = new byte[instructionLength];
                //for (var i = 0; i < instructionLength; i++) instructions[i] = data.ReadByte();

                // Ignore instructions.
                var instructionLength = source.ReadUShort();

                source.Offset += instructionLength;

                var numberOfCoordinates = endPtsOfContours[endPtsOfContours.Length - 1] + 1;
                var points = new List <GlyphPoint>(numberOfCoordinates);

                for (var i = 0; i < numberOfCoordinates; i++)
                {
                    var flag       = source.ReadByte();
                    var glyphPoint = new GlyphPoint(flag);
                    points.Add(glyphPoint);
                    if (glyphPoint.IsRepeat)
                    {
                        var count = source.ReadByte();
                        for (var k = 0; k < count; k++)
                        {
                            points.Add(new GlyphPoint(flag));
                            i++;
                        }
                    }
                }

                foreach (var ep in endPtsOfContours)
                {
                    points[ep].IsEndPoint = true;
                }

                short xSum = 0;
                short ySum = 0;

                // Read X
                foreach (var point in points)
                {
                    var flag     = point.Flag;
                    var isShortX = GetFlag(flag, 1);
                    var isSameX  = GetFlag(flag, 4);
                    if (isShortX)
                    {
                        if (isSameX)
                        {
                            xSum += source.ReadByte();
                        }
                        else
                        {
                            xSum += (short)-source.ReadByte();
                        }
                    }
                    else
                    {
                        if (!isSameX)
                        {
                            xSum += source.ReadShort();
                        }
                    }

                    point.X = xSum;
                }

                // Read Y
                foreach (var point in points)
                {
                    var flag     = point.Flag;
                    var isShortY = GetFlag(flag, 2);
                    var isSameY  = GetFlag(flag, 5);
                    if (isShortY)
                    {
                        if (isSameY)
                        {
                            ySum += source.ReadByte();
                        }
                        else
                        {
                            ySum += (short)-source.ReadByte();
                        }
                    }
                    else
                    {
                        if (!isSameY)
                        {
                            ySum += source.ReadShort();
                        }
                    }

                    point.Y = ySum;
                }

                return(points.ToArray());
            }
예제 #14
0
 private static ushort LoadGlyphCount(FontSource p, uint tableOffset)
 {
     p.Offset = tableOffset + 4;
     return(p.ReadUShort());
 }
예제 #15
0
        public static GlyphContourInfoV2 ToVector2(this GlyphContourInfo g, FontModel model, FontSource source)
        {
            switch (model.Type)
            {
            case FontModel.FontType.TrueType:
                return(FromTtfGlyph(g, model, source));

            case FontModel.FontType.OpenType:
                return(FromOtfGlyph(g, model, source));

            default:
                throw new InvalidEnumArgumentException(model.Type.ToString());
            }
        }
예제 #16
0
        private static GlyphContourInfoV2 FromTtfGlyph(GlyphContourInfo g, FontModel model, FontSource source)
        {
            var advancedWidth   = g.BaseInfo.AdvancedWidth * model.BaseScale;
            var leftSideBearing = g.BaseInfo.LeftSideBearing * model.BaseScale;

            if (g.Header.NumberOfContours > 0)
            {
                var con = new List <List <ContourPoint> >();

                var points = g.Header.GetGlyphPoints(source);
                if (points == null || points.Length == 0)
                {
                    throw new FontException("Cannot get points");
                }

                var list = new List <ContourPoint>();
                con.Add(list);
                for (var i = 0; i < points.Length; i++)
                {
                    var point        = points[i];
                    var contourPoint = new ContourPoint(point.X, point.Y, point.OnCurve);
                    list.Add(contourPoint);

                    if (point.IsEndPoint && i != points.Length - 1)
                    {
                        list = new List <ContourPoint>();
                        con.Add(list);
                    }
                }

                return(new GlyphContourInfoV2(advancedWidth, leftSideBearing, ContourToVector(con, model.BaseScale, true)));
            }
            else
            {
                // Composite Glyph
                var components = g.Header.GetGlyphComponents(source);

                var con = new List <List <ContourPoint> >();

                foreach (var component in components)
                {
                    var cg = model.GetGlyphFromGid(component.GlyphId, source);
                    if (cg.Header.NumberOfContours > 0)
                    {
                        var points = cg.Header.GetGlyphPoints(source);
                        if (points == null || points.Length == 0)
                        {
                            throw new FontException("Cannot get points");
                        }

                        if (component.MatchedPoints == null)
                        {
                            GlyphTable.GlyphHeader.GlyphComponent.TransformPoints(points, component);
                        }
                        else
                        {
                            // MatchedPoints are not supported.
                            break;
                        }

                        var list = new List <ContourPoint>();
                        con.Add(list);
                        for (var i = 0; i < points.Length; i++)
                        {
                            var point = points[i];

                            var contourPoint = new ContourPoint(point.X, point.Y, point.OnCurve);
                            list.Add(contourPoint);

                            if (point.IsEndPoint && i != points.Length - 1)
                            {
                                list = new List <ContourPoint>();
                                con.Add(list);
                            }
                        }
                    }
                }
                return(new GlyphContourInfoV2(advancedWidth, leftSideBearing, ContourToVector(con, model.BaseScale, true)));
            }
        }
예제 #17
0
        private static GlyphContourInfoV2 FromOtfGlyph(GlyphContourInfo g, FontModel model, FontSource source)
        {
            var advancedWidth   = g.BaseInfo.AdvancedWidth * model.BaseScale;
            var leftSideBearing = g.BaseInfo.LeftSideBearing * model.BaseScale;

            var contours = new List <List <ContourPoint> >();
            List <ContourPoint> contour = null;

            foreach (var cp in g.Path.Points)
            {
                if (cp.isContour)
                {
                    contour = new List <ContourPoint>();
                    contours.Add(contour);
                }

                if (cp.isCurve)
                {
                    contour.Add(new ContourPoint((short)cp.x, (short)cp.y, false));
                }
                else
                {
                    contour.Add(new ContourPoint((short)cp.x, (short)cp.y, true));
                }
            }

            foreach (var c in contours)
            {
                var first = c[0];
                var last  = c[c.Count - 1];
                if (first.X == last.X && first.Y == last.Y)
                {
                    c.RemoveAt(c.Count - 1);
                }
            }

            return(new GlyphContourInfoV2(advancedWidth, leftSideBearing, ContourToVector(contours, model.BaseScale, false)));
        }
예제 #18
0
 public GlyphContourInfo GetNotDefGlyph(FontSource fontSource)
 {
     return(GetGlyphFromGid(0, fontSource));
 }
예제 #19
0
 private static ushort LoadNumberOfHMetrics(FontSource p, uint tableOffset)
 {
     p.Offset = tableOffset + 34;
     return(p.ReadUShort());
 }