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

Font.setKerningValue(l,r,v) method #95

Open
mattlag opened this issue Feb 11, 2015 · 50 comments
Open

Font.setKerningValue(l,r,v) method #95

mattlag opened this issue Feb 11, 2015 · 50 comments

Comments

@mattlag
Copy link
Contributor

mattlag commented Feb 11, 2015

Would be nice to have a method for setting kern pair values: Font.setKerningValue(leftglyph, rightglyph, value). It looks like this is possible by manually editing the Font.kerningPairs object... but i'm not sure if there is some other internal stuff (GposKerningValue?) that also needs to be updated.

@davelab6
Copy link

The Kern table is deprecated, GPOS kerning should be used instead :)
On Feb 11, 2015 5:29 PM, "Matt LaGrandeur" [email protected] wrote:

Would be nice to have a method for setting kern pair values:
Font.setKerningValue(leftglyph, rightglyph, value). It looks like this is
possible by manually editing the Font.kerningPairs object... but i'm not
sure if there is some other internal stuff (GposKerningValue?) that also
needs to be updated.


Reply to this email directly or view it on GitHub
#95.

@mattlag
Copy link
Contributor Author

mattlag commented Feb 11, 2015

I'm looking through the opentype.Font object, there doesn't seem to be a GposKerning object (only a getGposKerningValue method). There is a kerningPairs object that appears to hold kern values. But i'm not sure how internally that maps to exported font features.

I'm going to try directly accessing it.. but like I said, it would be better if there was some sort of official method, as opposed to me just poking around :-)

@fdb
Copy link
Contributor

fdb commented Feb 12, 2015

Kerning values can be stored in two ways, either through the (deprecated) kern table or the (complicated) GPOS table. Currently we only have read support for both.

There is no way to set them so they will be saved, since we simply have no write support for GPOS. It should be added, but I haven't found the time yet.

@mattlag On a related note, last year I've been able to implement font writing in opentype.js through the Prototypo Kickstarter project. I'm open to discuss sponsored development of specific features.

@mattlag
Copy link
Contributor Author

mattlag commented Feb 12, 2015

Thanks for the kerning info, @fdb. At the moment, Glyphr Studio is a spare time project, and doesn't have any sponsoring capability :-)

@davelab6
Copy link

@fdb do you prefer cash to sponsor it, or do you prefer someone else to develop it and make a pull request?

@fdb
Copy link
Contributor

fdb commented Feb 12, 2015

@mattlag no problem at all. opentype.js is currently a spare time project as well :-)

@davelab6 both suggestions work for me! Since I have a job in academia, I can do sponsored development during the summer holiday, which is between July and August.

@davelab6
Copy link

@felipesanches are you interested?

@felipesanches
Copy link

Yes, I'm interested in working on this. I'll take a look at this today and see if I can do it or if I have any technical question.

@davelab6
Copy link

@felipesanches to be clear, this means implementing write support for the GPOS table.

https://github.com/behdad/fonttools/ has support for this in python, so basically the feature request is to port the fontTools code to opentype.js

@felipesanches
Copy link

great!

@fdb
Copy link
Contributor

fdb commented Jun 26, 2015

Awesome 👍

@mattlag
Copy link
Contributor Author

mattlag commented Jun 26, 2015

😄 🎉

@davelab6
Copy link

I see that #112 and mapbox/node-fontnik#85 are related

@felipesanches
Copy link

Ok... I've been reading the specs and getting used to the source code and now things are a bit clearer.

For starters, the proposed setKerningValue method will probably require the user to pass a 2-dimensional vector (dx, dy) because the kerning adjustment can also happen in the Y-axis.

We may accept both types of data, defaulting dy to zero if a single value is passed instead of the pair of offsets.

@felipesanches
Copy link

In order to implement the makeGposTable function (that encodes the GPOS table) we need access to the relevant data. Given that our current GPOS parser is incomplete, the output GPOS table will be incomplete as well.

@felipesanches
Copy link

While we may be happy about parsing only a portion of the table, the opposite may not be true. I guess that OTF files with incomplete GPOS tables might annoy the parsers of other programs. Anyone here knows if this is the case?

