Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add SVG table support #672

Merged
merged 4 commits into from
Apr 12, 2024
Merged

feat: add SVG table support #672

merged 4 commits into from
Apr 12, 2024

Conversation

kinolaev
Copy link
Contributor

Description

This PR adds support for 'SVG ' table.

Motivation and Context

Fixes #106, #193

How Has This Been Tested?

I added tests to test/svg.js file. These tests parse and encode Colortube font.

Screenshots (if appropriate):

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • I did npm run test and all tests passed green (including code styling checks).
  • I have added tests to cover my changes.
  • My change requires a change to the documentation.
  • I have updated the README accordingly.
  • I have read the CONTRIBUTING document.

@ILOVEPIE
Copy link
Contributor

Is this to implement apple-style emoji fonts? I'll pull in connum on this one as I'm a little out of the loop but this looks good to go.

@ILOVEPIE ILOVEPIE requested a review from Connum February 10, 2024 01:08
@ILOVEPIE
Copy link
Contributor

One issue I do notice for this is the lack of integration with the drawing code.

@kinolaev
Copy link
Contributor Author

Hello @ILOVEPIE! It is to parse and make color fonts like Colortube or Fattern where SVG documents are embedded directly into a font file instead of being converted into glyphs.
By drawing code you mean Font.prototype.draw method, right? In my opinion parsing and making SVG tables are useful features even without the ability to draw SVGs on canvas. So maybe we could leave the drawing part out of this PR? By the way SVG fonts usually have glyphs as fallback.
I'll look into it and try to implement drawing SVGs on canvas in a separate PR if I have time.

@ILOVEPIE
Copy link
Contributor

The purpose of the library is to parse, generate and draw fonts. This PR is incomplete imho.

@kinolaev
Copy link
Contributor Author

Ok, I see your point and will try to find time to implement drawing as part of this PR.

@Connum
Copy link
Contributor

Connum commented Mar 22, 2024

The purpose of the library is to parse, generate and draw fonts. This PR is incomplete imho.

IMHO it's OK to implement parsing and writing first and drawing with an additional PR. We have other features where parsing works but writing doesn't, so here we even have parsing and writing in one go.

@Connum
Copy link
Contributor

Connum commented Mar 22, 2024

But what I am missing is a method to retrieve the SVG document in plaintext, using extDecoder() as is done for the tests. This should be done in a deferred way using defineProperty() and getters/setters, so we only have to do that calculation when the data is needed, like we already do in other places like glyph paths.

@kinolaev kinolaev force-pushed the svg-table branch 2 times, most recently from 440881a to 3f7da0f Compare March 24, 2024 11:53
@Connum
Copy link
Contributor

Connum commented Mar 24, 2024

Nice! However, rendering of the Colortube-lWAd.otf is working in the font inspector and the two top canvases on the main page, but not the glyph inspector (or the glyphs on the main page) for me. Can you look into this?

Two optimizations we should implement for performance:

  • Only de-gzip and build the image when it is requested for a given glyph
  • Use the native DecompressionStream instead of tiny inflate if available (recent browser and node versions)

We can implement the second one independently from this PR at a later time, but it would be great if we could have the lazy loading from the start.

@Connum
Copy link
Contributor

Connum commented Mar 24, 2024

There also seem to be scaling issues when rendering SVG data in the following fonts from https://github.com/unicode-org/text-rendering-tests/tree/main/fonts

@kinolaev
Copy link
Contributor Author

I've added the ability to draw SVGs. Before drawing SVG images must be prepared by calling await font.prepareSVGImages(). It will render SVGs as HTMLImageElements and cache them.
It's also possible to pass an array of SVGDocumentRecord to that function to prepare images only for specific records.

I've exported decodeDocument function to retrieve the SVG document in plaintext.
As SVGs can be gzipped, I've also added utility functions to determine and uncompress a gzip buffer (using tiny-inflate).

@Connum, you wanted to have a getter svg on Glyph, is it right? The problem is that one SVG document can contain multiple glyphs. If you want to get an SVG document for a specific glyph, it must also be transformed after decoding (see makeGlyphSvgTemplate function). Transformed SVG documents in plaintext are available in data URL of prepared images (font.tables.svg.images.get(glyphId).image.src). Could you please explain a bit further, where you want me to add a getter and what exactly it should return?

@Connum
Copy link
Contributor

Connum commented Mar 24, 2024

When getting the path for a glyph, we do this via a getter that will calculate the path once when it is requested for the first time, and then returns a cached version of it. We should do the same for the SVGs per glyph instead of preparing all of them at once, which could take quite some time depending on how large and complex the SVG font is.

@Connum
Copy link
Contributor

Connum commented Mar 24, 2024

For example, NotoColorEmoji-SVG.otf is freezing the browser for a considerable amount of time.

@kinolaev
Copy link
Contributor Author

Thank you @Connum for the feedback! It's difficult to prepare images lazily, because draw is synchronous and prepareSvgImages is asynchronous... But the problem is clear to me now and I'll try to figure it out.

@Connum
Copy link
Contributor

Connum commented Mar 25, 2024

I'm currently working on COLR/CPAL font rendering (so far we only support parsing and writing the tables), where I will also implement lazy loading of the layers. I hope to have this ready in the next days, then you could take some inspiration from it.

