A break character is stored as the last character of the paragraph it
ends, followed by the usual '\n' separator. The story model, paragraph
ranges, styles, editing and every exporter keep splitting paragraphs on
'\n' only, so the change stays in the places that create, lay out or
write break characters:
- IDML import writes the break and ends the paragraph, so the range that
follows keeps its own paragraph style. NextOddPage and NextEvenPage map
to new odd and even page break characters; IDML export writes a
paragraph that ends in a break as that one Br with its
ParagraphBreakType, so export and re-import give the same story.
- Word and RTF import, Type > Insert Break Character (with odd and even
page breaks added) and typing end the paragraph after a break. Typing
between a break and its return goes to the next paragraph; deleting
either deletes both.
- Layout: a break that ends a paragraph leaves no empty line; the next
paragraph starts in the next column, frame or page, as a paragraph start
option would. Page breaks and Next Page start options move to the next
frame on a later page; odd and even ones to the next page with that
page number parity (no such frame: overset). A break inside a paragraph
from an older document still moves the rest of that paragraph on.
Storing the break as a paragraph separator of its own would have changed
every place that counts or splits paragraphs on '\n'.
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Text inside a tracked move destination was dropped on .docx import, so
moved passages vanished from both places. Treat moveTo like ins (both
kept as accepted); moveFrom stays skipped like del. Regression test
covers ins, del, moveFrom and moveTo together. Fixes#256.
Also adds my contributor credit entry.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Placing an RTF or Tagged Text file dropped every character outside the
Basic Multilingual Plane: emoji, mathematical letters and the rarer CJK
ideographs such as 𠮷 (U+20BB7, used in Japanese names). The text
around them came in, so the loss was silent. It also broke our own round
trip: Export Text writes these characters correctly and Place lost them.
Both formats carry such a character as a UTF-16 surrogate pair, two
units in a row: \u-10179?\u-8704? in RTF, <0xD83D><0xDE00> in Tagged
Text. Both readers passed each unit to char::from_u32 on its own. Half a
pair is not a character, so each half was dropped.
A small shared Utf16Units joins the units: a first half waits for the
second, and the pair becomes one character. Half a pair with no partner
is still left out, as before.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Conflict: crates/compose/src/lib.rs: start options, the break after the previous paragraph and a break inside a paragraph go through a local `go` that sends NextColumn to next's span-aware next_column (#276) and the rest to #285's Cursor::start; the in-paragraph break takes #283's `brk` (the line's last glyph); #283's ends_para uses is_break_char; Cursor keeps next's resume() and adds #285's start/next_page (#285's old next_baseline header dropped, next's is kept); the table branch is #285's
Conflict: crates/idml/src/tests.rs: tests only; kept both
Conflict: crates/textimport/src/docx.rs: tests only; kept both
A break character is stored as the last character of the paragraph it
ends, followed by the usual '\n' separator. The story model, paragraph
ranges, styles, editing and every exporter keep splitting paragraphs on
'\n' only, so the change stays in the places that create, lay out or
write break characters:
- IDML import writes the break and ends the paragraph, so the range that
follows keeps its own paragraph style. NextOddPage and NextEvenPage map
to new odd and even page break characters; IDML export writes a
paragraph that ends in a break as that one Br with its
ParagraphBreakType, so export and re-import give the same story.
- Word and RTF import, Type > Insert Break Character (with odd and even
page breaks added) and typing end the paragraph after a break. Typing
between a break and its return goes to the next paragraph; deleting
either deletes both.
- Layout: a break that ends a paragraph leaves no empty line; the next
paragraph starts in the next column, frame or page, as a paragraph start
option would. Page breaks and Next Page start options move to the next
frame on a later page; odd and even ones to the next page with that
page number parity (no such frame: overset). A break inside a paragraph
from an older document still moves the rest of that paragraph on.
Storing the break as a paragraph separator of its own would have changed
every place that counts or splits paragraphs on '\n'.
Text inside a tracked move destination was dropped on .docx import, so
moved passages vanished from both places. Treat moveTo like ins (both
kept as accepted); moveFrom stays skipped like del. Regression test
covers ins, del, moveFrom and moveTo together. Fixes#256.
Also adds my contributor credit entry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Placing an RTF or Tagged Text file dropped every character outside the
Basic Multilingual Plane: emoji, mathematical letters and the rarer CJK
ideographs such as 𠮷 (U+20BB7, used in Japanese names). The text
around them came in, so the loss was silent. It also broke our own round
trip: Export Text writes these characters correctly and Place lost them.
Both formats carry such a character as a UTF-16 surrogate pair, two
units in a row: \u-10179?\u-8704? in RTF, <0xD83D><0xDE00> in Tagged
Text. Both readers passed each unit to char::from_u32 on its own. Half a
pair is not a character, so each half was dropped.
A small shared Utf16Units joins the units: a first half waits for the
second, and the pair becomes one character. Half a pair with no partner
is still left out, as before.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Remove the docs/superpowers planning spec.
- textimport: cap decompressed zip parts at 64 MiB (protects Place and
xlsx data sources); oversized part is an error. Regression test.
- datamerge: refuse data files over 256 MiB and skip image candidates
over 256 MiB by metadata before reading.
- datamerge: assign_page uses get_mut instead of indexing spreads.
- datamerge: one/limit over u32 are errors, not truncated.
- select_records dedupes with a HashSet (was O(n^2)), order kept.
- Hyperlink cells: only http, https, ftp and mailto schemes link.
- Document data.* commands and data.merge creating a new document.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Records keep empty cells inside the header width and cap the table at
100,000 rows and 200 columns. Place as a table still uses the existing
Excel import, which drops empty cells.
An .xlsx cell reference far from the others no longer allocates the whole
range before the cell cap (billions of cells, out of memory), and long
column names can't overflow. IDML import doesn't trust zip part sizes for
up-front allocation and adds header/footer counts saturating. Tagged Text
and IDML export use let-else; the font fallback has a compiled-in last
resort. These crates deny the panicking lints.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- designcraft-textimport (L3): DOCX (styles by name with spacing/indents/alignment and font,
size, bold/italic/underline/strike/caps/position; runs, tabs, breaks, footnotes, tables,
hyperlink/field results; used styles only), RTF (text, paragraphs, bold/italic/underline/
strike, size, Unicode/cp1252, skipped destinations), plain text (+ tests)
- engine: file.place routes text files into the insertion point, the selected frame or a new
frame on the page; imported styles added (existing win); removeStyles; autoflow adds pages
with threaded frames until the story fits (+ test)
- app/web: Place dialogs accept .docx/.rtf/.txt/.md
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>