We may consider improving the parser before actually implementing the encoder.

@felipesanches
Copy link

Even if we decide to implement the encoder without improving the parser first, the current parsing code will have to be modified. That's because currently it exposes the kerning values by dinamically creating a getKerningValue function that operates on the parsed data that is only accessible to this function because it is trapped in its bound subtables variable (by creating a closure).

In order to output the table we need direct access to the parsed data.

@felipesanches
Copy link

I'm working on an implementation of the GPOS encoder at this feature branch:
https://github.com/felipesanches/opentype.js/tree/makeGposTable

I'll keep ammending that in the upcoming days until I reach a state that I consider acceptable for a pull-request.

@davelab6
Copy link

davelab6 commented Jun 30, 2015 via email

@felipesanches
Copy link

yes, I've been studying fontTools as well.

Regarding the Latin features, I agree that's a good start. My point is that perhaps void featureList and scriptList entries may lead to programs considering the file corrupt. Or even crashing perhaps. I have no idea. Would be good to know if it is safe to output a GPOS table without those entries.

@davelab6
Copy link

davelab6 commented Jun 30, 2015 via email

@felipesanches
Copy link

Regarding 2d verctors...

"To properly render Urdu, a text-processing client must modify both the horizontal (X) and vertical (Y) positions of each glyph."
https://www.microsoft.com/typography/OTSPEC/gpos.htm

@khaledhosny
Copy link

Are you sure 2d vectors? I thought it was either h or v, not both at once, but might be wrong.

You have horizontal and vertical advances and offsets, so 4 possible values to modify and all can be set at once.

@davelab6
Copy link

davelab6 commented Jul 1, 2015 via email

@mattlag
Copy link
Contributor Author

mattlag commented Jul 1, 2015

@khaledhosny what is the difference between an advance and an offset?

@khaledhosny
Copy link

The offset affects the X and Y position where the glyph is drawn (without affecting the position of the next glyph). The advance controls how much the line advances after the glyph i.e. controlling where the next glyph will be drawn.

@mattlag
Copy link
Contributor Author

mattlag commented Jul 1, 2015

I'm not sure i'd expect to set a glyph's advance through a kerning function - correct? Probably just x and y offsets (with zero as the default).

If the Kerning value between two glyphs is reduce by, say, 100, the advance width of the left glyph overlaps with that of the right glyph. It's not like the right hand glyph gets drawn at -100, but the two advance widths are still added as if no kerning took place. The overall net of both advance widths is also -100.

In my mind the advance width is a property of a glyph. And contextual spacing between glyphs is Kerning. But there is no such thing as contextual advance widths (or maybe i'm wrong, i'm not super familiar with all the OpenType features)

@khaledhosny
Copy link

This is advance width adjustment for specific context e.g. a kerning pair, not the actual glyph advance which is stored elsewhere in the font. Actually Latin kerning is simply changing the advance width of the first glyph in the kerning pair.

OpenType have even more generalised contextual positioning which can be used for kerning as well, pairwise positioning is just a kind of shortcut for the common case.

@felipesanches
Copy link

"A pair adjustment positioning subtable (PairPos) is used to adjust the positions of two glyphs in relation to one another-for instance, to specify kerning data for pairs of glyphs. Compared to a typical kerning table, however, a PairPos subtable offers more flexiblity and precise control over glyph positioning. The PairPos subtable can adjust each glyph in a pair independently in both the X and Y directions"

@mattlag
Copy link
Contributor Author

mattlag commented Jul 2, 2015

Interesting, so the full arguments would be something like (Glyph1x, Glyph1y, Glyph2x, Glyph2y) with the basic Kerning scenario only requiring Glyph2x, and default to 0 for everything else.

@khaledhosny
Copy link

You need four values for each glyph in the pair (x offset, y offset, x advance adjustment, y advance adjustment).

@behdad
Copy link

behdad commented Jul 4, 2015

To model what most people recognize as kerning you just need the original proposed Font.setKerningValue(l,r,v). I don't think there's any point in supporting more advanced GPOS positioning using a simplified API like this.

@khaledhosny
Copy link

But that wouldn't support RTL kerning. OK may be the API can have an optional direction parameter and when it is RTL set both the X offset and advance adjustment of the first glyph in the pair.

