Using Glyph objects | C++

Working with glyphs is crucial when coding fonts as it is their unit. When the basic information about glyphs is explained in the article of the What is font paragraph, here we are going to teach you the functionality of Aspose.Font for working with glyphs and the Glyph objects in particular.

Glyph object

Glyphs of any font are represented by Glyph type in Aspose.Font library. So, Glyph type is a universal object for font glyphs, independently from font format(TrueType, Compact Font Format, etc).

The functionality provided by the Glyph object

What functionality does this object provide for us?

This question is answered by the properties it has.

  • First, let’s look at the property State. Some of the fonts may be corrupted, and their glyphs also can be corrupted. Property State tells us whether the glyph is corrupted. If it has the value ParsedWithErrors, then that glyph was parsed with errors, and not all the glyph data was received.
  • Properties WidthVectorX and LeftSidebearingX tell us about such glyph metrics as advance width and left side bearing correspondingly.
  • Properties WidthVectorY and LeftSidebearingY have the same sense as WidthVectorX and LeftSidebearingX, but WidthVectorY and LeftSidebearingY are related to coordinates for the vertical Y-axis.

Let’s have an example with the next code snippet that shows how to calculate the width for the text “Hello world” in pixels when the font size is 10.

Add the next namespaces at the head of the file:

using namespace Aspose::Font::Glyphs;
System::SharedPtr<Aspose::Font::Font> font;

Then you need to take the next steps:

    //Declare text and other constants
    const System::String text = u"Hello world";
    const int32_t fontSize = 10;
    
    //Declare variable for string width
    double width = 0;
    
    //Get glyph for each letter in text and calculate width for whole text.
    //The same result can be achieved using method font->get_Metrics()->MeasureString(text, fontSize).
    for (char16_t symbol : text)
    {
        System::SharedPtr<GlyphId> gid = this->_font->get_Encoding()->DecodeToGid(symbol);
        System::SharedPtr<Glyph> glyph = this->_font->GetGlyphById(gid);
        width += (glyph->get_WidthVectorX() / this->_font->get_Metrics()->get_UnitsPerEM()) * fontSize;
    }

To get the glyph’s Bounding box use the GlyphBBox property of the Glyph object.

To get a visual representation of glyphs you need to know the coordinates for all glyphs’ points.

How to get coordinates for all glyph points from the Glyph object?

The next properties, IsEmpty and Path were designed for this case.

Property IsEmpty is auxiliary. It tells us whether the glyph’s path is empty, or in other words glyph has no drawing instructions at all. If it has the value false, it’s time to construct the whole glyph’s figure using the very useful property Path.

In the concept of Aspose.Font library the representation of any glyph is divided into the simplest graphic primitives, called segments, and represented by interface IPathSegment. Interface IPathSegment is a base abstract graphic primitive.

Concrete graphic primitives are represented by such types as MoveTo, LineTo, CurveTo, and ClosePath.

Type ClosePath is used to indicate the end of the current graphic contour.

Types MoveTo, LineTo, and CurveTo by their definition correspond with the identical postscript operators.

Also, the types MoveTo and LineTo by their definition correspond to the functions MoveToEx() and LineTo() from windows GDI lib, type CurveTo is used to describe B├ęzier curves.

Glyph property Path provides us with a collection of all graphic primitives for that glyph.

Property Path has type SegmentPath and every object of this type has property Segments of type PathSegmentCollection. This property Segments returns all the graphic primitives which object SegmentPath includes. In other words, we can get all the graphic primitives for the glyph using the entry glyph.Path.Segments.

The next example calculates all the points which glyph has and stores them in variable points, which represents an array of objects with Point type.

The logic used by this sample is simple and it doesn’t extract glyph contours. To get these contours using type ClosePath must be added to segments processing.

Add the next namespaces at the head of the file:

using System::Collections::Generic;
using System::Drawing;
using Aspose::Font::Glyphs;
using Aspose::Font::RenderingPath;

Then you need to take the next steps:

    System::SharedPtr<Glyph> glyph;

    //Declare resultant list with pints
    System::SharedPtr<System::Collections::Generic::List<System::Drawing::Point>> points = System::MakeObject<System::Collections::Generic::List<System::Drawing::Point>>();
    
    //Init service reference on IPathSegment
    System::SharedPtr<IPathSegment> prevSegment;
    
    //Iterate all glyph path segments and collect points
    for (auto&& segment : glyph->get_Path()->get_Segments())
    {
        if ((System::ObjectExt::Is<LineTo>(segment)) || (System::ObjectExt::Is<CurveTo>(segment)))
        {
            if (System::ObjectExt::Is<MoveTo>(prevSegment))
            {
                System::SharedPtr<MoveTo> moveTo = System::DynamicCast_noexcept<Aspose::Font::RenderingPath::MoveTo>(prevSegment);
                AddPoint((int32_t)moveTo->get_X(), (int32_t)moveTo->get_Y(), points);
            }
            if (System::ObjectExt::Is<LineTo>(segment))
            {
                System::SharedPtr<LineTo> line = System::DynamicCast_noexcept<Aspose::Font::RenderingPath::LineTo>(segment);
                AddPoint((int32_t)line->get_X(), (int32_t)line->get_Y(), points);
            }
            else if (System::ObjectExt::Is<CurveTo>(segment))
            {
                System::SharedPtr<CurveTo> curve = System::DynamicCast_noexcept<Aspose::Font::RenderingPath::CurveTo>(segment);
                AddPoint((int32_t)curve->get_X1(), (int32_t)curve->get_Y1(), points);
                AddPoint((int32_t)curve->get_X2(), (int32_t)curve->get_Y2(), points);
                AddPoint((int32_t)curve->get_X3(), (int32_t)curve->get_Y3(), points);
            }
        }
        prevSegment = segment;
    }	

void GlyphMetrics::AddPoint(int32_t x, int32_t y, System::SharedPtr<System::Collections::Generic::List<System::Drawing::Point>> points)
{
    System::Drawing::Point p;
    p.set_X(x);
    p.set_Y(y);
    points->Add(p);
}