-
Notifications
You must be signed in to change notification settings - Fork 51
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
Support Glyphs3 RTL kerning #778
Changes from 17 commits
d8bda71
698cce4
5cb0fa3
5d1f602
e9c20e5
f09d578
d9295ad
0cb3787
91dee17
9e7bc65
61a35d1
a6f2ace
2fe2e9b
9e6af8e
877f862
bb799de
8541e61
68537fe
5fe3238
9b1a409
7c9fed3
433ef5a
d3e8f29
52900c1
10c500b
454dc60
232aab1
2442b57
49258f3
ffd8714
05f0196
e82e96f
a4eab6c
cf93d8f
41ceb8d
ab76f97
41ea5dc
5938aa1
6e48267
ea8516b
5e36cc1
0e66a83
3c35d9a
4356125
e2af04d
5372913
f866a6e
76f3b2f
e51a087
4dc7328
1820c6e
4bf75aa
00853c4
e95e8b5
e43f4f8
f012805
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,7 +17,7 @@ | |
import os | ||
import re | ||
|
||
from glyphsLib import classes | ||
from glyphsLib import classes, glyphdata | ||
from .constants import GLYPHLIB_PREFIX | ||
from .glyph import BRACKET_GLYPH_RE | ||
|
||
|
@@ -140,12 +140,28 @@ def _to_glyphs_kerning_group(self, name, glyphs): | |
|
||
def _glyph_kerning_attr(glyph, side): | ||
"""Return leftKerningGroup or rightKerningGroup depending on the UFO | ||
group's side (1 or 2). | ||
group's side (1 or 2) and writing script. | ||
""" | ||
if int(side) == 1: | ||
return "rightKerningGroup" | ||
else: | ||
return "leftKerningGroup" | ||
if glyph.parent.format_version > 2: # Glyphs 3 | ||
glyph_data = glyphdata.get_glyph(glyph.name) | ||
if ( | ||
glyph_data.script in ("arabic", "hebrew") | ||
and glyph_data.category != "Number" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. btw, there other characters belonging to RTL scripts (e.g. Arabic) that are not "Numbers" (in the unicode category sense of Nd, No, etc.) but which have a (weak) left-to-right bidirectional type: e.g. arabic decimal/thousand separators (Po: Punctuation other), or arabic number sign (Cf: Format), and some others. In ufo2ft we don't check the category is Number, but we check if the bidiType ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note (to self, mostly) that in ufo2ft, the kerning pairs for the (LTR) arabic numbers are added to the same E.g. see this test case: Whereas here, it looks like the So ufo2ft's |
||
): | ||
if int(side) == 1: | ||
return "leftKerningGroup" | ||
else: | ||
return "rightKerningGroup" | ||
else: | ||
if int(side) == 1: | ||
return "rightKerningGroup" | ||
else: | ||
return "leftKerningGroup" | ||
else: # Glyphs 2 | ||
if int(side) == 1: | ||
return "rightKerningGroup" | ||
else: | ||
return "leftKerningGroup" | ||
|
||
|
||
def _assert_groups_are_identical(self, reference_ufo, ufo): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,25 +24,34 @@ def to_ufo_kerning(self): | |
for master in self.font.masters: | ||
master_id = master.id | ||
kerning_source = master.metricsSource.id # Maybe be a linked master | ||
if kerning_source in self.font.kerning: | ||
kerning = self.font.kerning[kerning_source] | ||
if kerning_source in self.font.kerningLTR: | ||
kerning = self.font.kerningLTR[kerning_source] | ||
_to_ufo_kerning(self, self._sources[master_id].font, kerning) | ||
if kerning_source in self.font.kerningRTL: | ||
kerning = self.font.kerningRTL[kerning_source] | ||
_to_ufo_kerning(self, self._sources[master_id].font, kerning, "RTL") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. since both kerningLTR and kerningRTL are merged into the same ufo.kerning dictionary, is it ever possible that a clash might occur? I.e. the same kern pair defined in both kerningLTR and kerningRTL (potentially with different kern values), which would then mean that the RTL coming last would overwrite the LTR kern pair with the same (first, second) key? |
||
|
||
|
||
def _to_ufo_kerning(self, ufo, kerning_data): | ||
def _to_ufo_kerning(self, ufo, kerning_data, direction="LTR"): | ||
"""Add .glyphs kerning to an UFO.""" | ||
|
||
warning_msg = "Non-existent glyph class %s found in kerning rules." | ||
|
||
for left, pairs in kerning_data.items(): | ||
match = re.match(r"@MMK_L_(.+)", left) | ||
if direction == "LTR": | ||
match = re.match(r"@MMK_L_(.+)", left) | ||
elif direction == "RTL": | ||
match = re.match(r"@MMK_R_(.+)", left) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wait.. does Glyphs 3 really call the class on the left-hand side There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I’ll try to answer: The kerning pairs are still defined in logical order. So the code logic doesn’t change here. First stays first, second stays second. Additionally, for the new RTL-specific kerning table, Georg has switched the FYI, you can check the two added |
||
left_is_class = bool(match) | ||
if left_is_class: | ||
left = "public.kern1.%s" % match.group(1) | ||
if left not in ufo.groups: | ||
self.logger.warning(warning_msg % left) | ||
for right, kerning_val in pairs.items(): | ||
match = re.match(r"@MMK_R_(.+)", right) | ||
if direction == "LTR": | ||
match = re.match(r"@MMK_R_(.+)", right) | ||
elif direction == "RTL": | ||
match = re.match(r"@MMK_L_(.+)", right) | ||
right_is_class = bool(match) | ||
if right_is_class: | ||
right = "public.kern2.%s" % match.group(1) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
{ | ||
.appVersion = "1352"; | ||
DisplayStrings = ( | ||
"/alef-hb/bet-hb", | ||
"/reh-ar/hah-ar.init.swsh/hah-ar.init" | ||
); | ||
date = "2022-02-09 20:07:11 +0000"; | ||
familyName = "RTL kerning"; | ||
fontMaster = ( | ||
{ | ||
ascender = 800; | ||
capHeight = 700; | ||
descender = -200; | ||
id = m01; | ||
xHeight = 500; | ||
} | ||
); | ||
glyphs = ( | ||
{ | ||
glyphname = A; | ||
layers = ( | ||
{ | ||
layerId = m01; | ||
width = 600; | ||
} | ||
); | ||
unicode = 0041; | ||
}, | ||
{ | ||
glyphname = B; | ||
layers = ( | ||
{ | ||
layerId = m01; | ||
width = 600; | ||
} | ||
); | ||
unicode = 0042; | ||
}, | ||
{ | ||
glyphname = C; | ||
layers = ( | ||
{ | ||
layerId = m01; | ||
width = 600; | ||
} | ||
); | ||
unicode = 0043; | ||
}, | ||
{ | ||
glyphname = "hah-ar.init"; | ||
lastChange = "2022-02-21 17:09:37 +0000"; | ||
layers = ( | ||
{ | ||
layerId = m01; | ||
width = 600; | ||
} | ||
); | ||
leftKerningGroup = "hah-ar.init"; | ||
unicode = FEA3; | ||
}, | ||
{ | ||
glyphname = "hah-ar.init.swsh"; | ||
lastChange = "2022-02-21 17:25:40 +0000"; | ||
layers = ( | ||
{ | ||
layerId = m01; | ||
width = 600; | ||
} | ||
); | ||
leftKerningGroup = "hah-ar.init.swsh"; | ||
}, | ||
{ | ||
glyphname = "reh-ar"; | ||
lastChange = "2022-02-21 17:09:13 +0000"; | ||
layers = ( | ||
{ | ||
layerId = m01; | ||
width = 600; | ||
} | ||
); | ||
rightKerningGroup = "reh-ar"; | ||
unicode = 0631; | ||
}, | ||
{ | ||
glyphname = "alef-hb"; | ||
lastChange = "2022-02-21 11:17:22 +0000"; | ||
layers = ( | ||
{ | ||
layerId = m01; | ||
width = 600; | ||
} | ||
); | ||
rightKerningGroup = leftAlef; | ||
unicode = 05D0; | ||
}, | ||
{ | ||
glyphname = "bet-hb"; | ||
lastChange = "2022-02-21 11:17:26 +0000"; | ||
layers = ( | ||
{ | ||
layerId = m01; | ||
width = 600; | ||
} | ||
); | ||
leftKerningGroup = rightBet; | ||
unicode = 05D1; | ||
}, | ||
{ | ||
glyphname = "he-hb"; | ||
lastChange = "2022-02-17 09:36:21 +0000"; | ||
layers = ( | ||
{ | ||
layerId = m01; | ||
width = 600; | ||
} | ||
); | ||
unicode = 05D4; | ||
}, | ||
{ | ||
glyphname = "one-ar"; | ||
lastChange = "2022-02-21 14:41:56 +0000"; | ||
layers = ( | ||
{ | ||
layerId = m01; | ||
width = 600; | ||
} | ||
); | ||
leftKerningGroup = "left-one-ar"; | ||
unicode = 0661; | ||
}, | ||
{ | ||
glyphname = "one-ar.wide.ss16.calt"; | ||
lastChange = "2022-02-21 14:59:56 +0000"; | ||
layers = ( | ||
{ | ||
layerId = m01; | ||
width = 600; | ||
} | ||
); | ||
leftKerningGroup = "left-one-ar"; | ||
}, | ||
{ | ||
glyphname = space; | ||
layers = ( | ||
{ | ||
layerId = m01; | ||
width = 600; | ||
} | ||
); | ||
unicode = 0020; | ||
} | ||
); | ||
instances = ( | ||
{ | ||
instanceInterpolations = { | ||
m01 = 1; | ||
}; | ||
name = Regular; | ||
} | ||
); | ||
kerning = { | ||
m01 = { | ||
"@MMK_L_leftAlef" = { | ||
"@MMK_R_rightBet" = -20; | ||
"he-hb" = 4; | ||
}; | ||
"@MMK_L_leftBet" = { | ||
"@MMK_R_rightAlef" = 20; | ||
}; | ||
"@MMK_L_reh-ar" = { | ||
"@MMK_R_hah-ar.init" = -50; | ||
"@MMK_R_hah-ar.init.swsh" = -50; | ||
}; | ||
"he-hb" = { | ||
"@MMK_R_rightAlef" = -2; | ||
"he-hb" = -21; | ||
}; | ||
}; | ||
}; | ||
unitsPerEm = 1000; | ||
versionMajor = 1; | ||
versionMinor = 0; | ||
} |
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.
why only "arabic" and "hebrew"?
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.
These are the two RTL scripts to my knowledge. This point is arguably the critical one, also about the numbers. I chose those to my best knowledge. Some algorithm needs to be in place to sort Glyphs’ kerning classes into the correct first/second pairs, and I didn’t know any better.
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 can add the writing direction to the glyphdata.xml but there might be a way to query unicode to get the writing direction of each code. Then we don’t need to check for
script == arabic and category != Number
.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.
You can get script direction from a function in ufo2ft.utils.
(I’m on the road this week so won’t be able to review.)
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.
Yes, I could try this approach. @anthrotype Seems like a good idea?