@behdad
Copy link

behdad commented Jul 4, 2015

But that wouldn't support RTL kerning. OK may be the API can have an optional direction parameter and when it is RTL set both the X offset and advance adjustment of the first glyph in the pair.

The API doesn't need a direction, whereas the code generating GPOS lookups from these numbers should take script direction into consideration when adding lookups to scripts.

From an API point of view, there's no ambiguity what it means by leftGlyph, rightGlyph, and value.

@khaledhosny
Copy link

I’m not concerned about which is the left and which is the right glyph, but rather which PairPos values the function will set. For LTR kerning only the x advance adjustment of the first (left most glyph) need to be set, but in LTR kerning both the x advance adjustment and the x offset of the first (right most glyph) need to be set. So the API need to address this in a way or another.

@behdad
Copy link

behdad commented Jul 8, 2015

I’m not concerned about which is the left and which is the right glyph, but rather which PairPos values the function will set. For LTR kerning only the x advance adjustment of the first (left most glyph) need to be set, but in LTR kerning both the x advance adjustment and the x offset of the first (right most glyph) need to be set. So the API need to address this in a way or another.

I understand that. But again, I don't think it needs to be reflected to the API.

Look at it this way: to adjust the rsb of a glyph, you only change X advance. To adjust the lsb, you update both X advance and offset. This is a universal rule that can be deducted from the OpenType glyph model.

Now, what this means for GPOS kerning is straightforward: if you are making changes to the left-side glyph, you update advance only. If you are changing the right-side glyph, you update both.

My point: no need to reflect any of this in the API.

@khaledhosny
Copy link

But the fact that the glyph being adjusted will end up being the rightmost or leftmost glyph in this case is only known at the layout time when the glyph stream is reversed (the font developer knows this beforehand of course because he can tell which glyphs will be used in RTL context).

Lets say I was to kern the Latin AV pair, here the first glyph in the pair is A which it will be the leftmost glyphs, and the second is V which will be the rightmost glyph, so nothing special is needed and I’ll be calling Font.setKerningValue("A", "V", 50).

Now, lets kern the Arabic <REH><ALEF> (را) pair, here the first glyph in the pair will be <REH> but it will end up being the rightmost glyph, and the second is <ALEF> and it will end up being the leftmost glyph. How can I kern this pair with the proposed API? Naturally I’d be calling Font.setKerningValue("REH", "ALEF", 50) but there is no way to tell that in this pair <REH> will be on the right and <ALEF> will be on the left. Calling Font.setKerningValue("ALEF", "REH", 50) does not help either as it will just result in an <ALEF><REH> kern pair (which is not what we want here) and it will still be adjusting the rsb of the first glyph it sees.

@graphicore
Copy link
Contributor

The API doesn't need a direction, whereas the code generating GPOS lookups from these numbers should take script direction into consideration when adding lookups to scripts.

I think this is the core of the problem. Is it always possible to deduce the correct direction for a kerning pair? How does it work?

@davelab6
Copy link

davelab6 commented Jul 8, 2015

@felipesanches you might like to review foliojs/fontkit#7

@behdad
Copy link

behdad commented Jul 9, 2015

Now, lets kern the Arabic (را) pair, here the first glyph in the pair will be but it will end up being the rightmost glyph, and the second is and it will end up being the leftmost glyph. How can I kern this pair with the proposed API? Naturally I’d be calling Font.setKerningValue("REH", "ALEF", 50)

That would be wrong. The API specifically says that the first argument is leftGlyph, and second argument is rightGlyph.

but there is no way to tell that in this pair will be on the right and will be on the left. Calling Font.setKerningValue("ALEF", "REH", 50) does not help either as it will just result in an kern pair (which is not what we want here) and it will still be adjusting the rsb of the first glyph it sees.

The code generating the feature files MUST know that under 'arab' script, first glyph is the rightmost one. So when encoding a lookup to be used under 'arab', it encodes the rightGlyph first. That has nothing to do with the API. And I'm not going to repeat that again :).

I'll add some more comments on unified-font-object/ufo-spec#16 with a more interesting examples (punctuation marks).

