The third coordinate of the family #23 and #24 record, turned up while measuring the two of
them and left out of PR #25 because the fix is wider than either of those was:
While measuring it, one further Racket coordinate turned up that is not fixed here and
is nobody's regression: a furawake row is laid out from bare advances, so a member
before a Table 1 amount inside its row is placed and reported the way #24 described for a
warichu. Fixing it needs the boundary after a furawake block to be read against the
block's last character rather than its first, which is a wider change to how
compose.rkt makes one item of a furawake — also its own issue.
This is that issue. It is a defect in the Racket engine on the footing #19, #23 and #24 were
filed on: the engine's own numbers disagree with each other, which no reading of JLReq
permits.
Input
※〉〈日※ — a §3.7.2 furawake of three full-em members, 〉〈日, set in two columns with a
one-fifth-em line gap, standing between two ordinary full-em characters. The caller states
one break inside the structure, at the third member, which is where §3.7.2's "the text is
broken in the indicated places" divides the columns: 〉〈 share the first row and 日 has
the second.
〉 is Appendix A cl-02 and 〈 is cl-01, so spec/captured/table1.en.tsv states the gap
between them as 1/2 be — half of the before character's own em, 500 units here — and it
falls inside the first row. Sixteen-em measure, so nothing adjusts. Schema-valid
protocol-v1 request (jlreq-conformance validate):
{"protocol":"jlreq.conformance/1","spec":"jlreq-2020-08-11+unicode-17.0.0","id":"issue/furawake-interior-boundary-table1-gap","request":{"source":"※〉〈日※","size":{"inline":1000,"block":1000},"frame":"full-em","clusters":[{"range":[0,3],"advance":1000},{"range":[3,6],"advance":1000},{"range":[6,9],"advance":1000},{"range":[9,12],"advance":1000},{"range":[12,15],"advance":1000}],"line_extent":16000,"breaks":[{"offset":9,"kind":"allowed"}],"constructs":[{"kind":"furawake","range":[3,12],"columns":2,"line_gap":200}],"alignment":"start","writing_mode":"horizontal-tb","style":{"profile":"jlreq-2020"}}}
Validated against crates/jlreq-conformance/protocol.schema.json, and accepted by all three
engines with no diagnostics.
What this library produced
The Racket reference engine, notated (inline_extent/block_extent) [inline:block+advance ...]:
(4500/2200) [0:0+1000 1000:-600+1000 2000:-600+1000 1000:600+1000 3500:0+1000]
〉, the furawake's first member, stands at 1000:-600 with a reported advance of 1000 —
its bare full-em body, with no Table 1 amount added. 〈, the member right after it in the
same row, stands at 2000:-600, which is 1000 units on, so the row is laid out as though
the boundary carried nothing. And the line's own last character, ※, stands at 3500:
1500 units past the block's first member, which is 500 more than the 1000-unit row the
engine just laid out.
The 500 units are not lost — they are put in the wrong place. furawake-item in
engines/racket/compose.rkt gives the whole block the class of its first cluster
((classify-cluster para (car clusters) style)), so the boundary after the block is read
as cl-02→cl-19 rather than cl-19→cl-19, and Table 1's 1/2 be is placed there
instead. Two errors of 500 units each, in opposite directions, and at this coordinate they
cancel in the line's inline_extent while leaving two cluster positions and one advance
wrong.
They do not cancel in general. ※・・日※, the same shape with cl-05 on both sides of the
in-row boundary — Table 1 1/4 be + 1/4 af, 500 units inside the row against 250 after the
block — differs in the line's own width as well:
racket (4500/2200) [0:0+1250 1250:-600+1000 2250:-600+1000 1250:600+1000 3500:0+1000]
expected (4750/2200) [0:0+1250 1250:-600+1500 2750:-600+1000 1250:600+1000 3750:0+1000]
What you expected instead
The Rust (crates/jlreq/src/pipeline.rs) and OCaml (engines/ocaml/lib/pipeline.ml)
reference engines, byte for byte identical to each other:
(4500/2200) [0:0+1000 1000:-600+1500 2500:-600+1000 1000:600+1000 3500:0+1000]
〉 reports 1500 — its 1000-unit body plus the 500-unit Table 1 gap the row carries after it
— which is exactly the distance to 〈 at 2500. The first row is 2500 units wide, that is
what the block charges the line, and the boundary after the block is read against 日, the
block's last character, where Table 1 states nothing. The line's own inline_extent
comes out the same 4500 by a different route, and ※ still stands at 3500.
Specification basis
JLReq 3.7.2 (a furawake sets its text on as many rows as the caller declared columns, and
the whole structure is one position on the line) and Appendix B Table 1's cl-02→cl-01
gap (spec/captured/table1.en.tsv, 1/2 be), read as
docs/decisions/stacked-structure-geometry.md
publishes it for the warichu that #24 was about:
A character inside the block is spaced like any other, and its advance is the step that
reaches the next one. [...] The character that ends a subline reports its body alone,
because nothing of that subline stands after it.
and, for the two edges:
A structure has two edges and they are the same edge twice.
A furawake is the other structure that reading is addressed to — the decision's own
"Applies to" names the furawake rounds — so nothing here is a fresh reading. What is new is
that a furawake block is one item in compose.rkt where a warichu's members are items of
their own, so the block has one class where it needs two: the first character's for the
boundary before it and the last character's for the boundary after it.
Other implementations
Not checked. A furawake holding a closing-then-opening bracket pair inside one of its
columns is a shape no browser composes, and LaTeX's jlreq class has no furawake macro.
Before submitting
The coordinates. Sweeping the shape above over all 529 ordered pairs of the census's own
Appendix A representatives (just census-classes), the Racket engine differs from the Rust
and the OCaml engines at 217 of them, and the Rust and OCaml engines agree with each
other at all 529.
just census constructs does not reach any of them today: its four furawake variants put
the pair under test at the structure's two edges (columned in
engines/ocaml/probe/census.ml) and fill the block itself with ordinary characters that
state nothing between them. A furawake-pair-inside variant — the pair inside one column,
the way warichu-pair-inside puts it inside one subline — is what reaches it, and adding it
is what closes this issue.
The third coordinate of the family #23 and #24 record, turned up while measuring the two of
them and left out of PR #25 because the fix is wider than either of those was:
This is that issue. It is a defect in the Racket engine on the footing #19, #23 and #24 were
filed on: the engine's own numbers disagree with each other, which no reading of JLReq
permits.
Input
※〉〈日※— a §3.7.2 furawake of three full-em members,〉〈日, set in two columns with aone-fifth-em line gap, standing between two ordinary full-em characters. The caller states
one break inside the structure, at the third member, which is where §3.7.2's "the text is
broken in the indicated places" divides the columns:
〉〈share the first row and日hasthe second.
〉is Appendix Acl-02and〈iscl-01, sospec/captured/table1.en.tsvstates the gapbetween them as
1/2 be— half of the before character's own em, 500 units here — and itfalls inside the first row. Sixteen-em measure, so nothing adjusts. Schema-valid
protocol-v1 request (
jlreq-conformance validate):{"protocol":"jlreq.conformance/1","spec":"jlreq-2020-08-11+unicode-17.0.0","id":"issue/furawake-interior-boundary-table1-gap","request":{"source":"※〉〈日※","size":{"inline":1000,"block":1000},"frame":"full-em","clusters":[{"range":[0,3],"advance":1000},{"range":[3,6],"advance":1000},{"range":[6,9],"advance":1000},{"range":[9,12],"advance":1000},{"range":[12,15],"advance":1000}],"line_extent":16000,"breaks":[{"offset":9,"kind":"allowed"}],"constructs":[{"kind":"furawake","range":[3,12],"columns":2,"line_gap":200}],"alignment":"start","writing_mode":"horizontal-tb","style":{"profile":"jlreq-2020"}}}Validated against
crates/jlreq-conformance/protocol.schema.json, and accepted by all threeengines with no diagnostics.
What this library produced
The Racket reference engine, notated
(inline_extent/block_extent) [inline:block+advance ...]:〉, the furawake's first member, stands at1000:-600with a reported advance of 1000 —its bare full-em body, with no Table 1 amount added.
〈, the member right after it in thesame row, stands at
2000:-600, which is 1000 units on, so the row is laid out as thoughthe boundary carried nothing. And the line's own last character,
※, stands at3500:1500 units past the block's first member, which is 500 more than the 1000-unit row the
engine just laid out.
The 500 units are not lost — they are put in the wrong place.
furawake-iteminengines/racket/compose.rktgives the whole block the class of its first cluster(
(classify-cluster para (car clusters) style)), so the boundary after the block is readas
cl-02→cl-19rather thancl-19→cl-19, and Table 1's1/2 beis placed thereinstead. Two errors of 500 units each, in opposite directions, and at this coordinate they
cancel in the line's
inline_extentwhile leaving two cluster positions and one advancewrong.
They do not cancel in general.
※・・日※, the same shape withcl-05on both sides of thein-row boundary — Table 1
1/4 be + 1/4 af, 500 units inside the row against 250 after theblock — differs in the line's own width as well:
What you expected instead
The Rust (
crates/jlreq/src/pipeline.rs) and OCaml (engines/ocaml/lib/pipeline.ml)reference engines, byte for byte identical to each other:
〉reports 1500 — its 1000-unit body plus the 500-unit Table 1 gap the row carries after it— which is exactly the distance to
〈at 2500. The first row is 2500 units wide, that iswhat the block charges the line, and the boundary after the block is read against
日, theblock's last character, where Table 1 states nothing. The line's own
inline_extentcomes out the same 4500 by a different route, and
※still stands at 3500.Specification basis
JLReq 3.7.2 (a furawake sets its text on as many rows as the caller declared columns, and
the whole structure is one position on the line) and Appendix B Table 1's
cl-02→cl-01gap (
spec/captured/table1.en.tsv,1/2 be), read asdocs/decisions/stacked-structure-geometry.mdpublishes it for the warichu that #24 was about:
and, for the two edges:
A furawake is the other structure that reading is addressed to — the decision's own
"Applies to" names the furawake rounds — so nothing here is a fresh reading. What is new is
that a furawake block is one item in
compose.rktwhere a warichu's members are items oftheir own, so the block has one class where it needs two: the first character's for the
boundary before it and the last character's for the boundary after it.
Other implementations
Not checked. A furawake holding a closing-then-opening bracket pair inside one of its
columns is a shape no browser composes, and LaTeX's
jlreqclass has no furawake macro.Before submitting
an advance of 1000 for
〉and places〈1000 units on, while charging the line 1500for the same step — its own numbers disagree with each other. Filed as a defect in the
Racket engine, the same footing as A tab sign inside a warichu: the Racket engine sets it one em wide #19, A warichu of full-size characters: the Racket engine doubles the line's block extent #23 and A member beside an in-block boundary: the Racket engine reports the bare advance #24.
The coordinates. Sweeping the shape above over all 529 ordered pairs of the census's own
Appendix A representatives (
just census-classes), the Racket engine differs from the Rustand the OCaml engines at 217 of them, and the Rust and OCaml engines agree with each
other at all 529.
just census constructsdoes not reach any of them today: its fourfurawakevariants putthe pair under test at the structure's two edges (
columnedinengines/ocaml/probe/census.ml) and fill the block itself with ordinary characters thatstate nothing between them. A
furawake-pair-insidevariant — the pair inside one column,the way
warichu-pair-insideputs it inside one subline — is what reaches it, and adding itis what closes this issue.