Commit Graph
250 Commits
Author SHA1 Message Date
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
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
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
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
skylar taylor-barrick 0e0ed78125 M6: integrate placed-PDF vector layer export with current main (#1241)
Preserve contributor commits and adapt optional-content registration and table-cell graphic calls to the current PDF exporter refactors. Keep current-main roadmap corrections.

All nine cargo xtask ci gates passed on tree d0c21f347e: 3209 tests passed, 6 skipped, 16 WASM-safe crates. The exact PR head also passed 118 PDF unit/integration tests. Independent production-export controls: 8 flat/null cases succeeded; 7 unreadable Prev values refused, 4 compatibility controls succeeded; all 12 emitted PDFs pass qpdf.

This PR is independent of the format-v4 pair (#1647/#1671), which remains held for a reproduced parent-placement IDML issue.
2026-10-24 14:38:11 -06:00
dotCooCoo 6bc5ac36fb Merge main into feat/file-info 2026-10-24 11:30:55 -07:00
alichaudhry55andClaude Opus 5.5 fe44b1cfdd Tables: gradient cell fills draw as gradients on screen and in PDF
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 18:36:54 +05:00
andeecollard 7074dc8a85 M6: preserve per-placement graphic layer options 2026-10-24 13:22:08 +01:00
Dylan M. TaylorandClaude Sonnet 5.5 140681429f refactor: split complex functions in epub table and pdf frame text (Sonar S3776)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-24 08:52:37 +00:00
Dylan M. TaylorandClaude Sonnet 5.5 c313c53497 refactor: split complex functions in ruby, warichu, pdf text, render text, epub, pdfx (Sonar S3776) (#1728)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-24 04:51:58 -04:00
Dylan M. TaylorandClaude Sonnet 5.5 cb763c2583 refactor: split complex functions in psd, table, parabox, index, trace, pdf export, render (Sonar S3776) (#1724)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-24 04:51:26 -04:00
Dylan M. TaylorandClaude Sonnet 5.5 8dc6717ff2 chore: replace glob imports in test modules with explicit names (Sonar S2208)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-24 08:32:10 +00:00
Dylan M. TaylorandClaude Sonnet 5.5 8299d26400 chore: replace trivial closures with method paths (Sonar S1612) (#1714)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-24 04:30:43 -04:00
KnottyDyesandClaude Opus 5.5 4c580086a0 PDF: a null OCProperties is no optional content; an unreadable Prev is damage
A placed file whose catalog has /OCProperties null (literal or through a
reference) exported on main but failed after the layer-choice change; treat it
as absent. Like the trailer guard in #1670, a non-null /Prev that can't be read
as an offset ends nothing and is reported as a damaged cross-reference table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 01:54:58 -06:00
KnottyDyesandClaude Opus 5.5 ea69836146 PDF: carry an explicit layer choice and read trailers structurally
Export takes the placed PDF's layer choice as file defaults or an explicit
hidden list, where an explicit empty list shows every layer, including one
the file hides by default. Today an empty list still means file defaults;
an explicit all-on state can map straight onto Some(&[]).

The incremental writer now reads each cross-reference section's trailer by
walking the table rather than searching for the keyword, follows the bounded
Prev chain for inherited encryption, and treats a literal null /Encrypt as
absent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 01:54:58 -06:00
KnottyDyesandClaude Opus 5.5 a84faee829 PDF: a hidden layer missing from the placed file aborts vector export
Main now aborts an export when a stored hidden layer can no longer be
applied (#876). Vector export follows it: a layer hidden in Object Layer
Options that the placed PDF doesn't have, because the file has no layers
or was relinked, fails with an error naming the file instead of exporting
everything visible. The image-path tests for oversized images and
undecodable content export as PDF/A-2b, which still rasterizes placed
PDFs with hidden layers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 01:54:58 -06:00
KnottyDyes 38d692089f PDF: preserve placed optional-content visibility in vector exports
Keep source defaults and per-frame hidden layers in plain and PDF/X-4 vector exports, including independent repeated placements, booklet output and book merging. Accept usage-application arrays and resource-only groups without a catalog configuration.

Use file-named warnings and all-ON defaults for recoverable default-configuration problems and unsupported base states. Add readable groups omitted from OCGs without discarding their visibility, combine concurrent recovery warnings, and generate missing names only when no frame override needs name matching. Retain errors for contradictory states, unresolved references and ambiguous name overrides.

Cache prepared variants per asset and hidden-name set while replaying recovery warnings through the existing deduplicating channel. Keep original source bytes when every group is visible and named, so reader-repaired cross-reference tables remain usable without an incremental layer update. When hidden groups or generated names require writing, reject an unusable cross-reference table with a file-named damage error and retain the first placement failure.

Cover visibility, recovery, names, independent placements, repaired cross-reference tables and vector output with 46 optional-content integration tests, plus preparation-cache and shared-buffer unit tests.
2026-10-24 01:54:58 -06:00
dotCooCoo a525894355 File Info: PDF export writes the document's title, author, description and keywords
File › File Info… opens a dialog with Document Title, Author,
Description, Keywords, Copyright Notice and Copyright Info URL, and the
document saves them. The new file.info command reads them with {} and
sets the fields it is given; "" or null clears a field, and keywords
are a list or one line split at semicolons and commas. file.info.get
returns the fields without editing the document, so opening the dialog
does not end a Data Merge preview.

Export PDF, PDF/X-4 and Print Booklet write the title, author,
description and keywords into the PDF's document information and XMP.
EPUB export takes the title and author from File Info, and HTML export
takes the title. A title or author given to the export replaces File
Info's, and an empty Document Title leaves the document's name as the
title. Export PDF's dialog no longer sends the document's name as the
PDF title, so the PDF takes its title from File Info; the file name
still comes from the document's name.
2026-10-24 00:20:21 -07:00
Brandon Thomas 058b369b53 Merge storytold/main into squad/pdfx-unembedded-fonts
# Conflicts:
#	docs/gaps.md
#	docs/target-app-parity.md
2026-10-23 17:30:20 -04:00
Brandon Thomas ce7d993f52 Merge pull request #1442 from HEOJUNFO/fix/grey-tiff-device-gray
Export placed greyscale TIFFs as DeviceGray
2026-10-23 17:30:03 -04:00
Brandon Thomas 807d9b5723 Merge pull request #1491 from Hamza0101/fix/underline-under-tabs
An underline runs across a tab, as the rest of the line's text (#1466)
2026-10-23 17:29:54 -04:00
Brandon Thomas 681e37d2cb Merge pull request #1615 from MarekSpodBiedry/issue-1591
Fix slice panics in the PDF post-passes on malformed page trees and metadata (#1591)
2026-10-23 17:26:46 -04:00
aiongg 1991fd5df6 Merge branch 'print-color' into print-color-x1a 2026-10-23 16:52:47 +00:00
aiongg 4c29b60700 Print color: PDF/X-1a conformance level, intent profile and review fixes
- Write GTS_PDFXConformance (PDF/X-1a:2003) in the document information and
  XMP; check_pdfx1a requires it.
- A registered printing condition (Coated FOGRA39) has no DestOutputProfile,
  so no ICC v4 profile goes into the PDF 1.4 file; an unregistered condition
  needs an ICC v2 profile, else the export fails.
- Images the flattener can't read fail the export, naming the page and its
  graphics.
- Shadow mask coverage (with - without) / (255 - without), so partly
  transparent objects drawn over their shadow give the rendered pixel back.
- The dropped-inks warning says their tints were dropped and won't print.
- Type 3 fonts pass; their glyph procedures are checked like page content.
2026-10-23 16:52:05 +00:00