Add the space-discarding feature - #182
Conversation
|
Tibetan comes to mind as an example of an orthography that doesn't use (ASCII) spaces but does have delimiters (syllable-based) which could break a line in the source code, but which should not incur an extra space when stitching things together (see Tibetan Orthography Notes). hth |
There was a problem hiding this comment.
I've gone through the source code (except the emoji part). I believe the current algorithm is simple and robust enough. I only have some suggestions regarding doc comments and tests. See my individual comments.
More materials supporting the current algorithm
East Asian Width
UAX #14: Unicode Line Breaking Algorithm also uses East Asian Width to filter out East Asian characters.
The symbol
$EastAsianstands for the set[\p{ea=F}\p{ea=W}\p{ea=H}]of characters with Fullwidth, Wide, or Halfwidth East Asian Width.
Pandoc
Pandoc's east_asian_line_breaks extension uses charWidth to determine if a soft break (e.g., single newline in markdown and typst) should be removed. However, we've argued in typst/typst#7350 (comment) that the rules for determining widths are too complicated and it's better to use East Asian Width directly.
LaTeX
LaTeX cannot be taken as a reference, because the implementations are limited by the technology. Specifically, the whitespace in 字\n“ should be discarded, but luatexja keeps it. And the whitespace in ”\nA should be kept as a word space, but xeCJK discards it. See typst/typst#792 (comment) for the tests.
Typst cjk-unbreak
As for typst packages, cjk-unbreak uses the following regex to determine if a character is CJ (Chinese + Japanese) and discards the space iff either side matches the regex.
[\p{Han},。;:!?‘’“”()「」【】…—\p{Hiragana}\p{Katakana}]
This package is designed only for CJ, so the regex includes a few characters that are considered YesOrAmbiguous in this PR.
The algorithm in this PR is designed for all writing systems, so the difference to cjk-unbreak is acceptable.
Typst cjk-spacer
A newer typst package, cjk-spacer, uses a more complex algorithm. If I understand correctly, then its algorithm is equivalent to the following.
#let default-cjk-regex = regex(
"["
+ "\p{scx:Hira}\p{scx:Kana}\p{scx:Han}\p{scx:Hang}\p{scx:Bopo}"
+ "\u3000-\u303F" // CJK Symbols and Punctuation
+ "\u3190-\u319F" // Kanbun
+ "\u31C0-\u31EF" // CJK Strokes
+ "\u3200-\u32FF" // Enclosed CJK Letters and Months
+ "\u3300-\u33FF" // CJK Compatibility
+ "\uFE10-\uFE1F" // Vertical Forms
+ "\uFE30-\uFE4F" // CJK Compatibility Forms
+ "\uFE50-\uFE6F" // Small Form Variants
+ "\uFF00-\uFFEF" // Halfwidth and Fullwidth Forms
+ "]",
)
#let default-western-open-punc-regex = regex(
"[\p{Pi}\p{Ps}--["
+ "\u3000-\u303F" // CJK Symbols and Punctuation
+ "\uFE10-\uFE1F" // Vertical Forms
+ "\uFE30-\uFE4F" // CJK Compatibility Forms
+ "\uFE50-\uFE6F" // Small Form Variants
+ "\uFF00-\uFFEF" // Halfwidth and Fullwidth Forms
+ "]]",
)
#let default-western-close-punc-regex = regex(
"[\p{Pf}\p{Pe}\p{Term}--["
+ "\u3000-\u303F" // CJK Symbols and Punctuation
+ "\uFE10-\uFE1F" // Vertical Forms
+ "\uFE30-\uFE4F" // CJK Compatibility Forms
+ "\uFE50-\uFE6F" // Small Form Variants
+ "\uFF00-\uFFEF" // Halfwidth and Fullwidth Forms
+ "]]",
)
#let discard_space_between(before, after) = {
if after.matches(western-open-punc-regex).len() == 0 and after.starts-with(cjk-regex) {
true
} else if before.matches(western-close-punc-regex).len() == 0 and before.ends-with(cjk-regex) {
true
} else {
false
}
}The cjk-spacer algorithm does not merely consider the characters immediately adjacent to the space, but rather the text segments around the space. This approach is appropriate when typesetting a document, but it's too surprising at the syntax level.
Also, cjk-spacer treats K the same as CJ. According to previous feedbacks in typst/typst#7350, discarding spaces is not preferable for Korean texts.
And I haven't check if the Unicode blocks enumerated by cjk-spacer are equivalent to this PR, but I think it's worth checking before merging this PR.
| /// kept. | ||
| /// | ||
| /// Currently this check includes characters which we determine to be from the | ||
| /// Chinese, Japanese, or Yi writing systems plus ideographic punctuation. Note |
There was a problem hiding this comment.
The term ideographic punctuation needs clarification.
-
There's a Unicode block called Ideographic Symbols and Punctuation, but obviously that isn't what you mean.
-
‼is listed in both Punctuation marks in Chinese in CLReq and Character Classes in JLReq, but it isn't considered as a ideographic punctuation.
It looks like that you use this term colloquially. I suggest putting the relevant test cases in a separate function and linking to it. (similar to test_spacing_emoji_presentation)
There was a problem hiding this comment.
I'll split into two test functions and integrate the "Miscellaneous" section into the two punctuation groups.
Do you think we should consider special-casing ⁇ or ‼?
| check_spacing('₩', YesOrAmbiguous); | ||
| check_spacing('₩', No); | ||
| check_spacing('¥', YesOrAmbiguous); | ||
| check_spacing('¥', No); |
There was a problem hiding this comment.
Just a comment for your information: I get the narrow ¥ when I type in Chinese mode on my mobile phone, but I get the fullwidth ¥ when I press Shift+4 ($) in Chinese mode on my desktop computer.
laurmaedje
left a comment
There was a problem hiding this comment.
Logic-wise, this seems reasonable and very well supported in terms of rationale. I'd trust in the research and the other involved people's judgement. I just have some minor nits.
d6c4e9e to
4d7ca98
Compare
|
I've updated comments and tests based on all of the feedback! I think for this initial PR we will not include Tibetan. But one of the main points for moving this to Codex is so we can add GitHub issues and iterate at a separate pace than For cjk-unbreak, it looks there are quite a few differences to this PR. The characters it doesn't include are all either Of those, it seems the groups we should consider are:
I'll prepare an update to include these. UnicodeSet syntax for this PR and cjk-unbreak
This PR currently: [
\p{Han} \p{Kana} \p{Hira} \p{Bopo} \p{Yi}
[ \p{Common} & [\p{EA=H}\p{EA=F}\p{EA=W}] - \p{Emoji} - [₩] ]
]cjk-unbreak default (excluding [
\p{Scx=Hira} \p{Scx=Kana} \p{Scx=Han} \p{Scx=Bopo}
[ \u3000-\u303F \u3190-\u319F \u31C0-\u31EF \u3200-\u32FF \u3300-\u33FF \uFE10-\uFE1F \uFE30-\uFE4F \uFE50-\uFE6F \uFF00-\uFFEF ]
] |
4d7ca98 to
71e7593
Compare
|
I've looked into these character groups some more and I've updated the implementation based on them. Here are my changes for each set and some of my thoughts about their East Asian Width ( 1. Fullwidth latin lettersI added the fullwidth latin letters. It feels odd that they don't have 2. Circled numbers on black squaresI added the circled numbers on black squares. I have no clue why these would be Here is Unicode's description of these characters (section 22.10.2, paragraph 2):
3. The Six CJ-specific combining marksI added the six combining marks. These only slipped by the initial definition because they're in It may be worth using language information to make determinations on other 4. Chinese corner tone marksI'm ignoring the corner tone marks since they're not for Chinese typesetting, but for transcription of Chinese into English, which is why their Here is Unicode's description of these marks (section 7.8.2, paragraph 2):
5. 〿 U+303F IDEOGRAPHIC HALF FILL SPACE(Note that the character is not failing to render, it is supposed to look like a vertical box with an X through it.) I'm not adding this character as it doesn't actually seem to be used in Chinese or Japanese writing. Conflicting PropertiesThis character gave me a lot of trouble because it has two seemingly conflicting property values in Unicode:
It makes no sense for a character to have "no basis in East Asian typography" but also be used in the Han script! It's also very confusing that 〿 U+303F is adjacent to 〾 U+303E IDEOGRAPHIC VARIATION INDICATOR, which is a wholly unrelated character used in ideographic description sequences. Trying to look for info about 〿 U+303F down that route is a dead end. But to make it worse, Unicode gives these two a custom subheader in their block as Special CJK Indicators despite having no historical similarity! Judging by its name alone you would think, "oh, this is used in ideographic contexts, of course it should have OriginsThe ideographic half fill space is a very old character in Unicode. It was present even in Unicode 1.0, and while I could not find any records documenting its choice of position in the CJK Symbols and Punctuation block, I did find that its real name used to be "Box X (DBCS Fill Character)" and its real codepoint was 000B when it lived in the IBM code pages for various CJK DOS systems using Double Byte Character Sets. You can find this from the character's GCGID of When you consider that the DOS code pages were originally designed for terminal-based systems using monospace fonts where characters would render in either one vertical cell or two (based on using either one or two bytes), Unicode's description of the ideographic half fill space starts to make more sense (section 6.2.13, paragraph 3):
(This description has been present since Unicode 2.0.0 and has not changed) The character is literally just an X in a box for filling empty cells! It's only "ideographic" because it was only used in code pages for CJK systems, but in that usage it was really a block element character similar to █ U+2588 FULL BLOCK or ▓ U+2593 DARK SHADE (the latter usually sharing an IBM code page with 〿 U+303F at position 0014). ResolutionSo of the two conflicting properties, only And if you really go looking, you can find reference to it in revision 3 of UTR #11 (June 1999), where Asmus Freytag notes that 〿 U+303F is an exception for an old paragraph in the Unicode standard, as it is a non-full-width character in the set of given blocks, and he updates its So I might submit a proposal to have Won signI also found some more context on the won sign in UAX #11:
This distinction was also discussed many years prior in the meeting minutes for UTC #78 (p10), where the answer was: "Because ISO8859-1 has Yen sign. No other character set has legacy half-width Won sign." To be honest, it actually makes me a little mad that the won sign isn't |
I think fullwidth Latin letters should be YesOrAmbiguous.
|
71e7593 to
9511e12
Compare
|
Thank you, I was not aware of that context. I've reverted the fullwidth letters. |
This adds the
space-discardingfeature as described at typst/typst#7350 (comment), although I have changed from "whether a writing system uses spaces between words" to "whether a writing system uses spaces at all."I will leave this PR description short as the code itself contains a plenty of discussion of rationale and implementation considerations, along with my research into the usage of space characters in various writing systems.
There is a lot of writing here, so I would really appreciate help with checking for typos and inconsistencies, as it has become hard for me to consider everything with fresh eyes. I am very amenable to suggestions :)
I will also restate that I only speak English and while I have tried to do good research, I am not infallible. I would appreciate any input from native speakers of Chinese or Japanese or any of the other writing systems discussed in the PR.
I would also like to thank @r12a for his wonderfully detailed orthography descriptions and script comparison table, without which this PR would not be nearly as complete or authoritative. If you're reading this, I would love any feedback you could provide.