@kinolaev
Copy link
Contributor Author

It will be interesting to see your lazy loading implementation. But before I've read your message I've already found a way to implement it.

It is now possible to add onload callback to an SVG table that will be called with a glyphId when an image for the corresponding glyph is ready. In that callback you can decide whether you need to redraw anything.
The image loading only runs when svg.getImage is called. The result is cached.
Does it looks about right to you?

I haven't solved the scaling problem with the fonts you mentioned above yet. I'll look at it later.

@kinolaev kinolaev force-pushed the svg-table branch 2 times, most recently from 5c18882 to eb091ce Compare March 26, 2024 15:49
@Connum
Copy link
Contributor

Connum commented Mar 26, 2024

I hope I'll finish the cpal/colr overhaul today or tomorrow. Maybe it will help you fix the performance issue.

@Connum
Copy link
Contributor

Connum commented Mar 27, 2024

Haven't created a PR yet, but you can already take a look at my current work in progress here:
https://github.com/Connum/opentype.js-next/tree/feature/colr-cpal-rendering

regarding the lazy loading, especially:
https://github.com/Connum/opentype.js-next/blob/feature/colr-cpal-rendering/src/glyphset.js#L136

@kinolaev kinolaev force-pushed the svg-table branch 4 times, most recently from e01d5af to ba13f13 Compare March 27, 2024 18:41
@kinolaev
Copy link
Contributor Author

kinolaev commented Mar 27, 2024

Thank you, @Connum , I've added svgImage getter. Does it look good to you?
I've also implemented decompression using native DecompressionStream when it's available.

@ILOVEPIE
Copy link
Contributor

We should definitely support all of the different methods of doing emoji that have been implemented into fonts, although I will say that only Apple supports the SVG table currently.

@Connum
Copy link
Contributor

Connum commented Mar 27, 2024

We should definitely support all of the different methods of doing emoji that have been implemented into fonts, although I will say that only Apple supports the SVG table currently.

I'll have a PR for CPAL/COLR v0 rendering ready by (hopefully) tomorrow. v1 is quite complex though.

@kinolaev
Copy link
Contributor Author

There also seem to be scaling issues when rendering SVG data in the following fonts from https://github.com/unicode-org/text-rendering-tests/tree/main/fonts

Scaling issues have been resolved. Is there anything anything else you'd like me to improve?

@Connum
Copy link
Contributor

Connum commented Apr 8, 2024

Can you please resolve the conflicts with the current master in order to do a rebase? Then we can have another look at it! Thanks!

@kinolaev kinolaev force-pushed the svg-table branch 2 times, most recently from 9fb94d7 to 4efaee0 Compare April 9, 2024 16:49
@kinolaev
Copy link
Contributor Author

kinolaev commented Apr 9, 2024

The conflicts are resolved. I took inspiration from your work and made three changes:

  • added drawSVG font render option
  • added getSvgImage method to Glyph class
  • moved all svg image creation code to SVGImageManager class

@Connum Connum self-assigned this Apr 9, 2024
@Connum
Copy link
Contributor

Connum commented Apr 9, 2024

Thank you! I already spotted a few things, but I'll do a full review soon, hopefully in the course of this week.

Copy link
Contributor

@Connum Connum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FInished review with a few requested changes.

Could you also please add a TODO comment and console message to

Glyph.prototype.toSVG = function(options) {

similar to
if (this._layers && this._layers.length) {
for COLR/CPAL?

Because we can't simply output the whole SVG document as it is, we should first sanitize it to make sure it doesn't contain any malicious scripts or features not supported in SVG fonts. (By the way, the latter about unsupported features would also be true for rendering in general, even on canvases, but we don't have to do that in this PR! See https://learn.microsoft.com/en-us/typography/opentype/spec/svg#svg-specification)

test/fonts/Colortube-lWAd.otf Outdated Show resolved Hide resolved
src/svgimages.js Show resolved Hide resolved
src/util.js Show resolved Hide resolved
docs/font-inspector.html Show resolved Hide resolved
src/svgimages.js Show resolved Hide resolved
src/path.js Outdated Show resolved Hide resolved
src/path.js Outdated Show resolved Hide resolved
src/path.js Outdated Show resolved Hide resolved
src/tables/svg.js Show resolved Hide resolved
docs/glyph-inspector.html Outdated Show resolved Hide resolved
@kinolaev
Copy link
Contributor Author

Thanks for the detailed review! I'll try to address all the issues you mentioned by the end of next week.

@kinolaev
Copy link
Contributor Author

TODO and warning are added to Path.prototype.toSVG method 2d56eb2.
I think I've answered all your comments. Please let me know if I've missed anything.

@Connum Connum added this to the Release 2.0.0 milestone Apr 12, 2024
Copy link
Contributor

@Connum Connum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good! I'll add the licenses for the three new test fonts to the LICENSE file in a follow-up PR, so we don't have to take another round.

@Connum Connum merged commit d3d3078 into opentypejs:master Apr 12, 2024
1 check passed
@Connum
Copy link
Contributor

Connum commented Apr 12, 2024

Thanks again for your work and your patience. I'd love to see more contributions in the future!

@kinolaev
Copy link
Contributor Author

Thank you for your time and attention. It was a pleasure to collaborate with you on this task!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support for 'SVG ' table
4 participants