@behdad
Copy link

behdad commented Jul 9, 2015

Here:
unified-font-object/ufo-spec#16 (comment)

I hope I don't have to repeat myself more than this. :)

@behdad
Copy link

behdad commented Jul 9, 2015

But the fact that the glyph being adjusted will end up being the rightmost or leftmost glyph in this case is only known at the layout time when the glyph stream is reversed (the font developer knows this beforehand of course because he can tell which glyphs will be used in RTL context).

Re "he can tell which glyphs will be used in RTL context" that's a completely different argument. For most glyphs the script is known to Unicode. For script=Common characters, sure, the user can give a hint as to which punctuation and other marks one wants to be included in kern table for each script. But all of these are really separate APIs. They do not justify the change suggested here.

@graphicore
Copy link
Contributor

The code generating the feature files MUST know that under 'arab' script, first glyph is the rightmost one. So when encoding a lookup to be used under 'arab', it encodes the rightGlyph first. That has nothing to do with the API. And I'm not going to repeat that again :).

and

For script=Common characters, sure, the user can give a hint as to which punctuation and other marks one wants to be included in kern table for each script. But all of these are really separate APIs. They do not justify the change suggested here.

I hope I'm not too pesky here. But how does this API know which script applies for the call to Font.setKerningValue(l,r,v)? Is there some hidden state, like a call to Font.setActiveScript('ARAB') before? Or the unicode database?

It seems that the other APIs are not that separate at all. There must be somewhere the knowledge how to construct the right lookup style and you are missing to tell us where this should happen. All you say is "not here" BUT "the user can give a hint". Where can the user do so and how is that separate then?

@behdad
Copy link

behdad commented Jul 9, 2015

See my reply here: unified-font-object/ufo-spec#16 (comment)

@skosch
Copy link

skosch commented May 13, 2019

Did this ever go anywhere? I am building a font metrics-related web interface and it would be really useful to be able to render text with sidebearing and kerning values set on the fly (alternative suggestions welcome)!

@dwn
Copy link

dwn commented Jul 23, 2019

Doesn't matter if it's called the first glyph or the left/right/top/bottom glyph, just wish something were possible. This method would've been so useful. Is it not possible to set a simple kerning on a font using JS?

@mattlag
Copy link
Contributor Author

mattlag commented Jul 29, 2019

Yeah, seems like there has been no movement here for 4 years... re-reading the thread, @felipesanches maybe was the last one to work on writing GPOS tables?

It seems from #309 that some GSUB table writing is possible, maybe not through any API, but just constructing an array buffer from scratch yourself(?)

I know this thread got complicated real quick, but some example of how to write basic latin ligature information would be very much appreciated! Maybe @Jolg42 can help?

@dwn
Copy link

dwn commented Jul 30, 2019

I hope I didn't react too annoyingly - was frustrated, because I've gotten quite far on a project and was so disappointed to reach this wall. Since I'm running on Google Cloud, it'd be possible to keep a VM with FontForge as a service just for kerning, but it seems so wasteful when the code looks so close to supporting kerning, something I'd think would be so standardized (and is so easy in deprecated SVG). But of course, I don't want to be the one to do it either X)

Yes, please @Jolg42 or anyone! would love to have a simple left-to-right example I could toss into my code and rid the demons of server maintenance.

@dwn
Copy link

dwn commented Aug 1, 2019

Just for sake of clarity, the prototype might imitate the SVG hkern/vkern tags:

Font.hkern(
unicode1RangeInitial,
unicode1RangeFinal = unicode1RangeInitial,
unicode2RangeInitial,
unicode2RangeFinal = unicode2RangeInitial,
spacing)

@BURROO
Copy link

BURROO commented Dec 11, 2021

Following up on this another few years later, I am wondering if there has been any development on this in the recent years? It looks like there is still some development happening on opentype.js. I have a hard time finding a tool the allows me to draw/write a font within the browser (with all the latest opentype features and kerning). Most tools are focused on parsing fonts, which makes sense, as writing is a very rare use-case anyways i guess. I am working on simple tool to create fonts in the browser. I haven't find any other library that could help with that yet.

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

No branches or pull requests

10 participants