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

Safari rejects OT-SVG if contains more than 2000 documents #276

Closed
anthrotype opened this issue Apr 15, 2021 · 14 comments
Closed

Safari rejects OT-SVG if contains more than 2000 documents #276

anthrotype opened this issue Apr 15, 2021 · 14 comments

Comments

@anthrotype
Copy link
Member

When an OT-SVG table contains more than 2000 unique svg documents, Safari won't display the color glyphs any more...

I made two test fonts, one with 2000 svgs, the other with 2001. The former works, the latter does not.

safari-max-2000-svgs-bug.zip

@miguelsousa already noted this bug in https:/adobe-fonts/noto-emoji-svg#warning-note-2-warning

Grouping more than one svg into the same document (covering a range of glyph indexes) seems to work around this issue.

We already do that for --color_format=picosvg where we group glyphs by shape reuse. We might have to do that also for untouchedsvg format, where we currently add one svg document in OT-SVG table for each input SVG. Otherwise if the count > 2000 (which is often the case for emoji fonts), they will stop working in Safari.

@anthrotype
Copy link
Member Author

While trying to fix this bug, by grouping multiple glyphs in the same document to avoid hitting the max 2000 svg docs per table, I found yet another abitrary limit that makes Safari reject the font.
This time it seems to be the number of glyphs grouped in each bucket. Although I am not yet sure if it's actually the length of the svg document, more than the number of glyphs it covers.

This zip file contains two OpenMoji-Color.ttf fonts and a static html file. One font groups glyphs in documents of 68 glyphs, the other one in groups of 69. One works with Safari (14.0 on macOS Catalina), the other one does not :(

safari-max-svg-glyphs-bucket.zip

Screenshot 2021-04-15 at 19 50 03

@anthrotype
Copy link
Member Author

I measured the maximum length in bytes of the embedded svg documents in each of the two test fonts attached above. The font with smaller bucket size (68 glyphs per document) actually happens to contain a document that is larger than the largest document in the other font with 69 glyphs per bucket:

OpenMoji-Color-68.ttf: max doc size (bytes): 371156 (WORKS in Safari) 
OpenMoji-Color-69.ttf: max doc size (bytes): 353954 (DOES NOT WORK in Safari) 

One would expect that the size of the documents to increase as the number of glyphs grouped in each bucket increase; that's definitely the case for the average size, while the max size may fluctuate if there are outliers (e.g. relatively big, complex glyphs) that change position as the bucket size change. Anyway the correlation is obvious.

Now, this particular comparison between 68 and 69 is puzzling as it appears to suggest that the problem isn't (or isn't just?) the max length of the documents. If we were hitting a maximum length limit for the svg documents, then why is it that the font which contains a larger document (but less individual glyphs grouped in each, 68) works whereas the other with a smaller max document size (but 1 more glyph per document) does not work? 🤔

Honestly right now making a OT-SVG font that works on Safari is like a hit-or-miss game... I'm out of ideas.

It would be great if @litherum could chime in and tell us what are Safari's hard-coded limits so these are known in advance and font developers can be confindent their color fonts will work on macOS.

@anthrotype
Copy link
Member Author

I made another test font with the same glyph set as OpenMoji font (3,892 glyphs in total, representative of a real-world emoji font), but with all the glyphs using the exact same (duplicated) SVG file, containing a simple green square rectangle:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
  <rect width="128" height="128" fill="green"/>
</svg>

Also in this case, Safari rejects the font when there are more than 2000 embedded svg documents, as noted in above in #276 (comment)

However, this time the cut-off number of glyphs per document after which the font gets rejected is no longer 69 (as in with the real emojis), but 500.

Screenshot 2021-04-16 at 13 06 10

It can't be the max document size, because in an ealier test the OpenMoji-Color-68.ttf (using real emojis grouped in buckets of 68 glyphs) was working good but had a larger maximum length of 371,156 bytes.

I attach the test font and html file below:

safari-svg-max-limits.zip

I literally have no idea what's going on. 😞

@anthrotype
Copy link
Member Author

Quick update. I found yet another hard-coded limit in Safari implementation of OT-SVG.
This time it's the number of elements within each SVG glyph: if this is greater than 500, the SVG glyph is not displayed. The other glyphs are displayed so the font is not outright rejected.

Here are some test fonts that reproduce this:
safari-max-glyph-size 2.zip

Each font contain just two glyphs, one glyph contain a single element, the other glyph contain more or less than 500 elements. When the number of element in one glyph exceed 500, the glyph is rejected.

I want to point out that it's very common for emoji glyphs to contain more than 500 elements, especially for country flags that usually contain lots of detail (arguably not all necessary).

I think this limitation is even worse than the max 2000 svg documents per font, since one can at least try work around the latter by grouping (even though one cannot know in advance what groupings will work because Safari is a black box).

For the max 500 elements per glyph, there is no workaround.

@yisibl
Copy link
Contributor

yisibl commented Apr 19, 2021

Thanks to @anthrotype for such in-depth research, your spirit is admirable.

If this problem cannot be reproduced stably, I suggest to shelve it for now. You can add some instructions in the readme to tell the user (designer) that you need to reduce the number of elements in the glyph when designing the icon to avoid the problem that Safari may not be able to display it.

