-
Notifications
You must be signed in to change notification settings - Fork 492
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: Support for Mark to Base Attachment Positioning (incl. GPOS 4, 9) #557
base: master
Are you sure you want to change the base?
feat: Support for Mark to Base Attachment Positioning (incl. GPOS 4, 9) #557
Conversation
- added GPOS LookupType 4 parser for MarkBasePosFormat1 support - added GPOS LookupType 9 parser for Extension Positioning SubtableFormat1 support - tests coverage for all supported and not supported lookups of GPOS TODO: Add a support device offsets for anchor (Parser.anchor) table format 3.
- introduced an union, ordered features lookup list - refactored the Font options features update method - supported kerning table - supported kern lookup table - supported mark to base attachments
- fix for kern sequence - tests coverage
Does this make #491 redundant, or do we have to consolidate the two? Could you please have a look at each other's code @rafallyczkowskiadylic & @AlexandreRivet? |
@rafallyczkowskiadylic I noticed that rlig is missing an implementation for latin scripts and liga is missing an implementation for arabic scripts, is that also fixed in this PR? |
@Connum #491 is related and probably redundant. But initially I was reviewing it and it felt like not fully covered and bounding with kern feature only. This MR covers positioning feature in first place, and GPOS 9 was required to be supported along with the GPOS 4. This MR fully covers what was proposed in https://github.com/opentypejs/opentype.js/pull/491/files My proposition for modus operandi: @ILOVEPIE These two MRs are considered as separate chunks of features. I will fix whatever will be needed starting with the smaller one #535 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could only find one issue with the code besides some naming choices for constants. I'm going to get @Connum to review this as well as it's a larger PR and I want to make sure that it's ok.
@@ -279,7 +282,8 @@ Font.prototype.defaultRenderOptions = { | |||
* and shouldn't be turned off when rendering arabic text. | |||
*/ | |||
{ script: 'arab', tags: ['init', 'medi', 'fina', 'rlig'] }, | |||
{ script: 'latn', tags: ['liga', 'rlig'] } | |||
{ script: 'latn', tags: ['liga', 'rlig'] }, | |||
{ script: 'DFLT', tags: ['mark'] }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to microsoft documentation dflt is lowercase.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ILOVEPIE I could not find this. I believe you might have referred to the langSysTable (tag) and this is a script tag? Moreover:
- when looking into specs, there are refs to the uppercased values only or here (screen)
- when verified with the font source definitions, found also uppercased (screen)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://learn.microsoft.com/en-us/typography/opentype/spec/scripttags it's lowercase for all other tags, but DFLT is uppercase in the docs
src/parse.js
Outdated
xCoordinate: this.parseShort(), | ||
yCoordinate: this.parseShort(), | ||
xDevice: 0x00, | ||
yDevice: 0x00, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when parsing these values, we have to call this.parseShort()
to advance the parser past the xDevice and yDevice or subsequent reads will be at the wrong location.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! I will fix shortly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. Pointer is moving now
* https://learn.microsoft.com/en-us/typography/opentype/otspec191alpha/chapter2#lookup-list-table | ||
* | ||
* @param {string[]} requestedFeatures | ||
* @param {string} [script='DFLT'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dflt should be lower case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
according to the spec it's uppercase: https://learn.microsoft.com/en-us/typography/opentype/spec/scripttags
Also, @rafallyczkowskiadylic does this PR fix #501, because if you're redoing this code anyways, it might be worth fixing it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have to admit that all that byte-level-parsing stuff is a bit out of my comfort zone (yes, despite me having implemented a few parsing features before ;)), but other than @ILOVEPIE's comments and those I added, there's nothing that caught my eye.
src/layout.js
Outdated
return []; | ||
} | ||
|
||
// Fitler out only supported by layout table features |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just y small typo, Fitler => Filter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
@rafallyczkowskiadylic the feature order is quite a critical thing to add before we start working on implementing more features like |
Sure, that's fine! We don't have to support everything at once when implementing a new feature or format. We should just make sure that users are notified about any missing/unsupported functionality. I just wanted to avoid that anything was misusing that was supposed to be in this PR already. |
@Connum Perfect! Great you are on top. So, let's address what is pending now. We stay in touch |
# Conflicts: # src/font.js # test/layout.js
# Conflicts: # src/font.js
I will check #501 |
@Connum @ILOVEPIE added few more commits with the substitution algorithm fixes and a new substitution format support. I haven't refactored the other working code (just the required bits). We can find few spots where we see "similar" functionalities. For example:
I believe we could move towards:
Not sure if Well this can open a discussion, but wanted to share some thoughts. |
I'm totally for unifying duplicate functionality. The way that feature processing is currently structured is something that has deterred me from working on new font features because it frankly seems a bit messy and not very intuitive to me. However, I would see that as a separate step from this PR. Let's get this support in first and consolidate feature processing in the future. Otherwise there might be too much code to review at once, and we have enough open and partly almost-finished PRs lying around as it is. That's my opinion and not the final say, of course. edit: after a quick glance at the code changes, it looks to me like you have already done most of the work described in your previous comment? Of course let's keep the work you already put in there and just move any further consolidation to somewhere down the line. |
This is blocking quite a few PRs, we should probably get this merged sometime soon. |
Thank you guys for the comments. Yes, most of suggestions/work has been done. I have consolidated functionality as described introducing more generic functions to handle/fix specifications. I will resolve conflicts soon. |
Great, looking forward to it! Thanks for your work! |
Fixes #501 according to #501 (comment) @rafallyczkowskiadylic it's been a couple of weeks - just a friendly nudge. :) |
* https://learn.microsoft.com/en-us/typography/opentype/otspec191alpha/chapter2#lookup-list-table | ||
* | ||
* @param {string[]} requestedFeatures | ||
* @param {string} [script='DFLT'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
according to the spec it's uppercase: https://learn.microsoft.com/en-us/typography/opentype/spec/scripttags
@@ -279,7 +282,8 @@ Font.prototype.defaultRenderOptions = { | |||
* and shouldn't be turned off when rendering arabic text. | |||
*/ | |||
{ script: 'arab', tags: ['init', 'medi', 'fina', 'rlig'] }, | |||
{ script: 'latn', tags: ['liga', 'rlig'] } | |||
{ script: 'latn', tags: ['liga', 'rlig'] }, | |||
{ script: 'DFLT', tags: ['mark'] }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://learn.microsoft.com/en-us/typography/opentype/spec/scripttags it's lowercase for all other tags, but DFLT is uppercase in the docs
I was finally able to test this more thoroughly... I merged it into my experimental fork after resolving some conflicts. The chars in the GPOS–3 test of the unicode test suite still rendered wrong, and they also didn't work in our demo page. So I had to make a few changes to make it work, see However, these calculations are quite expensive, even before my additions, because they are done for each single glyph. I don't think that's necessary and we should implement ways to at least cache this, or best only call the feature update when something has changed. |
Ok, now the tests for disabling the mark feature are failing of course, because I implemented it so that default features cannot be disabled. We'll have to talk about how we want to handle this. |
The more I look at this, the more confused I get... 😅 I think we still need some cleaning up before we can finally merge this, but I'm lost here without you @rafallyczkowskiadylic. I tried to fix https://rawgit.com/unicode-org/text-rendering-tests/master/reports/OpenType.js.html#GSUB-1, but I got caught up in spaghetti code for several hours trying to follow the path of the substitution mechanism and got confused between old and new functionality. e.g. we should really get rid of this:
Regarding this substitution case, I started wondering why we split into words in the first place? The space glyph being part of a substitution is perfectly fine, there's no rule against it to my knowledge. I know that's the crux of open source, but I wouldn't be such a pest if it wasn't blocking the progress so much. A clear structure for substitution and positioning as well as supporting all the different formats is really crucial for implementing further features. |
By the way, trying to get rid of the latn-specific
in analogy to
But then all ligatures stopped working... |
Hey @Connum I am on it. Let me resolve conflicts and see what is on the code. I will revert back by the end of the weekend |
Resolve conflicts
@Connum Conflicts resolved. Pending items:
It was implemented like that, and the reason for this is that within single sentence there may be characters from a different language context. Example:
Moreover, please keep in mind supported substitution formats.
Different fonts' source files may have these definitions in a different formats that eventually may not be supported yet. @Connum if you could share all your use cases, with results you expect to see and source font files - I can review all of them and eventually address. For the clean up I think we could just mark functions as deprecated for now. I'd refactor only bits covered with reliable tests so we can actually rely on outcomes before/after we change the code (this was also the reason I'd rather applied the code and "promoted" than removing or refactoring old functioning pieces). |
Thanks for your work! You can check out the unicode test suite: https://github.com/unicode-org/text-rendering-tests It does exactly what you proposed, compare SVG output with expected results. You can replace the opentype.js files in the node_modules folder with the files compiled from your dev branch (also the files in /bin/) to see if everything related to GSUB passes. The repo also provides the test files. If you're able to fix this with this PR, you should copy the file to our tests folder (it's licensed under OFL) and add our own test. But it's OK if we just do the refactoring and mark to base first if it holds us up too much. But I'll definitely need help with that space thing as well. :) |
See my reply! 😉 But it's OK if we stick to GPOS mark-to-base with this PR and tackle the rest later. I'll review the code tomorrow. |
I don't really like the idea of having both approaches in the code base in parallel. The arab and latn features should work with the new approach as well, so we should move everything to work with it. I understand if this is more than you're willing to do. Could you mark the PR as allowing maintainers to make changes? Then me or someone else could look into fixing this for all the scripts. |
Shouldn't I be able to change
and it should just work? But the ligature tests then fail. |
- script: latn, was not matching latinWord
Actually I checked and found few tests failing for your cases, for In other word, addLigature is using
yet If |
Description
Added support for Mark to Base Attachment Positioning (for the DFLT script). Feature is enabled by default because it is a part of a proper glyphs positioning. Includes capabilities implementation for the corresponding parsers (GPOS lookup 4 & 9)
Followed the current solution's convention to introduce new features within the font & layout implementation.
LookupType 4
parser forMarkBasePosFormat1
supportLookupType 9
parser for Extension PositioningSubtableFormat1
supportmultiSubstitutionFormat1
getFeaturesLookups
) for the layout to meet the OT specsTODO: Add support for non DFLT scripts for
MARK
(src/font.js:344
)TODO: Add a support device offsets for anchor table format 3 (
src/parse.js:515
)TODO: Add MarkToMark Attachment positioning (lookup type 6) functionality.
Motivation and Context
Positioning changes
Fonts like Mandarin or Thai to successfully render text with all glyphs (sometimes 3+) representing characters like
ปั
orริ่
requiring the
LookupType 4
for mark to base attachment positioning to do it properly.Extension subtables support is required to support the subtables that exceeds the 16-bit limits of the various other offsets in the GPOS table - that genuinely are being used eg. for the Apples SF Pro fonts and many others.
Substitution changes
There was an issue with the current algorithm where it was collecting all substitutions from all lookup tables and after that each substitution was conducted. This is invalid. Because it should apply substitution for each lookup table before moving to the next lookup table. In other words: substitution should be applied before moving to the next lookup table
Specification:
Ref: https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#table-organization
The union lookup feature
The union lookup feature (
getFeaturesLookups
) was required actually to meet the OpenType specification which guarantees that all enabled features will be processed in a valid order both for positioning and substitutions.How Has This Been Tested?
Added tests coverage to the
test/parser.js
andtest/tables/gpos.js
including new binary data and testing:These lookups uses existing structures like coverage tables, and were tested agains different formats. Extension positioning lookup was tested agains all currently supported and not supported subtable parsers.
Added tests coverage to the
test/layout.js
to test the method for union lookups (getFeaturesLookups
).Added tests coverage for glyph positioning tests in the
test/font.js
including:options.kerning
andoptions.features.kern
testingoptions.features.mark
testingAdded tests coverage to the
test/bidi.js
to testmultiSubstitutionFormat1
glyphs decompositionMoreover there were many test conducted for the rendered content using
latn
andarab
fonts.Screenshots (if appropriate):
Positioning issues
BEFORE (INVALID glyphs pos):

AFTER (FIXED):

Substitution issues
BEFORE (INVALID) - very end glyph is a single glyph that should be decomposed into two.

AFTER (FIXED) - decomposed into two glyph:

Types of changes
Checklist:
npm run test
and all tests passed green (including code styling checks).