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

Flip misnamed is_bitmap_embedding_allowed function, increase lenience of embed permissions for older OS/2 versions #137

Merged
merged 2 commits into from
Jan 1, 2024

Conversation

Fuzzyzilla
Copy link
Contributor

After referencing this Microsoft fsType specification, I noticed an inconsistency in the functions {Face, os2::Table}::is_bitmap_embedding_allowed which seemed to have an inverted interpretation of the flag's meaning. The checked flag, if set, really means "is embed required to be bitmap". Since that name isn't very clear either, I inverted the name to is_outline_embedding_allowed while keeping the logical return value unflipped.

Further, the specification allows much leniency for earlier versions of the OS/2 table, this PR adjusts the logic of os2::Table::{permissions, is_subsetting_allowed, is_outline_embedding_allowed} to take advantage of this to be as lenient as allowable for embeds.

After these changes, I am not sure the default values for when the table isn't found are correct. I assumed that, in a version prior to the flags' introduction, both subsetting and outline embedding are allowed due to the negative naming of the flags (eg. "no subsetting" implies subsetting is the default) but I was unable to find a resource that states this outright.

// Flag introduced in version 2
true
} else {
let n = Stream::read_at::<u16>(self.data, TYPE_OFFSET).unwrap_or(0);
Copy link
Owner

Choose a reason for hiding this comment

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

Wouldn't we always return true for old versions because of unwrap_or(0)? I agree that your code is more readable, just clarifying the logic.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Old versions (specifically versions 0 and 1) neglected to specify that unused bits are zeroed, so they could have any value: "Applications must ignore bits 4 to 15 when reading a version 0 or version 1 table."
While unlikely any fonts in the wild actually did write non-zeroes here, might as well be strict with the implementation x3

Copy link
Owner

Choose a reason for hiding this comment

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

Oh right, my bad. I thought we're slicing the exact length, therefore read out of bound would always return 0.

@RazrFalcon
Copy link
Owner

RazrFalcon commented Dec 25, 2023

I personally have no idea how it should work. And it's hard to test. Will see how CoreText handles it.
If you can check what other libraries return - it would be nice. Like DirectDraw, FreeType, etc.

@Fuzzyzilla
Copy link
Contributor Author

I have checked FreeType, fontTools, and FontForge for their behaviour - forgive me if I am missing obvious ones, I am not a usual visitor to the realm of font parsing!

  • FreeType - the fsType flags are not parsed directly, instead yielded to the user as-is along with a bunch of bitflags to check against. Documentation does not hint at the version differences, though no relevant behavior since it's up to user code to determine how to handle old versions of the flags.
  • fontTools - when merging fonts, the no-subset/bitmap-only bits are accessed but merely by copying over to the merge result instead of choosing the strictest. In particular though, for versions 0 thru 2 without the flag, it sets them to zero (most permissive)
  • FontForge - writes and parses the no-subset/bitmap-only bits regardless of OS/2 version (checked by experimentation)

In general, it seems that the support of these flags is a bit hit-or-miss in terms of adherence to the Microsoft spec I posted above!

@RazrFalcon
Copy link
Owner

I took a closer look and indeed is_bitmap_embedding_allowed is flipped. Probably just copy-pasted is_subsetting_allowed.

As for other apps, I couldn't find a similar API in CoreGraphics/CoreText. Font Book on macOS and Explorer on Windows do show font's permissions, but I don't have a font with the bitmap bit set. Do you have any?

I am not a usual visitor to the realm of font parsing!

Well, then welcome to a barely documented mess. You might be overwhelmed by how much Microsoft spec documents, but it's just a tip of an iceberg.

@Fuzzyzilla
Copy link
Contributor Author

I have made up some barebones test fonts with the relevant flags set here. (I figured it would be legally dubious to set these flags on an actual font i just grabbed somewhere :P )
They are both OS/2 Version 4 despite my best efforts to limit them to a lower version, but hopefully this helps!

If rather you meant had I ever encountered these fonts in the wild, I can't say I have - I am just in the process of writing a software which may need to perform font embedding and I would like to respect the licensing rules of any font the user may import. On my current system, all of my fonts are FOSS, so certainly none there have such restriction for me to test!

@RazrFalcon
Copy link
Owner

RazrFalcon commented Dec 27, 2023

Interesting. Seems like Apple recognizes this flag. Here is what Font Book.app shows:

TestFontNoSubsetNoOutlines.ttf:

No embedding restrictions. The font may not be subsetted prior to embedding. Only bitmaps contained in the font may be embedded. No outline data may be embedded.

TestFontNoSubset.ttf:

No embedding restrictions. The font may not be subsetted prior to embedding.

TestFontNoOutlines.ttf:

No embedding restrictions. Only bitmaps contained in the font may be embedded. No outline data may be embedded.

and just in case, here is Apple Color Emoji for the reference:

Preview and print embedding. This font may be embedded in documents and temporarily loaded on the remote system. Documents containing this font must be opened “read-only;” no edits can be applied to the document.

And its fsType is 0x04, aka only Preview & Print is set.

If rather you meant had I ever encountered these fonts in the wild, I can't say I have

Yeah, finding a real-world file that actually uses a specific feature is one of the challenges.

@Fuzzyzilla
Copy link
Contributor Author

I managed to get FontForge to export with OS/2 version 1 after much fiddling (not sure what i did to make it happen, the version dropdown doesn't actually work!) Please give it a test :3

Notably, I set the flags even when they're invalid to set on this version. FontForge reads them regardless, as does current master, curious to see the behavior of your font viewer apps in this situation.

@RazrFalcon
Copy link
Owner

No embedding restrictions. The font may not be subsetted prior to embedding. Only bitmaps contained in the font may be embedded. No outline data may be embedded.

So the same as TestFontNoSubsetNoOutlines.ttf.

@Fuzzyzilla
Copy link
Contributor Author

So I guess the question now is whether behavior should match the spec to the letter, or follow what the rest of the ecosystem does - it seems most others parse and use the bits regardless of OS/2 version, as current master does. My purpose for following the spec was to achieve maximum leniency - allow embedding wherever possible, and sealing up those little edge-cases to make it more likely that embedding was allowed while still following the letter of the law :3

@RazrFalcon
Copy link
Owner

I think we can keep checking OS/2 version. It doesn't add much complexity and not many fonts still use old OS/2 versions.

@RazrFalcon RazrFalcon merged commit f892c83 into RazrFalcon:master Jan 1, 2024
2 checks passed
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.

2 participants