sixlabors / fonts Goto Github PK
View Code? Open in Web Editor NEW:black_nib: Font loading and layout library.
Home Page: https://sixlabors.com/products/fonts
License: Other
:black_nib: Font loading and layout library.
Home Page: https://sixlabors.com/products/fonts
License: Other
var style = new FontSpan(f, 72) { WrappingWidth = targetWidth };
s = measurer.MeasureText(text, style);
This is the code from the example you gave before for doing "Scaled Word Wrap"
but this no longer works in the latest versions
Is there a new way to achieve the same as above?
Previous issue where you gave the example code
layout wrapping support #4
Attempting to measure a string which contains only whitespace results in an InvalidOperationException.
I´m using beta 02.
When I try to render a font to a image by rendering each character individually (to create a bitmap font), the text measurer causes the characters to missalign.
The results of this code look like this to me:
The problem is not fixed by using the MeasureBounds method and using the Right property as the width.
Is this a missconception in my code or a possible bug?
Edit: it was a logical mistake
When using SystemFontCollection, On This Line, it'll try to look for fonts using the *.ttf filter.
That will fail on Unix systems if the font file has a *.TTF extension (uppercase) instead, since its has a case sensitive file system.
Alternatively, replace that line with
IEnumerable<string> files =
found.SelectMany(x => Directory.EnumerateFiles(x, "*.*", SearchOption.AllDirectories))
.Where(x => Path.GetExtension(x).Equals(".ttf", StringComparison.OrdinalIgnoreCase));
So its more reliable on platforms other than windows.
edited to perma-link the source line reference
Add a return value to IGlyphRenderer.BeginGlyph
that can return true or false.. If true do not continue rendering the glyph and instead immediately call EndGlyph
.
So I am doing image.DrawText();
and I get this exception from some reason,
Exception: Method not found: 'Void SixLabors.Fonts.IGlyphRenderer.BeginGlyph(SixLabors.Primitives.RectangleF)'.
I used breakpoints and it runs fine until DrawText(). I am not sure how I would solve this issue.
At the moment there are 2 types of FontCollection
s the public one and a static SystemFontCollection
SystemFontCollection
uses a FontFileInstance to limit the up font loading required when accessing it the first time. instead of loading in all the font glyph data up front it loads the FontDescription
first and then only as any glyph's data is first requested do we load in the full font details.
We should update FontCollection
so that when you cal install with a file path then instead of loading all the glyph data up front then instead load in the data using the FileFontInstance
class so lazy load it instead, thus you can new up a font collection and on startup load all your fonts into the collection with a reduced overhead and only load in the full glyphs data as required,
update IGlyphRenderer
so the BeginGlyph()
will be passed the location/glyph metadata at the start of the rendering pass.
This will allow 3rd parties to provide a version of IGlyphRenderer
that (based on the location) could skew/transform subsequent points.
This would enable those 3rd parties to be able to perform an effect like rendering text along a path by applying rotation and translations based on the X/Y coordinate generated by the Layout Engine.
support font kerning
Support for font files with multiple variants.
https://learn.microsoft.com/en-us/typography/opentype/spec/otvaroverview
Provide a mechanisum for marking up spans of the supplied text with different formats allowing for simple rich text support
i.e. The quick brown fox jumps over the lazy dog
would use the below proposed font style
new SpanningFontStyle(
new Span(5, 9){ bold = true; },
new Span(28,31){ italic = true; },
);
originally posted by @CDDelta at SixLabors/ImageSharp#375
When wrapping text with spaces to a new line DrawText() starts the next line with a space.
Draw text (eg. "TEST TEST TEST TEST") that includes spaces and enable wrapping.
Output and view it.
Here is an example I made.
When the TabWidth is set to 0, any line containing a tab will be cut off following the tab.
pass in a rectangle into the layout engine and it should auto wrap text at the provided width.
Any plans to add support for the WOFF2 format?
I have an issue when i use words with accents on azure app service.
When i use word with accent, like á é í ó ú ç ã õ, i receive an error:
Object reference not set to an instance of an object
i try to use font family like Arial and Comic Sans, and problem still happen.
if i remove all accents, all error desapear.
export underline while rendering if enabled
where each character it skewed/transformed in relation to the angle of intersection along the path
When rendering a string that contains more than 3 tabs, only the first 3 are rendered. I haven't found out completely how it works yet, but that seems to be the general gist of it. The easiest way to reproduce the bug is comparing how the strings "\t\t\tx" and "\t\t\t\tx" get rendered.
Option to provide vertical align options to render to change the logic about what local means, does it refer to top middle or bottom of the final glyphs.
Add cross platform system for enumerating all the fonts installed on a users machine and expose them as a FontCollection
.
Most OSs have a standard location where fonts will be installed
depend on #13
To support the full Unicode range, the character representation should map to a Unicode codepoint which is 32-bit. Currently char
is used. This is important to support characters that require 4 bytes in UTF-16 like emoji. I have a branch in my fork where I changed the internal representation to UTF-32 (by using int
everywhere instead of char
), so I can set up a PR if that's all right :)
Jjagg@4c058ec?w=1
The only change in functionality is in TextLayout.cs to keep it easy to review. For 2-byte UTF-16 characters nothing changed functionally at all.
Add a unique glyph instance id to the IGlyphRenderer.BeginGlyph to enable systems to be able to cache rendered glyphs and skip the more expensive vector rending if they already have a cached rasterized glyph.
The unique instance id should be unique across all aspects of a glyph that could cause it to require rendering differently. Font size, glyph index, font name & style (it should/would only require being unique for the lifetime of the process, i.e be a HashCode)
render portions of text in a superscript style
I am not sure if this is related to the actual fonts been badly made or if it is an issue also with the "Shapes" libary... It looks like the shapes being drawn for the font charaters are missing a point and therefore assume a point of 0,0
It is not all fonts only a few I have found in the "Serif" section of Google Fonts
I have the latest packages of
ImageSharp - 1.0.0-alpha5-00069
ImageSharp.Drawing - 1.0.0-alpha5-00063
SixLabors.Fonts - 0.1.0-alpha0006
SixLabors.Shapes - 0.1.0-alpha0010
I have found a number of fonts that do this a few listed below (all downloaded from google fonts)
Marko One
Coustard
Wendy One
The image is an example of the issue with these fonts, again this was made using the method from the Text Wrapping demo https://github.com/SixLabors/Demos/blob/master/WaterMarkSample/Program.cs#L71
support drawing a strike through across glyphs
Currently FontInstance is internal, meaning I can't call the LoatFont variants from my code and I'm forced to use FontCollection.
However FontCollection is not a useful abstraction for me: it first loads the font from the stream and then checks if the family name already exists.
I know better in my code and have my own caching to prevent loading the font from the stream in the first place (using file name or embedded resource id as the key) if it's already loaded so I don't need FontCollection and it just adds overhead with those dictionary checks and the lock:
Fonts/src/SixLabors.Fonts/FontCollection.cs
Line 105 in 4ee628b
Making FontInstance public would allow me to implement my own caching without those overheads.
When rendering the string "", an InvalidOperationException with the description "Sequence contains no elements" is thrown.
Provide a mechanism for processing the font hints.
When the TabWidth is set to 0, any line containing a tab will be cut off following the tab.
When the TabWidth is greater than 1, the actual drawn TabWidth will be 1 too small.
Measuring strings starting or ending with a tab ignores those tabs.
Measuring the string "x\tx" in a monospaced font returns a width of 4 characters when the TabWidth is 4, so the tab seems to be 2 spaces too short. Measuring "x\t\tx" returns a width of 8 characters, so it seems like one of the tabs has the correct width.
FontCollection fonts = new FontCollection();
Font font1 = fonts.Install("./path/to/font1.ttf");
Font font2 = fonts.Install("./path/to/font2.woff");
I cant get the sample code working.
Although the main focus of SixLabors.Fonts is to provide font rendering features to ImageSharp, I don't see why it can't be used for much more than that.
So I would like to request to expose glyph information such as:
Glyph metrics are required in case you want to create a texture font atlas, as used by game engines, where each letter is rendered into the texture, and the corresponding glyph metadata is stored in a separate file, so the engine can use it to render text on its own.
Glyph geometry (both raw and triangulated) can be used for vector graphics, or rendering large form factor text in videogames.
if the otf file contains only CFF data then we need to be able to extract the glyphs
http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5176.CFF.pdf
Error Using ImageSharp.Drawing, but the error is related to the font.
Not really sure if I should post the issue here or on ImageSharp, but as it seems to be font related I posted it here
I have the latest packages of
ImageSharp - 1.0.0-alpha5-00069
ImageSharp.Drawing - 1.0.0-alpha5-00063
SixLabors.Fonts - 0.1.0-alpha0006
Again using a font from Google Fonts, this is the font that caused the error
https://fonts.google.com/specimen/Love+Ya+Like+A+Sister
The error occurs in this method from previous answer for Text Wrapping issue thread
https://github.com/SixLabors/Demos/blob/master/WaterMarkSample/Program.cs#L71
at ImageSharp.Processing.ImageProcessor1.Apply(ImageBase
1 source, Rectangle sourceRectangle)
at ImageSharp.Image1.ApplyProcessor(IImageProcessor
1 processor, Rectangle rectangle)
at ImageSharp.ImageExtensions.DrawText[TColor](Image1 source, String text, Font font, IBrush
1 brush, IPen1 pen, Vector2 location, TextGraphicsOptions options) at ImageSharp.ImageExtensions.DrawText[TColor](Image
1 source, String text, Font font, TColor color, Vector2 location, TextGraphicsOptions options)
This is the Inner Exception
at SixLabors.Shapes.InternalPath.FindIntersections(Vector2 start, Vector2 end, Vector2[] buffer, Int32 count, Int32 offset)
at SixLabors.Shapes.ComplexPolygon.FindIntersections(Vector2 start, Vector2 end, Vector2[] buffer, Int32 count, Int32 offset)
at ImageSharp.Drawing.ShapeRegion.Scan(Single y, Single[] buffer, Int32 length, Int32 offset)
at ImageSharp.Drawing.Processors.FillRegionProcessor1.OnApply(ImageBase
1 source, Rectangle sourceRectangle)
at ImageSharp.Processing.ImageProcessor1.Apply(ImageBase
1 source, Rectangle sourceRectangle)
A FontCollection
becomes the root object for dealing with loading fonts from disk/streams.
FontCollections can cascade. i.e. you could have a global font collection and new up a new one which has the global one as a parent then add fonts to the child collection and you will be able to use files loaded into either context and disposing of the child context would free up data from only the newed/overridden fonts.
update api to allow processing ReadonlySpan<char>
to avoid unnecessary string allocations.
will then be able to allow consumers of ImageSharp to draw text with out the additional string allocations.
When rendering a string that starts with a tab, the rendered tab is one space too short. Any subsequent tabs have the correct width, however.
Trying to get any font from the system collection results in the above error on a Mac. Windows is working fine. e.g.
using System;
using SixLabors.Fonts;
namespace TestSixLaborsFont
{
class Program
{
static void Main(string[] args)
{
try
{
FontFamily family = FontCollection.SystemFonts.Find("Arial");
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
}
SixLabors.Fonts.Exceptions.InvalidFontFileException: Invalid glyph format, only TTF glyph outlines supported.
at SixLabors.Fonts.FontReader..ctor(Stream stream, TableLoader loader)
at SixLabors.Fonts.FontDescription.LoadDescription(String path)
at SixLabors.Fonts.FileFontInstance..ctor(String path)
at SixLabors.Fonts.SystemFontCollection.<>c.<.ctor>b__1_3(String x)
at System.Linq.Enumerable.SelectEnumerableIterator2.MoveNext() at System.Collections.Generic.EnumerableHelpers.ToArray[T](IEnumerable
1 source, Int32& length)
at System.Collections.Generic.EnumerableHelpers.ToArray[T](IEnumerable1 source) at System.Linq.Enumerable.ToArray[TSource](IEnumerable
1 source)
at SixLabors.Fonts.SystemFontCollection..ctor()
at SixLabors.Fonts.FontCollection.<>c.<.cctor>b__15_0()
at System.Lazy1.CreateValue() at System.Lazy
1.LazyInitValue()
at TestSixLaborsFont.Program.Main(String[] args) in .../TestSixLaborsFont/Program.cs:line 12
Add a flag to RenderOptions
to turn on word breaking.
wher word breaking is the ability to cut words in half at word wrapping boundries.
woff fonts are just a verient of ttf fonts but with compressed tables
Line 63 of TextMeasurer.cs should be:
float bottom = glyphLayouts.Max(x => x.Location.Y - x.LineHeight + x.Height);
Create a version of the Font
class FileFont
that reads the name & OS\2 tables to get the meta data about the font but immediately closes the file reader until more details are required (I.E. loading glyph data ) at which point it loads the full font definition from the source file for full usage.
I get this error a lot if I try to use a .ttf font I have downloaded from Google Fonts
I have tried many different fonts and this is a common error with most of the fonts I have downloaded from Google Fonts
This is how I load the fonts
var collection = new FontCollection();
var font = collection.Install($"{_env.WebRootPath}\\fonts\\CarterOne.ttf");
This is using SixLabors.Fonts in ImageSharp.Drawing
at SixLabors.Fonts.Tables.General.Glyphs.EmptyGlyphLoader.CreateGlyph(GlyphTable table)
at SixLabors.Fonts.FontInstance.GetGlyph(Char character)
at SixLabors.Fonts.TextLayout.GenerateLayout(String text, FontSpan style)
at SixLabors.Fonts.TextMeasurer.MeasureText(String text, FontSpan style)
Use case: for text editing in a UI the character bounds are needed so when a user clicks somewhere in a text string, the cursor can be shown at the correct character position.
The goal is to provide functionality similar to System.Drawing.Graphics.MeasureCharacterRanges()
:
Graphics measureGraphics = DrawUtil.MeasureGraphics;
// Measure character by character.
CharacterRange[] characterRanges = new CharacterRange[text.Length];
for (int i = 0; i < text.Length; ++i) {
characterRanges[i] = new CharacterRange(i, 1);
}
// Set string format.
StringFormat stringFormat = new StringFormat(StringFormat.GenericTypographic);
stringFormat.FormatFlags |= StringFormatFlags.NoWrap | StringFormatFlags.MeasureTrailingSpaces;
stringFormat.SetMeasurableCharacterRanges(characterRanges);
Region[] regions =
measureGraphics.MeasureCharacterRanges(text, basicFont.Font, Rectangle.Empty, stringFormat);
if (regions.Length > 0) {
for (int i = 0; i < regions.Length; ++i) {
Region region = regions[i];
RectangleF bounds = region.GetBounds(measureGraphics);
region.Dispose();
result = new Vector2D(bounds.Right, 0);
characterAdvances.Add(result);
}
}
stringFormat.Dispose();
I've already implemented it locally, see attached TextMeasurer adaptation.
// Licensed under the Apache License, Version 2.0.
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Numerics;
using SixLabors.Primitives;
namespace SixLabors.Fonts
{
/// <summary>
/// Encapulated logic for laying out and measuring text.
/// </summary>
public static class TextMeasurer
{
private static readonly RectangleF?[] EmptyRectangleFArray = new RectangleF?[0];
/// <summary>
/// Measures the text.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="options">The style.</param>
/// <returns>The size of the text if it was to be rendered.</returns>
public static SizeF Measure(string text, RendererOptions options)
=> TextMeasurerInt.Default.Measure(text, options);
/// <summary>
/// Measures the text.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="options">The style.</param>
/// <returns>The size of the text if it was to be rendered.</returns>
public static RectangleF MeasureBounds(string text, RendererOptions options)
=> TextMeasurerInt.Default.MeasureBounds(text, options);
/// <summary>
/// Measures the character bounds of the text. For each control character the list contains a <c>null</c> element.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="options">The style.</param>
/// <param name="characterBounds">The list of character bounds of the text if it was to be rendered. The list may contain <c>null</c> entries.</param>
/// <returns>Whether any of the characters had non-empty bounds.</returns>
public static bool MeasureCharacterBounds(string text, RendererOptions options, out IList<RectangleF?> characterBounds)
=> TextMeasurerInt.Default.MeasureCharacterBounds(text, options, out characterBounds);
internal static SizeF GetSize(ImmutableArray<GlyphLayout> glyphLayouts, Vector2 dpi)
{
if (glyphLayouts.IsEmpty)
{
return Size.Empty;
}
float left = glyphLayouts.Min(x => x.Location.X);
float right = glyphLayouts.Max(x => x.Location.X + x.Width);
// location is bottom left of the line
float top = glyphLayouts.Min(x => x.Location.Y - x.LineHeight);
float bottom = glyphLayouts.Max(x => x.Location.Y - x.LineHeight + x.Height);
Vector2 topLeft = new Vector2(left, top) * dpi;
Vector2 bottomRight = new Vector2(right, bottom) * dpi;
var size = bottomRight - topLeft;
return new RectangleF(topLeft.X, topLeft.Y, size.X, size.Y).Size;
}
internal static RectangleF GetBounds(ImmutableArray<GlyphLayout> glyphLayouts, Vector2 dpi)
{
if (glyphLayouts.IsEmpty)
{
return RectangleF.Empty;
}
bool hasSize = false;
float left = int.MaxValue;
float top = int.MaxValue;
float bottom = int.MinValue;
float right = int.MinValue;
for (var i = 0; i < glyphLayouts.Length; i++)
{
var c = glyphLayouts[i];
if (!c.IsControlCharacter)
{
hasSize = true;
var box = c.BoundingBox(dpi);
if (left > box.Left)
{
left = box.Left;
}
if (top > box.Top)
{
top = box.Top;
}
if (bottom < box.Bottom)
{
bottom = box.Bottom;
}
if (right < box.Right)
{
right = box.Right;
}
}
}
if (!hasSize)
{
return RectangleF.Empty;
}
var width = right - left;
var height = bottom - top;
return new RectangleF(left, top, width, height);
}
internal static bool GetCharacterBounds(ImmutableArray<GlyphLayout> glyphLayouts, Vector2 dpi, out IList<RectangleF?> characterBounds)
{
bool hasSize = false;
if (glyphLayouts.IsEmpty)
{
characterBounds = EmptyRectangleFArray;
return hasSize;
}
characterBounds = new List<RectangleF?>();
for (var i = 0; i < glyphLayouts.Length; i++) {
var c = glyphLayouts[i];
if (c.IsControlCharacter)
{
characterBounds.Add(null);
}
else
{
hasSize = true;
var box = c.BoundingBox(dpi);
characterBounds.Add(box);
}
}
return hasSize;
}
internal class TextMeasurerInt
{
internal static TextMeasurerInt Default { get; set; } = new TextMeasurerInt();
private TextLayout layoutEngine;
internal TextMeasurerInt(TextLayout layoutEngine)
{
this.layoutEngine = layoutEngine;
}
/// <summary>
/// Initializes a new instance of the <see cref="TextMeasurerInt"/> class.
/// </summary>
internal TextMeasurerInt()
: this(TextLayout.Default)
{
}
/// <summary>
/// Measures the text.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="options">The style.</param>
/// <returns>The size of the text if it was to be rendered.</returns>
internal RectangleF MeasureBounds(string text, RendererOptions options)
{
ImmutableArray<GlyphLayout> glyphsToRender = this.layoutEngine.GenerateLayout(text, options);
return GetBounds(glyphsToRender, new Vector2(options.DpiX, options.DpiY));
}
/// <summary>
/// Measures the text.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="options">The style.</param>
/// <returns>The size of the text if it was to be rendered.</returns>
internal bool MeasureCharacterBounds(string text, RendererOptions options, out IList<RectangleF?> characterBounds) {
ImmutableArray<GlyphLayout> glyphsToRender = this.layoutEngine.GenerateLayout(text, options);
return GetCharacterBounds(glyphsToRender, new Vector2(options.DpiX, options.DpiY), out characterBounds);
}
/// <summary>
/// Measures the text.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="options">The style.</param>
/// <returns>The size of the text if it was to be rendered.</returns>
internal SizeF Measure(string text, RendererOptions options)
{
ImmutableArray<GlyphLayout> glyphsToRender = this.layoutEngine.GenerateLayout(text, options);
return GetSize(glyphsToRender, new Vector2(options.DpiX, options.DpiY));
}
}
}
}```
Add option to text layout engine to support rendering 'right to left' instead of the current 'left to right'.
This will require new option on RendererOptions
to allow configuring 'right to left`
and updating
Fonts/src/SixLabors.Fonts/TextLayout.cs
Line 25 in 4ee628b
render portions of text in a subscript style
Currently if a space or Tab character is at the start or end of a line then its width is not included in text size calculations.
provide a flag to enable pixel grid aligning
pixel grid aligning is the act of offsetting the glyph so that it move closely aligns with the underlying pixel grid.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.