public int Lookup(CodePoint codePoint) { int index; if (table.TryGetValue(codePoint, out index)) { return(index); } return(-1); }
/// <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)); }
/// <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)); }
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 }); } }