Commit Graph
275 Commits
Author SHA1 Message Date
aiongg 89ef0e3d81 Merge PR #1606: Paragraph Shading and Border pages
crates/ui-egui/src/panels/properties.rs, properties/object.rs: main's split module kept; preview_row and paint_corner_shape exported for the shading corner menus
crates/ui-egui/src/panels/properties/tests.rs: both sides' tests kept
crates/ui-egui/src/dialogs/tests.rs: main's tests kept; the border test uses the new corner labels; the shading layout test added
crates/ui-egui/src/i18n/*.rs: union of keys without duplicates
crates/ui-egui/src/dialogs.rs: the corner shape face uses corner_shape_button (no hard-coded colour in the dialog)
2026-10-30 23:17:40 +00:00
Dylan M. Taylor 15d13dde20 Update prompt shows the app icon and accent color instead of a letter (#2023)
UPDATE_APP (native builds; craft-update is a native-only dependency) carries our 128 px icon and accent (#7bb51c, its colour in ArtCraft Launcher). The updater, the self-update
helper and craft-update's startup prompt all get them through crate::UPDATE_APP; the
prompt draws the icon, the accent and "by ArtCraft" (craft-update's default developer).

craft-update is pinned to storytold/craft-libs main at 1065653e7 (#434).
Clippy fixes for Rust 1.99 in untouched code (same logic):
- crates/engine/src/cmd/export/text_tests.rs
- crates/pdf/src/gradient_inks.rs
2026-10-30 01:28:06 -04:00
Brandon Thomas 56decd96e2 Merge storytold/main into PR #1990 2026-10-29 21:55:23 -04:00
Brandon Thomas e22e6038dd Merge storytold/main into PR #1990
# Conflicts:
#	docs/typography-parity.md
2026-10-29 21:53:38 -04:00
Brandon Thomas e5b30c4527 Merge storytold/main into PR #1238 2026-10-29 21:53:38 -04:00
Brandon Thomas 2a3f92e63b Merge pull request #1919 from Silvertree2010/fix/1881-form-fields-export-with-a-stroke
Export form field borders from the item stroke
2026-10-29 21:53:21 -04:00
Brandon Thomas 45369565ad Merge pull request #1872 from Hamza0101/fix/pdf-name-escaping
PDF export: a name escapes every byte a name cannot hold (#1582)
2026-10-29 21:52:30 -04:00
Brandon Thomas 61adb2063e Merge pull request #1306 from Hamza0101/reproducible-pdf-export
PDF export: SOURCE_DATE_EPOCH stands in for the clock, so exports repeat
2026-10-29 21:52:12 -04:00
Brandon ThomasandClaude Opus 5.5 eace4735da Draw the checkbox border in its own function
Keeps add_fields under the cognitive complexity limit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 21:43:49 -04:00
Brandon ThomasandClaude Opus 5.5 680dfdc964 Format the checkbox border
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 21:41:41 -04:00
Brandon ThomasandClaude Opus 5.5 56eebe9556 Keep the host frame's text area across anchored text frames in PDF export
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 21:40:24 -04:00
Brandon ThomasandClaude Opus 5.5 44b07403b1 Draw a checkbox's border at the item's stroke weight
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 21:40:04 -04:00
moppeeer 165d8c65ac Merge remote-tracking branch 'origin/main' into paragraph-shading-indesign
# Conflicts:
#	ROADMAP.md
#	crates/doc/src/attrs.rs
#	crates/idml/src/export.rs
#	crates/idml/src/import.rs
#	crates/ui-egui/src/dialogs.rs
#	crates/ui-egui/src/i18n.rs
#	crates/ui-egui/src/panels/properties.rs
2026-10-29 11:50:25 +01:00
KnottyDyesandClaude Opus 5.5 d7bc6633e7 Compose: honor discretionary hyphens in single-line composition
Recognize U+00AD independently of automatic hyphenation in the greedy breaker, reusing the existing visible hyphen output. Cover both composers, zero-width unbroken soft hyphens, pattern suppression, and tagged and untagged PDF text extraction.

Fixes #1955

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 00:14:44 -06:00
Claude 5c6909db46 Merge storytold/main (v0.14.0) into inline-objects-under-text
The PDF change moves to export/walk.rs (draw_leaf_content passes the drawn flag back) and the
render test to render/src/tests.rs, with upstream's splits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CgGsJkVe5GYG9qK6VFdECa
2026-10-28 18:03:03 +00:00
Hamza 87b49d9973 PDF export: SOURCE_DATE_EPOCH stands in for the clock, so exports repeat
A PDF export recorded the second it was made in, so exporting one document
twice gave different files although the pages matched -- and the instance id
krilla derives from the bytes followed the timestamp, so nothing about the two
files was comparable.

SOURCE_DATE_EPOCH stands in for the clock when the export is given no date, as
the reproducible-builds specification defines it. A date passed in still wins,
and a value the PDF date format cannot hold -- malformed, negative, or past
the range -- is ignored rather than refusing the export or writing a date no
reader can parse. With the epoch pinned the two files are identical byte for
byte.

Rebased onto the post-split main: export_time and pinned_or live in
export/metadata.rs beside now_unix, which they stand in front of, and the call
site is in export/pipeline.rs. The ROADMAP keeps upstream's 2026-10-17 entry
with this one above it.
2026-10-28 19:09:25 +05:00
Dylan M. Taylor 07366af1b6 Refactor pdf for strict Clippy checks 2026-10-28 11:39:26 +00:00
aiongg 1abeda4459 Merge pull request #1802 from dotCooCoo/fix/column-rules-span-the-text
Text frames: column rules run from the top line's ascent to the bottom line's descent
2026-10-27 13:45:19 -04:00
NetterSilverandClaude Opus 5.5 1db1635a88 Ask the composition cache once per story and page in PDF export
The PDF exporter called Cache::get twice for every text frame it drew.
Each call rebuilds the cache signature of the whole story, which looks up
every frame of the story with Document::find (a linear walk over all
spreads), and the anchored-frame placements walk them again. For a story
threaded through many frames this is O(frames^2 x items) per export.

The document doesn't change during an export, so the exporter now keeps
the composition it got for each (story, page) and reuses it. The cache
still decides what to compose; the output is byte-identical.

Add examples/pdf_export.rs to designcraft-cli: File > Export PDF through
a headless session on the `designcraft-cli perf` stress document (200
pages, 1M characters in 40 threaded stories, 500 placed images). The
stress document generator moves unchanged from src/perf.rs to a small
library module (src/stress.rs) that the binary and the example share.

Ryzen 16 threads, Linux, 12 runs each, interleaved:
  main     median 3485 ms, min 3427 ms
  patched  median 2016 ms, min 1656 ms  (-42 % median, -52 % min)
The exported PDF is byte-identical (12,075,744 bytes).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 16:47:25 +01:00
dotCooCoo 9ef2e64f9f Merge main into the column rule extent fix 2026-10-27 07:16:50 -07:00
NetterSilverandClaude Haiku 5.5 516998d449 Export form field borders from the item stroke
Fixes #1881

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-27 13:33:17 +00:00
aiongg f33de8f4aa refactor(pdf): split export.rs into export/ 2026-10-26 21:00:23 +00:00
aiongg 504d0e9979 refactor(pdf): move the tests module of pdfx.rs to pdfx/tests.rs 2026-10-26 18:18:14 +00:00
Hamza 6f567fdb6d PDF export: a name escapes every byte a name cannot hold (#1582)
A placed media file's type goes into the embedded file's `/Subtype` as a PDF
name, and only `/` was escaped. ISO 32000-1 7.3.5 allows only the regular
characters in a name, so a type carrying a space, a delimiter, a comment
character or `#` ended the name early and left the rest as loose tokens in the
file's structure -- in a PDF the user then distributes.

`pdf_name` escapes every irregular byte as `#xx`. The test covers the shapes
that leaked: a space, `<<`/`>>`, `(`/`)`, `[`/`]`, `%`, `#` itself and a
non-ASCII byte, and checks an ordinary type is unchanged.

This is the escaping part of DC-01 only. The larger part of that issue -- the
post-passes finding the catalog, the page tree and pages by text search over
the output and taking the last match -- needs them to stop rediscovering
objects at all, through the xref chain or by carrying the offsets each pass
writes. That is a rework of `transitions.rs` rather than a patch, and the
issue says a reproduction is available to maintainers privately, which I would
want to test against before changing how objects are found.
2026-10-26 17:06:38 +05:00
dotCooCoo 00d0867d50 Merge branch 'main' into fix/column-rules-span-the-text 2026-10-25 21:39:36 -07:00
aiongg 2e6df922c8 Merge remote-tracking branch 'upstream/main' into print-color-separations
# Conflicts:
#	Cargo.lock
#	crates/engine/src/cmd/export.rs
2026-10-26 02:05:49 +00:00
aiongg 7cc4f7111c Merge pull request #1384 from aiongg/print-color-x1a
Print color: PDF/X-1a export through craft-prepress's flattener
2026-10-25 21:59:30 -04:00
aiongg 3c19a24e68 Merge pull request #1374 from aiongg/print-color
Print color: overprint, knockout, CMYK blending and spot gradients through craft-pdf
2026-10-25 21:58:20 -04:00
Dylan M. Taylor 42463ce99d Reduce cognitive complexity in variables, PDF graphics, compose frames and menu shortcuts (#1847)
* refactor(engine): split convert_all's per-story plan into convert_plan

Resolves SonarCloud rust:S3776 on convert_all (cmd/variables.rs).

* refactor(pdf): split graphic_unclipped into per-kind helpers

Resolves SonarCloud rust:S3776 on graphic_unclipped (pdf/src/export.rs).

* refactor(compose): extract wrap exclusions and frame text area from frame_specs_placed

Resolves SonarCloud rust:S3776 on frame_specs_placed (compose/src/lib.rs).

* refactor(ui): extract command shortcut lookup from shortcut_key

Resolves SonarCloud rust:S3776 on shortcut_key (ui-egui/src/menus.rs).
2026-10-25 21:25:52 -04:00
Dylan M. Taylor e413cdf59b Reduce cognitive complexity in find, lists, PDF font/layer scans; fix wildcard import (#1846)
* test(pdf): replace wildcard super import in registration_tests

Resolves SonarCloud rust:S2208 (new code).

* test(engine): extract child-scope rejection assertions in find tests

Resolves SonarCloud rust:S3776 on find_scope_children_error_atomically_and_parent_requests_stay_top_level.

* refactor(engine): extract per-paragraph list update from apply_list

Resolves SonarCloud rust:S3776 on apply_list.

* refactor(pdf): extract form scanning from FontScan::content

Resolves SonarCloud rust:S3776 on FontScan::content (pdfx/fonts.rs).

* refactor(pdf): extract layer helpers from optional_content::prepare

Resolves SonarCloud rust:S3776 on prepare (optional_content.rs).
2026-10-25 21:25:48 -04:00
aiongg d90881a8a9 Merge branch 'print-color' into print-color-separations
# Conflicts:
#	Cargo.toml
2026-10-26 01:11:35 +00:00
aiongg c856db68f1 Merge branch 'print-color' into print-color-x1a
# Conflicts:
#	Cargo.toml
2026-10-26 01:11:11 +00:00
aiongg 240dbcf731 Merge remote-tracking branch 'upstream/main' into print-color
# Conflicts:
#	Cargo.toml
#	crates/idml/src/export.rs
2026-10-26 01:10:39 +00:00
Dylan M. Taylor 781bec49de Merge pull request #1736 from storytold/sonar/s3776-a4
refactor: reduce cognitive complexity in epub table and pdf frame text (Sonar S3776)
2026-10-25 16:30:47 -04:00
Dylan M. Taylor 05e5f17913 Merge pull request #1717 from storytold/sonar/s2208-a
chore: explicit imports in test modules (Sonar S2208)
2026-10-25 16:29:55 -04:00
aiongg 074db00079 Merge print-color into print-color-x1a
Brings in #1374 with upstream main merged (egui 0.37, craft-libs at
storytold/craft-libs e3b9e239).

Conflicts:
- Cargo.toml: print-color's pins (storytold/craft-libs e3b9e239 for
  craft-pdf, craft-prepress, krilla and the hayro crates; winit and egui
  patches), with craft-prepress keeping the `flatten` feature.
- xtask/src/pdf_deps.rs: print-color's REPO/REV.
- Cargo.lock: print-color's, relocked with cargo metadata; adds only
  craft-prepress and linesweeper.
- crates/pdf/src/export.rs: main's optional-content registration and
  add_form_fields helper; the helper leaves form fields and media out of
  PDF/X-1a with a warning.
- crates/engine/src/cmd/export.rs: the X-1a parameter docs plus main's
  `created`; the X-1a flatten and shadow-mask path on top of main's
  PDF/A-2b-only rasterize_layered.

Also: x1a_tests builds FlattenTotals through Default, since the struct is
non_exhaustive at e3b9e239.
2026-10-25 19:55:21 +00:00
aiongg 9c977f41a6 Merge print-color (#1374 with main: egui 0.37, craft-libs e3b9e239) into print-color-separations
print_pdf, shared by Export PDF, File › Print and Separations Preview, takes main's two
changes to the inline export pipeline: rasterize_layered runs only for PDF/A-2b (other
standards keep hidden placed-PDF layers as optional content), and the report gets
GRAPHIC_LAYER_RENDERING_WARNING when the source document has graphic layer visibility
overrides and the writer did not report it. The Separations Preview command matches
craft_prepress::Conversion with a fallback arm, since the enum is non-exhaustive at
craft-libs e3b9e239.
2026-10-25 19:54:40 +00:00
aiongg 65c387732d Merge upstream/main into print-color
PDF/X and CMYK groups keep this branch's approach: krilla's serialize
settings (pdfx::print_settings) write the CMYK page and transparency
groups, the GTS_PDFX output intent and the PDF/X Info and XMP entries.
make_pdfx4 adds only the XMP dates, so main's title argument and its
output-intent, Info and pdfxid XMP edits are dropped, and both
cmyk_group_spaces calls after writer.finish() (Export PDF, Print
Booklet) stay removed. Main's new parts are kept: file_info_metadata
(also for Print Booklet, on top of the print settings), optional content
registration and layer errors, add_form_fields, and pdfx's fonts module.
Main's metadata panic test calls the two-argument make_pdfx4.

craft-libs and the krilla patch are pinned to storytold/craft-libs
e3b9e239; Cargo.lock is main's, relocked without updates.
2026-10-25 19:47:27 +00:00
aiongg 02cb2876d2 Merge upstream main into docking-trial
Main's File Info PDF metadata gets craft-pdf's modified field (None, as before), and the
Notes panel ids use Id::unique (egui 0.37).
2026-10-25 17:52:22 +00:00
Brandon Thomas fffe2fdc3d Merge pull request #1768 from alichaudhry55/fix/table-gradient-fills
Tables: gradient cell fills draw as gradients on screen and in PDF
2026-10-25 12:26:38 -04:00
Brandon Thomas 743eced7ba Merge pull request #1712 from dotCooCoo/feat/file-info
File Info: PDF export writes the document's title, author, description and keywords
2026-10-25 12:26:32 -04:00
aiongg 56f15a600d Merge upstream main into docking-trial; pin craft-libs to main 5bbe9f62 2026-10-25 16:25:57 +00:00
moppeeer 41b3b5e569 Merge remote-tracking branch 'origin/main' into paragraph-shading-indesign 2026-10-25 17:13:49 +01:00
dotCooCoo 5f77ea123b Text frames: column rules run from the top line's ascent to the bottom line's descent
A column rule ran from the top of the columns to their bottom, so a frame
that the text only half fills drew its rules down to the empty bottom of
the frame. InDesign draws them from the top line's ascent to the bottom
line's descent. column_rule_rects now takes that extent from the frame's
lines, and the top and bottom insets shorten the rule from there. A frame
without lines draws no rules. Existing documents draw their column rules
at the new length, on screen, in PDF and in PowerPoint export.
2026-10-24 18:54:30 -07:00
dotCooCoo 0f037cb67e Merge main into feat/file-info 2026-10-24 18:14:03 -07:00
skylar taylor-barrick a32ad396f3 PDF: give the test-only Graphic literals the layer_options field
Graphic gained an optional layer_options field, so the test-only literals in the PDF
crate and in the engine's optional-content support need it. No behaviour changes.
2026-10-24 17:39:46 -06:00
aiongg 6053b099bc Docking: pin craft-libs to 6e7f28518dd1 (main)
Every craft-libs crate and the Krilla/Hayro patches use one revision, which
also vendors hayro-postscript. Clipping text paints nothing in the PDF bounds
device; its clip arrives through push_clip_path.
2026-10-24 22:58:49 +00:00
skylar taylor-barrick 2927861b32 PDF: apply an explicit all-on layer choice and keep an unrewritable one failing closed
An explicit all-on choice is Some(&[]) for the placed PDF, not the file's defaults, so the
item walk now passes it whenever the placement has a layer choice. Without that, a sheet
that inherits a placed PDF with "show every layer" exported the source's default-hidden
layers instead.

The second half is the fail-closed rule: prepare() skipped writing a group's state when a
visible, named layer needed no change, so an all-on choice on a file whose defaults already
show everything embedded the untouched source - no rewrite, so no error. It now writes every
group's state when a choice is explicit, which is the same catalog rewrite the canvas does,
so a history that cannot take one is reported instead of being embedded.

Regression: the two existing engine tests that were failing (placed_pdf_all_on_is_distinct
_from_defaults_and_survives_exports renders the exported page; explicit_all_on_validates_an
_actual_rewrite_before_editing expects the error for a 33-section xref history).
2026-10-24 15:49:38 -06:00
skylar taylor-barrick ab8ec71234 PDF: keep both layer readers compiling after the merge
Graphic gained an optional layer_options field, so the test-only literals in the PDF
crate and in the engine's optional-content support need it. No behaviour changes.
2026-10-24 15:37:10 -06:00
skylar taylor-barrick 9fc02f8f7b Merge contributor branch codex/placed-graphic-layer-overrides (PR #1647) into main
Conflict resolutions:
- crates/pdf/src/export.rs: keep main's `graphic(.., hidden)` signature from
  #1241 and report retained graphic-layer overrides that drawing cannot apply.
- docs/gaps.md: keep main's change list and add the graphic-layer entry.
2026-10-24 15:30:03 -06:00