@anthrotype
Copy link
Member Author

We can't write instructions if we don't know exactly what we're dealing with. Also, instructions won't fix the problem for the --color_format=picosvg where we group multiple glyphs that share same shapes in the same svg document. There we need to know how far we can safely go before we hit those limits.
For the --color_format=untouchedsvg we currently don't do any groupings and leave one svg document for each individual SVG glyphs in the input, but that immediately rules out emoji fonts which normally have more than 2000 glyphs (and we now know Safari rejects if SVG table has more than 2000 svg documents in total).

@twardoch
Copy link

I think the 68 glyphs limit per document is most unfortunate! In OT+SVG, within the same SVG document, I can define various repeatable things in the defs element, which includes "base shapes" for composites, each in a g element, but also more complex fills like gradients.

Then, in the same document, I can build composite glyphs from those defs by simple use. This works similar to CFF subroutines. But glyph descriptions for all base letters and marks for even just Latin would need to be in the same SVG document. If only up to 68 glyphs can be in one SVG doc, then the whole concept of re-using assets within the same SVG doc gets defeated, and the same definitions need to be repeated in each SVG doc.

68? Really? :D

@anthrotype
Copy link
Member Author

No, it's not 68 glyph per SVG doc. It's actually worse than that.

Let me recap what I have found out so far in my experiments.

  1. max 2000 SVG documents per font, otherwise Safari rejects whole SVG table.

Then there are two limits that apply for each SVG document individually, i.e. make Safari not display a particular SVG document and all the glyphs defined within it, but still display the other ones that don't exceed the limits:

  1. max 500 children per element: i.e. any element anywhere in the tree, including at the root <svg> level. So if each glyph is defined within a <g> element, you can't have more than 500 glyphs, or actually 499 if you have a top-level <defs> where you share shapes/gradients across multiple glyphs.

  2. max 1000 elements in total per SVG document: this one is new, and it's the reason why the test font "OpenMoji-Color-69.ttf" that I linked to in my earlier comment Safari rejects OT-SVG if contains more than 2000 documents #276 (comment) does not work, whereas the one containing 68 glyphs per SVG document does.

Here's what I did to verify the last limit of 1000 elements per doc. I took the "OpenMoji-Color-69.ttf" file and identified the svg document which contains all the three smileys from the screenshot (😁😃😄), this is SVG.docList[28] and has total number of elements 1014. I chopped off elements from another glyph also defined in the same svg document, until I noticed that the the other three reappered as soon as the total number of elements in the doc was <= 1000.

This is how the test fonts look like in FireFox, as expected:

Screenshot 2021-04-20 at 17 11 07

This is how they look in Safari:

Screenshot 2021-04-20 at 17 11 39

All the 4 smileys are part of the same svg doc. As soon as the total number of element exceeds 1000, none of them gets displayed. The first color glyph (the hourglass) is in a document that has less than 1000 elements in all the four test fonts, so it still gets displayed.

you can find the test fonts and html file in here:
safari-max-1000-elements-per-doc.zip

@anthrotype
Copy link
Member Author

anthrotype commented Apr 20, 2021

Note that the total count for max 1000 number of elements per SVG doc also includes the root <svg> element. I counted them with len(list(svg.iter())) using lxml library. The etree.Element::iter method yields each element in document order including the one where the traversal begins.

@anthrotype
Copy link
Member Author

anthrotype commented Apr 26, 2021

I found yet another hard-coded limit in Safari OT-SVG implementation. If any embedded svg document exceeds 200 KB (200 * 1024 bytes) in length it is rejected and not displayed.

Here's a test font containing only two color glyphs respectively for Andorra and Portugal's flags, 🇦🇩 and 🇵🇹, each contained in a separate svg document in the OT-SVG table. One document weighs 192,719 bytes (Portugal's flag), the other weighs 214,216 bytes (Andorra's flag).

This is how they should look (in Firefox):
Screenshot 2021-04-26 at 19 38 54

This is how they look in Safari, notice the Andorra flag which exceed 200 KB in size has disappeared:
Screenshot 2021-04-26 at 19 39 10

The test font, html file and the original SVG input files can be found here:
safari-svg-max-200KB.zip

the test font was built with nanoemoji --color_format=untouchedsvg *.svg

Note that these particular SVGs do not exceed any of the other limits which I have found early on (the max 1000 elements per document and the max 500 children per element), so I'm convinced that this has to do with the overall size of the SVG documents.

@litherum
Copy link

Please try this on the beta versions of macOS Monterey.

@twardoch
Copy link

Safari Version 15.4 (17613.1.17.1.13) on macOS Monterey 12.3.1 renders it fine.

scr- 2022-04-12 at 15 22 38

@anthrotype
Copy link
Member Author

I finally retried all the test fonts in this issue with the current Safari 15.5 on macOS Monterey 12.4 (the latest versions that I could test today, 20 July 2022) and I can confirm all the limits mentioned here (maximum 2000 documents per SVG table, maximum 500 children per element, maximum 1000 elements per SVG document), have been lifted. I haven't had the chance to hit new hard-coded limits yet; until we do, we should consider this fixed.

Thanks @litherum for fixing all of these!

@rsheeter
Copy link
Collaborator

Awesome, ty @litherum!

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

No branches or pull requests

6 participants