[prev in list] [next in list] [prev in thread] [next in thread] 

List:       freetype-devel
Subject:    [ft-devel] GSoC 2019 - Adding support for OpenType-SVG fonts to FreeType
From:       Moazin Khatri <moazinkhatri () gmail ! com>
Date:       2019-08-26 14:03:14
Message-ID: CABgJyi52ydEt2MV417YM-+MAftK79v7TdRPkLjDMz=s+Bf0ABw () mail ! gmail ! com
[Download RAW message or body]

[Attachment #2 (multipart/alternative)]


Hi folks,

I have prepared a final report for the project.

You can get it here:
https://moazin.bitbucket.io/gsoc-2019/

It's also available from repository:
https://bitbucket.org/moazin/freetype-docs/src/master/ot-svg-report.md

I'm also attaching a copy of the final version.


Moazin

[Attachment #5 (text/html)]

<div dir="ltr">Hi folks,<br><div><br></div><div>I have prepared a final report for \
the project.</div><div><br></div><div>You can get it here:</div><div><a \
href="https://moazin.bitbucket.io/gsoc-2019/">https://moazin.bitbucket.io/gsoc-2019/</a><br></div><div><br></div><div>It&#39;s \
also available from repository:</div><div><a \
href="https://bitbucket.org/moazin/freetype-docs/src/master/ot-svg-report.md">https:// \
bitbucket.org/moazin/freetype-docs/src/master/ot-svg-report.md</a><br></div><div><br></div><div>I&#39;m \
also attaching a copy of the final \
version.</div><div><br></div><div><br></div><div>Moazin</div></div>


["ot-svg-report.md" (text/markdown)]

# GSoC 2019: Adding support for OpenType-SVG fonts to FreeType

## A Project Report

### Project Information

> Field           | Value                                                        |
> --------------- | ------------------------------------------------------------ |
> Project Link    | https://summerofcode.withgoogle.com/projects/#6096036961976320 |
> Organization    | [The FreeType Project](https://www.freetype.org/)            |
> Mentors         | Werner Lemberg and Suzuki Toshiya                            |
> Languages/Tools | `ANSI C`, `Unix Build Tools`                                 |
> My Email        | `moazinkhatri@gmail.com`                                     |

### Project Discussion

In this project I successfully added OpenType-SVG font support to FreeType. This \
involved:

1. Writing code to read the SVG table and load SVG documents.
2. Researching on SVG rendering libraries and using them to render the documents. \
                This involved:
   * Closely observing the rendering behavior of popular SVG rendering libraries \
([`librsvg`](https://github.com/GNOME/librsvg/), \
[`resvg`](https://github.com/RazrFalcon/resvg/) and \
                [`svgnative`](https://github.com/adobe/svg-native-viewer)) with SVG \
                documents.
   * Studying the specification to learn about glyph placement in documents and \
getting them rendered properly. 3. Writing the necessary code to connect FreeType to \
the external library and render the glyphs. This was done by creating a rendering \
module named `ot-svg`. 4. Modifying the FreeType demo tools and the FreeType cache \
system to work with OpenType-SVG glyphs. 5. Modifying the build system of FreeType to \
incorporate OT-SVG support.

All of the goals have been achieved. The only thing remaining is to decide on a \
default SVG rendering library. There is no hurry for that. We need features that are \
either absent or under development in most libraries. Thus this decision will be \
taken quite late. 

### Final Work Product

1. [*Development code in FreeType \
repository*](https://git.savannah.gnu.org/cgit/freetype/freetype2.git/log/?h=GSoC-2019-moazin): \
All of my code lives in the branch `GSoC-2019-moazin`. Since it's a development \
branch, the commit history doesn't look clean. I made many mistakes and later fixed \
them. The work done within the GSoC period is from commit \
[0729a6516](https://git.savannah.gnu.org/cgit/freetype/freetype2.git/commit/?h=GSoC-2019-moazin&id=0729a65165dd27445bc2ebd1df41df288c85acdd) \
to [f943af649](https://git.savannah.gnu.org/cgit/freetype/freetype2.git/commit/?h=GSoC-2019-moazin&id=f943af64905378bde29eaf7c4404f1a0fb971830). \
See the *diff* [here](https://git.savannah.gnu.org/cgit/freetype/freetype2.git/diff/?i \
d=f943af64905378bde29eaf7c4404f1a0fb971830&id2=0729a65165dd27445bc2ebd1df41df288c85acdd).


3. *Writing rendering ports*: FreeType doesn't link directly with SVG rendering \
libraries. Instead, a callback hook mechanism was created to allow users to plug-in \
their own libraries. Later on, once we decide on a default rendering library, we will \
set default hooks so that FreeType supports SVG fonts out-of-the-box. Currently, \
`librsvg` has been set as the default one. This is just temporary. I call these hooks \
**rendering ports**. I have written three ports for three popular SVG rendering \
libraries:

   * [`librsvg port`](https://bitbucket.org/moazin/librsvg-port-freetype-otsvg): Last \
commit within GSoC period is \
[9743ce8cf](https://bitbucket.org/moazin/librsvg-port-freetype-otsvg/commits/9743ce8cf4a8b5ee2e729a3a8cac2d2b9b6674f9)
                
   * [`resvg port`](https://bitbucket.org/moazin/resvg-port-freetype-otsvg/src)
   * [`svgnative port`](https://bitbucket.org/moazin/svgnative-port-freetype-otsvg)

   Out of these, `librsvg port` is the most up-to-date and I plan to update the other \
ones soon.  
### Outline

This report is a large document. Please jump to the relevant section depending on \
what you seek.

* [Usage](#usage): This section should help you immediately run the code I have \
written and get OT-SVG glyphs rendered. 

* [A brief summary of how OT-SVG fonts \
work](#a-brief-summary-of-how-ot-svg-fonts-work): This section introduces how OT-SVG \
fonts really work. This is almost a brief summary of the [OT-SVG \
                specification](https://docs.microsoft.com/en-us/typography/opentype/spec/svg). \
                
* [OT-SVG project in clean steps](#ot-svg-project-in-clean-steps): This section is \
for people who are already familiar with FreeType and want to know the technical \
                details of the project's implementation in a simple series of steps.
* [Design choices and challenges faced](#design-choices-and-challenges-i-faced): This \
section discusses the challenges I faced in the project and the design choices I made \
to fix them. 

### Usage

The project code hasn't been merged to master yet. Thus, it only lives in the branch \
`GSoC-2019-moazin`. OT-SVG feature can be compiled with or without default hooks.

#### Procedure to test OT-SVG fonts without default hooks

Firstly, I should mention that this has been only tested on Ubuntu 19.04. To be able \
to use this project, you must have the latest in-development version of `librsvg` \
compiled and installed in your system. At the time of writing this, I am using tag \
[`2.45.91`](https://github.com/GNOME/librsvg/tree/2.45.91). Please follow their \
instructions to compile and install the library. Once you've done that, follow these \
steps:

1. Clone the FreeType repository and checkout `GSoC-2019-moazin`.
   ```bash
   git clone https://git.savannah.gnu.org/git/freetype/freetype2.git
   cd freetype2
   git checkout GSoC-2019-moazin
   git checkout f943af6490537 # GSoC-2019-moazin might break due to future commits.
   cd ..
   ```
2. Clone the port repository and checkout to the current commit.
   ```bash
   git clone https://moazin@bitbucket.org/moazin/librsvg-port-freetype-otsvg.git
   cd librsvg-port-freetype-otsvg
   git checkout 9743ce8cf4a8b5 # again, future commits might change things
   cd ..
   ```
3. Copy the `rsvg_port.c` and `rsvg_port.h` files to the FreeType source.
   ```bash
   cp ./librsvg-port-freetype-otsvg/port/rsvg_port.c ./freetype2/src/svg/
   cp ./librsvg-port-freetype-otsvg/port/rsvg_port.h ./freetype2/src/svg/
   ```
4. Build FreeType.
   ```bash
   cd freetype2
   sh autogen.sh
   ./configure --with-svg=no-default
   make
   cd ..
   ```
5. Build the port and run the test program.
   ```bash
   cd librsvg-port-freetype-otsvg/port
   mkdir build
   cd ../tester
   mkdir build
   sh compile_port.sh
   # ./build/main ./path/to/font-file.otf c <character you want to print>
   # or
   # ./build/main ./path/to/font-file.otf i <glyph id you want to print>
./build/main ./path/to/font-file.otf c g    # Just an example
   ./build/main ./path/to/font-file.otf i 120  # Just an example
   ```
   

In case you're getting some errors, please make sure you have all the dependencies \
installed. Check the `Makefile` in `librsvg-port-freetype-otsvg/tester` to see the \
dependencies. In case you need help, feel free to reach me out at \
`moazinkhatri@gmail.com`. 

### A brief summary of how OT-SVG fonts work

[SVG](https://en.wikipedia.org/wiki/Scalable_Vector_Graphics) is a popular vector \
graphics format which is being extensively used in web pages these days. The \
[`SVG`](https://docs.microsoft.com/en-us/typography/opentype/spec/svg) table is a \
feature addition to OpenType that allows SVG documents to be used to describe glyphs. \
The table contains a list of SVG documents where each SVG document can contain glyph \
descriptions for one or a whole range of glyph IDs. If an SVG document contains the \
glyph description of only one glyph ID, the whole document is supposed to be \
rendered. If on the other hand, the document contains multiple glyph descriptions for \
a range of glyph IDs, only the element with the ID `'glyph<ID>'` is to be rendered. \
Read the section *[Glyph \
Identifiers](https://docs.microsoft.com/en-us/typography/opentype/spec/svg#glyph-identifiers)* \
to read more about this. 

#### SVG coordinate system and glyph placement

SVG, being a vector graphics format, ultimately relies on Bézier curves. These \
curves consist of points which exist at certain coordinates in a coordinate system \
that we shall call the `SVG` coordinate system. This coordinate system should be \
analogous to the coordinate system of TTF/CFF outlines. However, unlike the TTF/CFF \
coordinate system, this one has an inverted `y` axis. So, positive `x` is to the \
right while positive `y` points down. Just like the TTF/CFF coordinate system has an \
EM square which font designers use, same is true with the SVG coordinate system, \
though, unlike TTF/CFF outlines, the EM square's size can vary. The `viewBox.width` \
and `viewBox.height` or `width/height` attribute on the root `SVG` node can indicate \
the size of the EM square. If such attributes are missing, it should be assumed that \
the EM square is of the same number of units as the corresponding TTF/CFF outlines' \
EM square. The `viewBox` attribute describes a rectangle. Imagine that the whole SVG \
coordinate system is covered by an opaque sheet with one transparent window and that \
transparent window is the `viewBox`. Any SVG rendering library will output what can \
be seen through this window. We shall define a new coordinate system `SVG'` as the \
coordinate system formed by translating the `SVG` coordinate system to the point \
`viewBox.x` and `viewBox.y`. In case the `viewBox` attribute is missing, `SVG'` \
coordinate system is totally identical to the `SVG` coordinate system. The glyph is \
placed in such a way that the baseline is at `y = 0` of the `SVG'` coordinate system. \
The `advance.x` is the same as that for the corresponding TTF/CFF glyph. For properly \
rendering the glyph, the origin of `SVG'` must be placed on top of the origin of the \
current pen point. If that's done properly, the glyph will just land up right where \
it's supposed to. 

### OT-SVG project in clean steps

In FreeType, there are two basic steps involved in rendering a glyph that has a \
particular ID.

1. **Loading the glyph**: The glyph outlines (or the vector data) is loaded into the \
`glyphslot`. 2. **Rendering the glyph**: The glyph outlines that have been loaded are \
rendered into a pixmap.

For OpenType SVG glyphs, we devise a similar plan. In the **loading** part, the \
appropriate SVG document will be fetched and stored in the glyph-slot.  In the \
**rendering** part, the outlines will be sent to an external SVG rendering library, \
which will return a pixmap.

#### Making OT-SVG an optional feature

`FT_CONFIG_OPTION_SVG` is added to allow toggling OT-SVG support. This was done so \
that smaller builds of FreeType can disable OT-SVG support to reduce build size. 

#### Adding code to load the SVG table

In FreeType, for fonts that have an `sfnt` header, loaded tables are usually stored \
in `TT_FaceRec`. The manner in which they are stored differs between tables. For \
`SVG` a strategy similar to `CPAL` and `SVG` has been adopted. An internal module \
specific structure `Svg` is created to hold basic information extracted from the \
`SVG` table. `TT_FaceRec` gets a new field of type `void *` named `svg` which would \
hold a pointer to this structure. A flag `FT_FACE_FLAG_SVG` is added, this will be \
set whenever an `SVG` table is present and has been loaded. `sfnt` related functions \
are exposed by the `sfnt` module. Thus, `SFNT_Interface` was modified to add two more \
fields `load_svg` and `free_svg`. The actual functions to fill these fields were \
created with the names `tt_face_load_svg` and `tt_face_free_svg`. The function \
`sfnt_load_face` loads all the available tables of interest and `sfnt_free_face` \
frees all the allocations performed by `sfnt_load_face`. Both of these were modified \
to *load* and *free* the `Svg` structure respectively. 

#### Adding code to *load* SVG glyphs

To store the SVG document in the `glyphslot`, a structure named `FT_SVG_DocumentRec` \
is created which contains the SVG document along with other necessary details to \
accompany it. Please see the in-file documentation of `otsvg.h` for more details of \
this structure. This document is referenced by `other` field of `FT_GlyphSlotRec`. \
`ft_glyphslot_init`, `ft_glyphslot_clear` and `ft_glyphslot_done` were modified to \
take care of allocating and freeing memory for this structure.

To fetch the correct SVG document, one more function is added to `SFNT_Interface` \
named `load_svg_doc`. The actual function that populates this place is \
`tt_face_load_svg_doc`. 

Ultimately, `cff_slot_load` and `TT_Load_Glyph` were modified to add special code \
that'll call `load_svg_doc` and fetch `advance` information (and set it) if an OT-SVG \
glyph is found, otherwise the control flow remains totally the same.  

#### Adding code to *render* SVG glyphs

To render OT-SVG glyphs, an external library is to be used. For this purpose, a new \
*Renderer Module* is created named `ot-svg`. However, instead of directly linking an \
external library to FreeType, hooks are used. Thus, the client application can plug \
in any SVG rendering library of its choice by writing the hook functions. Later, we \
will decide on a default rendering library and have it linked by default. Thus, \
FreeType will support OT-SVG glyphs out-of-box and will also give users the ability \
to plug in other SVG rendering libraries if they want to.

We could just create a renderer module like the other ones out there but we need to \
store the hooks somewhere. For this reason, we create a new type `SVG_RendererRec` \
that inherits from `FT_RendererRec` and adds some more fields

```c++
  typedef struct SVG_RendererRec_
  {
    FT_RendererRec     root;   /* This inherits FT_RendererRec */
    FT_Bool            loaded;
    FT_Bool            hooks_set;
    SVG_RendererHooks  hooks;  /* Holds out hooks to the outside library */
  } SVG_RendererRec;
```

1. `loaded` is a flag that allows us to lazily load the SVG rendering library at the \
point when the first SVG glyph is rendered. 

2. `hooks_set` is a flag that simply indicates whether hooks have been set or not. In \
case they haven't been set, the module isn't really functional.

3. `hooks` is a structure that contains the hooks. 
   There are a total of *four* hooks.

   ##### 1. Init Hook

   ```c++
   typedef FT_Error
   (*SVG_Lib_Init_Func)( FT_Library  library );
   ```

   This hook is called at the time when the first OT-SVG glyph is to be rendered. \
Once it has been called `loaded` will be set to `TRUE`. The job of this hook is to \
let the SVG rendering library perform any sort of initializations that it wants to. \
We would want the SVG rendering library to have a *state* or *persistence* between \
different function calls. The library can create a *state* structure of its own, \
allocate it upon initialization and set `svg_renderer_state` of `library` to \
reference it. 

   ##### 2. Free Hook 

   ```c++
   typedef void
   (*SVG_Lib_Free_Func)( FT_Library  library );
   ```

   To let the SVG rendering library perform any cleanups. For example, the library \
should free the *state* structure if it created one.

   ##### 3. Preset Hook

   ```c++
   typedef FT_Error
   (*SVG_Lib_Preset_Slot_Func)( FT_GlyphSlot  slot, FT_Bool  cache);
   ```

   The preset hook does the job of *presetting* the bitmap slot. However, for an \
OT-SVG glyph, *presetting* the slot needs many calculations which are useful at the \
rendering stage too. The preset hook is called from two places only. 

      1. At the **loading** stage from `ft_glyphslot_preset_bitmap`.
      2. Right before rendering the glyph in `ft_svg_render`. 

   When it is the latter, we want the function to store those useful calculations in \
the *state* of the library so that they can be later used by the `render` hook. The \
calls are right next to each other so we can be sure that the state will remain \
intact. However, when it's the former, we do not want the function to modify state in \
anyway. That's the reason we have the `cache` argument. When it is `TRUE` the \
calculations can saved in *state*, when it's `FALSE` the calculations can't be stored \
in *state*. The reason why the preset hook is called again in `ft_svg_render` is \
because we want to make sure that the `glyphslot` has been preset, so we can use its \
`width/height` to allocate enough memory for the pixmap buffer.

   ##### 4. Render Hook

   ```c++
    typedef FT_Error
    (*SVG_Lib_Render_Func)( FT_GlyphSlot  slot );
   ```

   The render hook simply renders the loaded SVG document and places the resultant \
pixmap in `slot->bitmap.buffer`. Memory for the buffer is already allocated so it \
shouldn't be allocated from inside. The following values must be set:

   ```c++
   slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA;
   slot->bitmap.num_grays = 256;
   slot->format = FT_GLYPH_FORMAT_BITMAP;
   ```

   #### Adding support of OT-SVG glyphs in Glyph Management API

   A crucial part of FreeType is the **Glyph Management API**. Thus, it's necessary \
to add OT-SVG support to it. This is done by creating new handle `FT_SvgGlyph`, its \
structure `FT_SvgGlyphRec` and creating a new glyph class, named \
`ft_svg_glyph_class`. We also need to modify some functions to appropriately handle \
OT-SVG glyphs but the changes are small.

   #### Adding support for transforms to OT-SVG glyphs

   For traditional outlines, transformations are directly applied on the actual \
outlines itself. For OT-SVG glyphs, FreeType cannot reach any of the actual graphics \
data, since, only the SVG document string is held. Thus, we can't apply any \
transforms ourselves. The only thing we can do is store the transformation matrix and \
later have the external library apply it for us. For this purpose, we add two fields, \
`transform` and `delta` to `FT_SVG_Document` and `FT_SvgGlyphRec`. If multiple \
transformations are applied one after another, some maths is done to compute an \
equivalent transformation matrix. The external library should pick up these fields \
and apply the transforms while presetting and rendering the document. So far, this \
approach has worked pretty well. 

### Design choices and challenges I faced

For any project, I think one of the hardest part is making choices. Same was the case \
with this project. I list the major challenges I faced below:

1. *Deciding where to store the SVG document and other relevant information*: I \
needed to store the SVG document  itself and some information about it in the \
`glyphslot` but couldn't really figure out where to store it. Firstly, I tried adding \
new fields to `TT_GlyphSlotRec` and `CFF_GlyphSlotRec`. The approach worked but soon \
I realized there were some problems with this approach. [See the thread: \
[msg00092](https://lists.nongnu.org/archive/html/freetype-devel/2019-06/msg00092.html)] \
So, upon the suggestion of FreeType developers, I created a new structure \
`FT_SVG_DocumentRec` and referenced it in the `other` field of `glyphslot`. \
`FT_SVG_DocumentRec` contains all the fields needed. This approach has turned out to \
work pretty well. 2. *Nature of the SVG rendering module*: We wanted to give client \
users the ability to plug in different SVG rendering libraries if they want to. Thus, \
it was decided not to link the external library directly. Instead, we created a \
callback hook mechanism. The users can set their own hooks to plug-in another \
library. There was a contrary idea under consideration too, which was to create \
different SVG rendering modules for different libraries. The matter was under \
discussion in the mailing list but ultimately it was decided that the *callback \
hooks* idea is preferable. [See these threads: \
[msg00042](https://lists.nongnu.org/archive/html/freetype-devel/2019-07/msg00042.html), \
[msg00074](https://lists.nongnu.org/archive/html/freetype-devel/2019-07/msg00074.html) \
and [msg00051](https://lists.nongnu.org/archive/html/freetype-devel/2019-08/msg00051.html)]
 3. *Setting the hooks*: How do we get the hooks from the external user? At first, we \
were using an API function, `FT_Set_Svg_Hooks`, but later we dropped it. Instead, we \
use *module properties* to set the hooks. [See these threads: \
[msg00077](https://lists.nongnu.org/archive/html/freetype-devel/2019-07/msg00077.html), \
[msg00098](https://lists.nongnu.org/archive/html/freetype-devel/2019-07/msg00098.html) \
and [msg00051](https://lists.nongnu.org/archive/html/freetype-devel/2019-08/msg00051.html)] \
 4. *Storing the hooks*: There was a confusion about where to store the function \
pointers for the callback hooks. The solution I came up with, was, instead of \
creating an `FT_RendererRec` as the rendering module, I created a subclass of it \
named `SVG_RendererRec` and I added a hooks field to this structure as well as some \
flags. This solution has worked well and looks fine from a design point of view.  5. \
*Rendering the SVG glyphs*: Read the section [SVG coordinate system and glyph \
placement](#svg-coordinate-system-and-glyph-placement) to read about how SVG glyphs \
are placed within the SVG coordinate system. The behavior of popular SVG rendering \
libraries is that they just render whatever is in the `viewBox` to a pixmap. To \
properly render a glyph its bounding box must be known so that we can shift the \
coordinate system to get a proper rendering. How to get this bounding box is a \
challenging problem. Different SVG rendering libraries do provide ways of getting the \
bounding box but they are not always accurate (can be bigger than the actual bounding \
box). Firstly, we decided to use the bounding box of `TTF/CFF` outlines and from that \
predict the bounding box in SVG coordinates. [See the thread: \
[msg00050](https://lists.nongnu.org/archive/html/freetype-devel/2019-06/msg00050.html)] \
This worked perfectly for many fonts but failed for one. I brought up the issue in \
the [OpenType-SVG list](https://lists.w3.org/Archives/Public/public-svgopentype/) and \
ultimately it was realized that the bounding boxes don't have to be the same at all. \
[See the thread: [0000](https://lists.w3.org/Archives/Public/public-svgopentype/2019Jul/0000.html)] \
Thus, we resort to using the the SVG rendering library specific methods to get the \
bounding boxes. [See the thread: \
[msg00010](https://lists.nongnu.org/archive/html/freetype-devel/2019-08/msg00010.html)] \
The bounding box is almost always accurate and when it's not, `bitmap_left` and \
`bitmap_top` make sure that the glyph is positioned correctly after the rendering. \
Everything works great! :-) 6. *Transformation support for OT-SVG glyphs*: For \
traditional glyphs, FreeType can manipulate the outlines by itself, and thus it can \
apply the transformations directly to the outlines. For SVG glyphs, that isn't true, \
all we store is just the document string. To support transforms, we store a \
transformation matrix and a translate vector in `FT_SVG_DocumentRec` and \
`FT_SvgGlyphRec` and later the SVG rendering library picks it up and applies the \
transformation. There is a problem though. FreeType users input a transformation \
matrix that has been designed for the TTF/CFF coordinate system and it turns out that \
the `SVG` coordinate system is quite different. Thus, directly applying the \
transformation should give unexpected results. The solution I came up with is, we let \
the user pretend that the coordinate system is just a traditional one, then we \
convert the transformation to an equivalent one in SVG coordinates and then apply it. \
Another problem is, how do we support cascaded transformations? The solution I \
figured out was to use do some maths to compute an equivalent transformation matrix \
that has the effect of all the transformations applied. This has worked pretty well \
and the results are good. [See the thread: \
[msg00037](https://lists.nongnu.org/archive/html/freetype-devel/2019-08/msg00037.html)]



[Attachment #7 (text/plain)]

_______________________________________________
Freetype-devel mailing list
Freetype-devel@nongnu.org
https://lists.nongnu.org/mailman/listinfo/freetype-devel


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic