Ejemplo n.º 1
0
        public int Lookup(CodePoint codePoint)
        {
            int index;

            if (table.TryGetValue(codePoint, out index))
            {
                return(index);
            }
            return(-1);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Gets glyph data for a specific character.
        /// </summary>
        /// <param name="codePoint">The Unicode codepoint for which to retrieve glyph data.</param>
        /// <param name="pixelSize">The desired size of the font, in pixels.</param>
        /// <returns>The glyph data if the font supports the given character; otherwise, <c>null</c>.</returns>
        public Glyph GetGlyph(CodePoint codePoint, float pixelSize)
        {
            var glyphIndex = charMap.Lookup(codePoint);

            if (glyphIndex < 0)
            {
                return(null);
            }

            // set up the control value table
            var scale = ComputeScale(pixelSize);

            interpreter.SetControlValueTable(controlValueTable, scale, pixelSize, prepProgram);

            // get metrics
            var glyph      = glyphs[glyphIndex];
            var horizontal = hmetrics[glyphIndex];
            var vtemp      = vmetrics?[glyphIndex];

            if (vtemp == null)
            {
                var synth = verticalSynthesized;
                synth.FrontSideBearing -= glyph.MaxY;
                vtemp = synth;
            }
            var vertical = vtemp.GetValueOrDefault();

            // build and transform the glyph
            var points    = new List <PointF>(32);
            var contours  = new List <int>(32);
            var transform = Matrix3x2.CreateScale(scale);

            Geometry.ComposeGlyphs(glyphIndex, 0, ref transform, points, contours, glyphs);

            // add phantom points; these are used to define the extents of the glyph,
            // and can be modified by hinting instructions
            var pp1 = new Point((FUnit)(glyph.MinX - horizontal.FrontSideBearing), (FUnit)0);
            var pp2 = new Point(pp1.X + (FUnit)horizontal.Advance, (FUnit)0);
            var pp3 = new Point((FUnit)0, (FUnit)(glyph.MaxY + vertical.FrontSideBearing));
            var pp4 = new Point((FUnit)0, pp3.Y - (FUnit)vertical.Advance);

            points.Add(pp1 * scale);
            points.Add(pp2 * scale);
            points.Add(pp3 * scale);
            points.Add(pp4 * scale);

            // hint the glyph's points
            var pointArray   = points.ToArray();
            var contourArray = contours.ToArray();

            interpreter.HintGlyph(pointArray, contourArray, glyphs[glyphIndex].Instructions);

            return(new Glyph(renderer, pointArray, contourArray, horizontal.Advance * scale));
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Gets kerning information for a pair of characters.
        /// </summary>
        /// <param name="left">The left character.</param>
        /// <param name="right">The right character.</param>
        /// <param name="pixelSize">The size of the font, in pixels.</param>
        /// <returns>The amount of kerning to apply, if any.</returns>
        public float GetKerning(CodePoint left, CodePoint right, float pixelSize)
        {
            if (kernTable == null)
            {
                return(0.0f);
            }

            var leftIndex  = charMap.Lookup(left);
            var rightIndex = charMap.Lookup(right);

            if (leftIndex < 0 || rightIndex < 0)
            {
                return(0.0f);
            }

            var kern = kernTable.Lookup(leftIndex, rightIndex);

            return(kern * ComputeScale(pixelSize));
        }
Ejemplo n.º 4
0
        public void AppendText(char *text, int count, TextFormat format)
        {
            // look up the cache entry for the given font and size
            CachedFace cachedFace;
            var        font = format.Font;
            var        size = FontFace.ComputePixelSize(format.Size, Dpi);
            var        key  = new CacheKey(font.Id, size);

            if (!cache.TryGetValue(key, out cachedFace))
            {
                cache.Add(key, cachedFace = new CachedFace(font, size));
            }

            // process each character in the string
            var   nextBreak = BreakCategory.None;
            var   previous  = new CodePoint();
            char *end       = text + count;

            while (text != end)
            {
                // handle surrogate pairs properly
                CodePoint codePoint;
                char      c = *text++;
                if (char.IsSurrogate(c) && text != end)
                {
                    codePoint = new CodePoint(c, *text++);
                }
                else
                {
                    codePoint = c;
                }

                // ignore linefeeds directly after a carriage return
                if (c == '\n' && (char)previous == '\r')
                {
                    continue;
                }

                // get the glyph data
                CachedGlyph glyph;
                if (!cachedFace.Glyphs.TryGetValue(codePoint, out glyph) && !char.IsControl(c))
                {
                    var data   = font.GetGlyph(codePoint, size);
                    var width  = data.RenderWidth;
                    var height = data.RenderHeight;
                    if (width > atlas.Width || height > atlas.Height)
                    {
                        throw new InvalidOperationException("Glyph is larger than the size of the provided atlas.");
                    }

                    var rect = new Rect();
                    if (width > 0 && height > 0)
                    {
                        // render the glyph
                        var memSize = width * height;
                        var mem     = memoryBuffer;
                        if (mem == null)
                        {
                            memoryBuffer = mem = new MemoryBuffer(memSize);
                        }

                        mem.Clear(memSize);
                        data.RenderTo(new Surface {
                            Bits   = mem.Pointer,
                            Width  = width,
                            Height = height,
                            Pitch  = width
                        });

                        // save the rasterized glyph in the user's atlas
                        rect = packer.Insert(width, height);
                        if (rect.Height == 0)
                        {
                            // didn't fit in the atlas... start a new sheet
                            currentPage++;
                            packer.Clear(atlas.Width, atlas.Height);
                            rect = packer.Insert(width, height);
                            if (rect.Height == 0)
                            {
                                throw new InvalidOperationException("Failed to insert glyph into fresh page.");
                            }
                        }
                        atlas.Insert(currentPage, rect.X, rect.Y, rect.Width, rect.Height, mem.Pointer);
                    }

                    glyph = new CachedGlyph(rect, data.HorizontalMetrics.Bearing, data.HorizontalMetrics.Advance);
                    cachedFace.Glyphs.Add(codePoint, glyph);
                }

                // check for a kerning offset
                var kerning = font.GetKerning(previous, codePoint, size);
                previous = codePoint;

                // figure out whether this character can serve as a line break point
                // TODO: more robust character class handling
                var breakCategory = BreakCategory.None;
                if (char.IsWhiteSpace(c))
                {
                    if (c == '\r' || c == '\n')
                    {
                        breakCategory = BreakCategory.Mandatory;
                    }
                    else
                    {
                        breakCategory = BreakCategory.Opportunity;
                    }
                }

                // the previous character might make us think that this one should be a break opportunity
                if (nextBreak > breakCategory)
                {
                    breakCategory = nextBreak;
                }
                if (c == '-')
                {
                    nextBreak = BreakCategory.Opportunity;
                }

                // alright, we have all the right glyph data cached and loaded
                // append relevant info to our buffer; we'll do the actual layout later
                buffer.Add(new BufferEntry {
                    GlyphData = glyph,
                    Kerning   = kerning,
                    Break     = breakCategory
                });
            }
        }