Build the generated Unicode tables from Unicode 18.0.0 - #265
Open
jakejackson1 wants to merge 5 commits into
Open
jakejackson1 wants to merge 5 commits into
jakejackson1 wants to merge 5 commits into
Conversation
…a recently added character joins (#251) Shaper\Arabic's two joining tables stopped at the blocks Unicode had when they were last extended by hand. A character in neither table joins nothing and unjoins its neighbour, so the letter before a recently added one was drawn with no form where HarfBuzz gives it init or medi. Both tables are now written by composer arabicjoining:update from ArabicShaping.txt, beside a test that the checked-in tables are what the generator writes - the pattern Ucdn::$ot_languages already uses. What is in scope is read from Ucdn's script for each character, because the script is what routes a run to this shaper, which is why the generator reads the same Unicode version Ucdn's scripts were built from. 77 characters change: the 72 the issue measured over Mandaic, the Syriac Supplement and Arabic Extended-A and -B, and five in Arabic Extended-C, which is script Arabic and so resolved here for the same reason. U+0847 MANDAIC IT was the one filed in the wrong table and is now right-joining only. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…eads them (#251) The scope test read Ucdn's script and admitted Common and Inherited, on the grounds that a character Ucdn has no script for reaches no shaper. It does: Otl::analyseCharacters() will not start a run on Unknown any more than on those two, so the character stays in the run before it and the Arabic shaper is handed it. Measured through Lateef, a Beh after U+10EE8 CROWN FEH - Arabic and left-joining as of Unicode 18, Unknown to Ucdn's Unicode 17 - is drawn with no form until the table has it, which is this defect with a shorter fuse. Unknown is now in scope with the other two, and the tables are no longer tied to Ucdn's release to be correct. The four scripts are Otl::selectShaper()'s decision rather than the generator's, so a test holds the two lists together; a fifth script in that branch would otherwise bring the defect back for it with nothing failing. Also: the version line each generator rewrites was a third copy of one preg_replace, so it moves to GeneratedTable and gains the missing-marker check replaceArray() already had; the partition reads the three joining types it wants rather than the two it does not; and U+0883 to U+0885 and U+07FA are Join_Causing with scripts of their own, so the comment claiming Join_Causing means "no script" is gone. No table content moves: no character of Unicode 17 is Unknown to Ucdn 17. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ed-joining-tables
UcdnTables::DEFAULT_VERSION is the one place the repository names its Unicode
release, and ArabicJoining reads it, so raising it and running both generators
is the whole change:
composer ucdn:update 1017 records, 178 scripts (3 new), 438 mirror pairs
composer arabicjoining:update 284 left-joining, 375 right-joining characters
composer otlanguages:update is not part of it. Ucdn::$ot_languages comes from
HarfBuzz's table rather than the UCD, and running it over the rebuilt class
writes the same bytes.
The joining tables gain exactly the 22 characters of ArabicShaping.txt's whole
17-to-18 delta, U+10ED9..U+10EEE, the Arabic Extended-C crown letters. All 22
are Joining_Type=L, so they enter $leftJoining alone and $rightJoining does not
move - the first character in any script this shaper is called for to join
forwards and not back. src/Ucdn.php gives them SCRIPT_ARABIC, where Unicode 17
had them as SCRIPT_UNKNOWN.
Ucdn's larger half moves in four ways. 13,007 codepoints were unassigned and
now carry a script. Three scripts are appended - Jurchen 175, Proto-Cuneiform
176 and Seal 177 - and no script that had a number changed it, which is what
the rest of mPDF depends on: Otl compares those numbers by range. Ten mirror
pairs are added, matching BidiMirroring.txt's ten new lines. The remaining
2,145 lines are the index tables re-packing around four new records, the first
of which is first seen at U+1B3A and so shifts every record number after it.
The one already-assigned character to be reclassified is Balinese: U+1B3A,
U+1B3C and U+1B42 move from Bidi_Class=NSM to L.
Otl::analyseCharacters() wrote SCRIPT_INHERITED and SCRIPT_UNKNOWN as the bare
literals 40 and 102, on the line that decides which script folds into the run
before it. Both numbers held, but a Unicode bump is when that line would fail
silently, so it reads the constants now.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e cannot assign ArabicJoiningTest asserted that a joining type Ucdn's script table has not caught up with is still written, with U+10EE8 CROWN FEH standing for it. Unicode 18 gives that character SCRIPT_ARABIC, so it now reaches ArabicJoining::inScope() by script and the SCRIPT_UNKNOWN clause it was written for is reached by nothing - the test stayed green while the branch went uncovered. It reads U+FDD0 now, a noncharacter that Unicode's stability policy will never assign, so the case holds at every release rather than needing the next one's newest codepoint. Removing that clause fails the test again. Otl::analyseCharacters()'s script-block loop is copied verbatim into Mpdf.php and Image/Svg.php, and both still spelled SCRIPT_INHERITED and SCRIPT_UNKNOWN as 40 and 102; they read the constants too, so one grep finds all three. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The follow-up to #251, which pinned its generator to Unicode 17.0.0 and said which Unicode mPDF claims
was a maintainer decision. It is 18.0.0 now. One edit —
UcdnTables::DEFAULT_VERSION— thencomposer ucdn:updateandcomposer arabicjoining:update.Stacked on #251 (PR #261), so this branch contains its commits too. #261 should merge first.
composer otlanguages:updateis not part of this and was checked rather than assumed:OtLanguageTags::DEFAULT_VERSIONis a HarfBuzz release, not a Unicode one, and running it over therebuilt
src/Ucdn.phpproduces no diff.The cost, stated up front: the independent oracle goes dark
UcdnAgainstIcuTestcompares every one of the 1,114,112 codepoints against ICU — a wholly separateimplementation of the same data — and skips itself when ICU's Unicode release does not match the repo's.
Local ICU 78.2 carries Unicode 17.0, so this change switches that test off until ICU ships Unicode 18.
That matters more than a skip count: it is the only check on
src/Ucdn.phpthat is not the generatorgrading its own homework. After this, the regenerated tables are pinned by the round-trip test (which
proves the generator agrees with itself), the golden masters and the shaping suite — and nothing
independent.
Its last word on the tables is on record. At #251's tip with
DEFAULT_VERSIONstill 17.0.0:Every codepoint agreed with ICU 78.2 on category, combining class, bidi class, mirrored, East Asian
width and script. The comparison itself is untouched; the skip is the test protecting itself from
grading Unicode 18 tables against Unicode 17 data, and it is correct to do so.
What moved in the joining tables: exactly 22 characters
$leftJoining262 → 284,$rightJoining375 → unchanged. The 22 are contiguous,U+10ED9..U+10EEE, and all
Joining_Type=L— which is why only the left table moves.This is bounded at the source rather than inferred from the output: the entire data delta between
ArabicShaping-17.0.0.txtand-18.0.0.txtis those 22 added lines, 834 → 856, with nothing removedand nothing changed. So no character of any other script could have moved.
At Unicode 17 no in-scope character had
Joining_Type=Lat all, soLwas a column the tables couldnot reach.
Why the new test's oracle is not a plain hb-shape run
Released HarfBuzz does not carry Unicode 18's joining data yet. Both 14.3.1 and 14.4.0 (the current
release) generate
src/hb-ot-shaper-arabic-table.hhfromArabicShaping-17.0.0.txt— the header says so— and only
mainhas been regenerated. Sohb-shapegives U+10ED9 no joining behaviour, and that is agap in the oracle, not a disagreement. For these 22 characters mPDF is ahead of released HarfBuzz.
The oracle is therefore in two parts. The joining type is normative UCD data, the same
ArabicShaping-18.0.0.txtline that HarfBuzzmainreads. The semantics ofLcome fromhb-shape14.3.1 read on a character Unicode 17 already typed
L— and HarfBuzz'sarabic_state_table, the codethat turns a type into a form, is byte-identical between 14.3.1 and
main, so only the data differs:Both new tests fail at #251's tip:
The shape of the src/Ucdn.php diff
2,145 changed lines, and it decomposes completely:
By script: Seal 11,328, Jurchen 965, Proto-Cuneiform 164, Latin 161, Cuneiform 159, Common 153,
Arabic 39, Inherited 11, then 16 scripts with six or fewer. No codepoint moved from one named script
to another.
letters plus 17 marks and digits.
jurc,pcun,seal.BidiMirroring.txt's ten new lines exactly; none removed or changed.Bidi_ClassNSM → L. That is the whole of the non-assignment delta.$ucd_records1013 → 1017, the first new recordinserted at slot 383, so record numbers from 383 up shift.
No existing script's number moved. The only change to the
SCRIPT_*constants is three appendedlines;
SCRIPT_INHERITEDis still 40 andSCRIPT_UNKNOWNstill 102, which three call sites depend on.Three literals, because there were three copies
src/Otl.php:359readif ($sbl && $sbl != 40 && $sbl != 102)—SCRIPT_INHERITEDandSCRIPT_UNKNOWNas bare numbers, on the line that decides which script starts a run and thereforewhich shaper a character reaches.
Otl::analyseCharacters()'s loop is copy-pasted verbatim intosrc/Mpdf.php:26932andsrc/Image/Svg.php:3343, and both carried the same two literals. Fixing one ofthree would be worse than fixing none, so all three now name the constants. Behaviour-identical — the
numbers held — and one grep now finds every site.
The rule is still stated in four places and the three
src/sites are the same ~20-line loop. That is arefactor of its own: #266.
Two tests this invalidated
UcdnTablesTest::testANewScriptIsAppendedAndTheRestKeepTheirNumbershardcoded Adlam 175 and Toto 176,which is one past the last number in use and therefore moves at every Unicode release. It now derives
the next number. Every failure the literals caught is still caught — appending into the holes Adlam
(103) and Toto (168) leave, appending in read order, appending with a gap.
SCRIPT_UNKNOWN === 102stays hardcoded as the stability anchor. Verified passing at both 17 and 18.
ArabicJoiningTest's Unknown-script case used U+10EE8 to stand for "a joining type Ucdn's script tablehas not caught up with". At 18 that character has a script, so the case went dead while staying green.
It is U+FDD0 now, a noncharacter Unicode's stability policy will never assign, so it holds at every
release. Mutation-checked: removing the
SCRIPT_UNKNOWNclause frominScope()fails it.Verified
Base at #251's tip: 2532 / 6702 / 3 skipped. The one remaining skip is
UcdnAgainstIcuTest, for thereason above.
No golden master and no snapshot moved. All four generators were re-run over the whole corpus after
deleting
tests/Mpdf/tmp/mpdf; every fixture was rewritten and every one came back byte-identical.utils/data/ucd/18.0.0/is gitignored and not committed, as the other generators' sources are.🤖 Generated with Claude Code