Commit Graph
1933 Commits
Author SHA1 Message Date
aiongg cb28b0c194 jpeg: use the quantization table in force at a component's first scan
The test encoder takes several components, scans of any of them, segments
between scans and per-table conditioning, and drops trailing zero bytes as
libjpeg does.
2026-10-24 21:06:47 +00:00
aiongg b636dad56e jpeg: read the reference PPM with as_chunks 2026-10-24 21:03:28 +00:00
aiongg 3acc132ff0 jpeg: mutation test, fuzz target and CI workflow
tests/mutation.rs runs a seeded mutation pass over the test images and
small SOF9, SOF10 and 12-bit seeds, checking that decode never panics
and stays within small limits. fuzz/ is a cargo-fuzz crate (own
workspace) with a decode target and those seeds. jpeg.yml runs fmt,
clippy, tests and a wasm32 check on Linux. The README gains a testing
section.
2026-10-24 21:00:51 +00:00
VoiceLessQ 140c7041e1 Proofing: the spelling options are read in one place
Czech, Croatian, Indonesian, Italian, Polish, Portuguese and Spanish began
their spelling checks with the same thirteen lines, byte for byte: no
dictionary means no marks, single letters and words without letters pass,
words with digits and short words in capitals pass when the File › Options
› Proofing choices say so, and words in another script pass. Each also had
its own copy of the Latin-script test. These were the lines storytold/wordcraft#3966 had to fix
in six copies one by one, and where the Russian and Turkish copies stayed
wrong until the first commit here.

spelling.rs has them once (`word_to_check`), and each language goes on
from there with its own rules (Polish apostrophe endings, Italian elisions,
Indonesian clitics, Uruguayan `vos` forms).

Tests: words_needing_no_lookup. Ignoring the uppercase option there fails
it and all seven languages' proofing-options tests. Proof: 286 tests.
2026-10-24 19:58:30 -01:00
aiongg 3be6c3d09d jpeg: bound decoder memory and work
Check coefficients, planes and output against max_alloc together. Stop an
arithmetic scan once it has consumed more than 8 zero bytes past its data or
misses a restart marker, and cap the decoded blocks at 64 passes over the
frame instead of capping the scan count.
2026-10-24 20:57:23 +00:00
VoiceLessQ a388c4787c Proofing: one language table instead of nine lists
lib.rs named every dictionary language in nine places: reloading for a new
folder, the installed list, load errors, the missing-dictionary hint, the
loaded check, the spelling message, spelling, suggestions and grammar. The
lists had drifted. dictionary_load_error had no Russian or Czech entry, so a
Russian or Czech dictionary that failed to load showed no reason in File ›
Options › Proofing, though the status bar, which reads another list, said
why.

LANGUAGES holds one row per language: its dictionary slot, its spelling
message, and its spelling, suggestion and grammar functions. The nine
functions read the row, so a language now joins with its module, its
ProofLang and one row. The per-language reload_dictionary and
installed_path wrappers had no callers left, inside the crate or out, and
are gone.

Tests: every_dictionary_language_reports_its_load_error, which fails for
Russian on the old code, and the_language_table_covers_the_proofed_languages
(each ProofLang::PROOFED language but English has exactly one row). Proof
285, engine 927 and layout 501 (lib).
2026-10-24 19:55:43 -01:00
aiongg 6fdd2efbba docking: showing a panel with no known place opens it in the first dock group or at an edge (#286)
* docking: open an unanchored panel in the first dock group or at an edge

* docking: an unanchored open prefers the end-side dock; an edge frame is a quarter wide

* docking: drop the redundant edge resize; an edge frame is already a quarter wide

* docking: an unanchored open passes over minimized groups and iconic columns
2026-10-24 16:52:51 -04:00
aiongg f28de79037 jpeg: conformance tests against the libjpeg-turbo test images
Commit testorig.jpg, testimgari.jpg and testorig12.jpg with a jpegtran
Huffman transcode of testimgari.jpg and libjpeg-turbo's 12-bit decode
of testorig12.jpg. The arithmetic-coded image must match its transcode
exactly; the 8-bit and 12-bit decodes are compared against zune-jpeg
and libjpeg-turbo within a small tolerance. Replaces the ignored
conformance_samples test.
2026-10-24 20:49:41 +00:00
VoiceLessQ c19b1cae73 Proofing: one suggestion engine with per-language tuning
Czech's dictionary-driven ranking (case, MAP, REP, missing spaces, KEY,
TRY edits, a stem scan) had five older copies. Polish, Portuguese,
Croatian, Italian and Russian each kept one: 300 to 400 lines of the same
rounds with a few lines of language knowledge mixed in. Fixes reached some
copies only. NOSUGGEST (storytold/wordcraft#1048) and the first-difference tie-break (storytold/wordcraft#797)
never got to all of them, and none had the KEY round or the bound on TRY
letters.

affix_suggest.rs is that ranking once. A language's `Tuning` holds what is
its own: the short words that run into their neighbours (Polish,
Portuguese, Croatian, Italian), Russian's split rule, the letter groups and
confusions its dictionaries lack (Croatian, Russian), letters to try for a
dictionary without a TRY line, and cost hooks for Polish final devoicing
and Russian е/ё. Czech, Indonesian and Spanish use the dictionary's tables
alone. Turkish keeps its own suggester, which reads another dictionary type
with rounds of its own.

The language tables add to the dictionary's instead of replacing them, and
TRY letters are tried in the dictionary's order, commonest first, as the
copies did (the Czech engine sorted them).

Old and new compared on the test dictionaries, with every one-letter typo
of each word plus the tests' misspellings, top eight suggestions: Czech,
Croatian, Italian and Portuguese give the same for all 7,903 inputs.
Polish differs for 44 of 3,244 and Russian for 33 of 2,346, all orderings
among candidates of equal cost, from the tie-break they now share (`ie` →
`nie` before `i`).

New tests pin the moved knowledge: the tuning mechanism, Croatian's groups
without MAP lines, and Russian's costs and split rule. Turning each knob
off fails one. Proof: 283 tests.
2026-10-24 19:49:22 -01:00
VoiceLessQ 172c935fca Proof tests: the user-dictionary file test waits for the dictionary tests
user_dictionary_persists_through_a_file saves the whole user dictionary to
a file and loads it back. Run beside
user_dictionary_and_ignore_all_match_both_normalization_forms, it could
catch a word that test had added and bring it back after the test removed
it, so the other test's last check failed now and then (once in about two
dozen full runs here). It now holds DICTIONARY_TESTS, as the other test
does.
2026-10-24 19:48:47 -01:00
aiongg 6354ef2728 jpeg: README and crate list entry 2026-10-24 20:43:51 +00:00
aiongg 5b609acb6f jpeg: make the decoder a standalone crate
Own Error and Limits types, a Decoded image with typed samples, a
public frame() probe for routing, and the tests adapted to them.
2026-10-24 20:43:14 +00:00
aiongg 1dd057f534 Merge PR storytold/wordcraft#4109: Proofing: web addresses after a no-break space are not marked as misspelled 2026-10-24 20:38:49 +00:00
Higgs32584andClaude Opus 5.5 537ac17c7c JPEG: cap the scans decoded from one stream at 500
Every scan walks its whole MCU grid, and an arithmetic-coded scan does so
even with no entropy-coded data (the decoder is fed zeros), so a small file
repeating empty scan headers multiplied the work by the frame size. Scans
past the cap are dropped and the image is reported damaged, keeping what
the earlier scans decoded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 20:37:15 +00:00
Higgs32584andClaude Opus 5.5 425bd319c0 JPEG: 12-bit and arithmetic-coded files keep their Photoshop resources
The jpeg_ext path set exif, xmp and dpi but not photoshop_resources, so APP13 8BIM blocks
(paths, Photoshop settings) were lost on import and re-export. Both decoder paths now fill
the same Metadata. Test: a 12-bit JPEG with an APP13 block keeps it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 20:37:15 +00:00
Higgs32584andClaude Opus 5.5 5e2d33cd82 JPEG: a Huffman scan that runs out of data is damaged, not clean
Bits now tracks how many buffered bits are zero padding supplied at a
marker or the end of the data; a read that consumes padding marks the
scan exhausted and the block returns an error, so the scan is reported
as damaged (or fails if no MCU decoded) even when EOI is present.
Arithmetic termination is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 20:37:15 +00:00
Higgs32584andClaude Opus 5.5 8566c598e5 JPEG: 12-bit and arithmetic-coded files open (#4934, #5003)
zune-jpeg decodes neither 12-bit extended sequential (SOF1, P=12) nor
arithmetic-coded (SOF9/SOF10, DAC) JPEGs. Route those files to a new
clean-room T.81 decoder (codecs/jpeg_ext.rs): Huffman sequential 12-bit,
Annex D arithmetic decoding with DAC conditioning (sequential F.2.4 and
progressive G.1.3, 8 or 12-bit), restart intervals, 1/3/4 components and
any sampling factors. Float IDCT, centred linear upsampling, the existing
YCbCr / Adobe CMYK / YCCK conventions; 12-bit output is U16 (scaled).
Allocations are checked against Limits first; every stream read is
bounds-checked and damaged entropy data stops the scan with a Truncated
warning instead of panicking.

Tests: a test-only arithmetic encoder round-trips coefficients through
sequential and progressive (incl. successive approximation) scans with
restarts; a hand-built 12-bit SOF1 block; baseline files relabelled SOF1
match zune; truncation/byte-flip sweeps never panic. Locally the
conformance samples testorig12.jpg and testimgari.jpg decode with a mean
abs diff < 1 level vs testorig.jpg (samples not committed).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 20:37:15 +00:00
VoiceLessQ 8f4616eece Proofing: one dictionary slot for every language
Nine languages each kept their own copy of the same code: the installed
dictionary behind a lock, read on first use and logged, replaced by
install_dictionary, forgotten by reload_dictionary, and its file reported
by installed_path. The copies had drifted already. Every one documented that
a dictionary given with install_dictionary has no file and isn't reported,
but only Russian and Czech did that (storytold/wordcraft#3252); the other seven reported the
disk file anyway.

`DictSlot` (proof/src/slot.rs) is that code once. It is generic over the
dictionary type, so Turkish's `TrDict` fits, and keeps the dictionary and
its in-memory origin under one lock as storytold/wordcraft#3252 made it. Each language keeps
its functions as one-line wrappers over its slot, so no caller changes, and
the log lines are the same.

installed_path now follows the documented rule in every language. Nothing
outside tests installs a dictionary in memory, but tests do, concurrently,
so the dictionaries-folder tests in proof and engine could race on the
reported path of a language another test installs. They now accept the
folder's file or none there, and check that the folder is searched first
with hunspell::find, which no install affects.

Tests: proof 279, engine 927, layout 501 (lib); slot.rs has a test of its
own.
2026-10-24 19:33:17 -01:00
VoiceLessQ 0d953d7179 Croatian, Italian and Russian suggestions skip NOSUGGEST words
storytold/wordcraft#1048 (c14e97428) taught affix.rs about NOSUGGEST, the dictionary flag for
words that are correct but never offered (vulgarisms, rare forms), and
patched the Polish and Portuguese copies of the suggestion engine; Czech's
started with it. The Croatian, Italian and Russian copies never got it: they
test candidates with `contains`, so a suggestion could be a word the
dictionary says not to offer. They now use `contains_suggestible` and drop a
suggestion with any part `can_suggest` refuses, as the others do.

Tests: Croatian and Italian run the shared assert_nosuggest_is_honoured.
Russian gets its own, since its MAP and REP tables are built in and the
shared Latin-script check doesn't reach them. Each fails on the old code.
2026-10-24 19:21:57 -01:00
VoiceLessQ f417d88631 Proofing options reach the Russian and Turkish checks too
storytold/wordcraft#3966 made Ignore words in UPPERCASE and Ignore words that contain numbers
reach six languages' own checks, one copy at a time, and Russian and Turkish
were left out. lib.rs still called `ru::is_correct(word)` and
`tr::is_correct(word)`, which accept words with digits (both) and short words
in capitals (Russian) whatever the options say, so turning either option off
changed nothing in those languages.

Both now have `is_correct_with_options`, like the others. Russian's rule for
a part in capitals (`ИИ-консультант`) follows the option too, as Indonesian's
does, and its digits rule keeps counting superscripts (`м²`). Turkish had no
test dictionary, so its two dictionary tests returned early wherever none was
installed, CI included; it has a synthetic one now, also in the
dictionaries-folder test.

Tests: proofing_options_reach_the_russian_check and
proofing_options_reach_the_turkish_check. Calling the old checks from lib.rs
fails each, and so does ungating the Russian part rule or the Turkish hyphen
recursion.
2026-10-24 19:19:25 -01:00
aiongg 17744430be docking: a divider drag keeps the split's fixed or ratio size (#291) 2026-10-24 16:18:39 -04:00
aiongg 24a472717a prepress: transparency flattener (flatten feature) (#194)
* hayro-interpret: ink colors, overprint and font bytes for the flattener

Local patches for craft-prepress's PDF-in adapter, labeled Craft patch
(flatten) and listed in vendor/README.craft-reader.md:

- Device::set_overprint and Device::set_isolated, called where the
  blend mode is set and after an isolated group is pushed.
- Color::ink_paint, ColorSpace::ink_space and ink_values: colors and
  image samples as the inks they print, without converting to RGB.
  RasterImage::with_inks gives native samples with Decode and Matte.
- OutlineFontData::embedded and index, and the CFF bytes of Type1C
  fonts, so text can be embedded again.
- The sh operator ignores /Background (ISO 32000-2 8.7.4.5.1).

* plate-test: flattened-page checks, image alpha and scenes F01, F02

- report::run_flattened separates each scene's page after a caller's
  flattener as well as the original, and structure::transparency lists
  what transparency a page still has. plate-test doesn't depend on the
  flattener.
- Scene images take an alpha channel, written as /SMask.
- F01: a partly transparent image over a flat fill; where its alpha is
  0 the fill keeps its exact inks (vectorcraft#2160).
- F02: art hidden under opaque fills leaves no trace after flattening
  (vectorcraft#2161).

* prepress: transparency flattener (flatten feature)

craft_prepress::flatten turns PDF pages with transparency into pages
without it, keeping inks, overprint and live text, for PDF/X-1a and
other output that can't carry transparency.

- pdf: reads a page into a display list in inks through hayro-interpret:
  paths, outlined strokes, clips, groups, soft masks, overprint, spot
  and DeviceN colors, images in their own color space, gradients,
  rasterized mesh shadings, tiling patterns and glyph runs.
- font: prepares the source fonts for embedding again (TrueType,
  OpenType, CFF wrapped as OpenType); others fall back to outlines with
  ActualText.
- geom: planar regions with the shapes that cover each one (linesweeper).
- compose: composites each region per ink (separable and non-separable
  blend modes, isolated and knockout groups, overprint per plate as ISO
  32000 defines it); regions under images, gradients and soft masks are
  marked complex.
- raster: complex regions as DeviceN images through plate mode, grid
  snapped with a one-pixel outset; flatten_page.
- write: writes the result with the vendored krilla; flatten_document
  writes every page into one PDF with the output intent, page boxes and
  fonts shared across pages when their bytes match.

The regions and the compositor are ported from VectorCraft (commit
c2b15982, crates/pathops and crates/engine/src/cmd/flatten.rs), credited
in the crate README. scripts/plate-check.sh --flatten separates every
scene's flattened page with Ghostscript (local only). CI runs
craft-prepress with all features.

* krilla: DeviceN /Colorants from PDF 1.3, NChannel attributes from 1.6 (patch 11)

* prepress: flatten_document output PDF version and PDF/X version

* prepress: format the PDF/X-1a flatten test

* krilla, craft-prepress: write GTS_PDFXConformance next to GTS_PDFXVersion

PDF/X-1a:2003 files carry GTS_PDFXVersion (PDF/X-1:2003) and
GTS_PDFXConformance (PDF/X-1a:2003). Krilla patch 12 adds
SerializeSettings::pdfx_conformance, and FlattenDocumentOptions passes it
through.

* krilla, craft-pdf, craft-prepress: carry the modification date over

Krilla patch 13 adds Metadata::modification_date, written as /ModDate and
xmp:ModifyDate in place of the creation date. craft-pdf's Metadata gains
modified, and flatten_document takes it from the source's ModDate.

* craft-prepress: count the print annotations a read leaves out

ReadReport::dropped_annotations counts the annotations a printer would
paint (Print flag, not Hidden, with a normal appearance) when
ReadOptions::annotations is false; FlattenTotals sums it.

* craft-prepress: degenerate groups rasterize without a soft mask

A group the region sweep fails on is clipped to its members' boxes, which
no other group's items reach, so the paper is its only backdrop. Its
image is written opaque, as the plates of the items over paper, and the
page keeps no transparency for PDF/X-1a.

* craft-prepress: write untouched JPEG images as they are

A DCT-encoded DeviceGray or DeviceCMYK image that krilla's JPEG
passthrough writes with the same meaning (8 bits, no decode parameters,
the default gray Decode or the inverted Adobe CMYK one) keeps its data
in ImageItem::jpeg, and the writer embeds it unchanged when the image
has no soft mask. Other images are still 8-bit Flate samples.

* craft-prepress: keep group members with no transparency above them

Groups are transitive by bounding box, so a background frame put all the
body text on it under a transparent object far away, and the text came
back as outlined fills. A member is now drawn as it is when it is opaque
and, in every region its shapes are in, only members kept this way are
above it. The region fills under it are the composite of the other
members, and it is drawn over them in paint order.

* prepress: keep the backdrop under elements of a knockout group

* prepress: rasterize complex regions thinner than a point

* prepress: mark flatten errors, reasons and reports non_exhaustive

* prepress: bound the items a tiling pattern fill expands into

Each of up to MAX_TILING_CELLS cells copied the whole cell content, and a
cell can hold many items or an expanded pattern of its own. A fill whose
cells would hold more than MAX_TILING_ITEMS items in all, counting groups,
masks and nested patterns, is read as an image.

* prepress: stroke solid past the dash budget

A dash pattern that cuts a stroke into more than 250,000 pieces is outlined
solid, with the estimate hayro's renderer uses.

* prepress: bound the sampling and memory of rasterized tiling patterns

Each pixel sampled every cell over it, which for a BBox far larger than
its step is millions of cells per pixel; past MAX_TILING_CELLS cells per
pixel the fill paints nothing, and below it the image resolution drops to
keep the samples within MAX_TILING_SAMPLES. The canvases also hold a value
per page ink, so their pixel limits shrink with the ink count to keep the
memory of CMYK canvases, and the canvas size uses checked arithmetic.

* prepress: mark the page reader's error and report non_exhaustive

* prepress: format the tiling tests

* prepress: CodeRabbit fixes: no nonzero overprint mode for images and shadings, count dropped tilings, widen the hhea ascender

* prepress: refuse DeviceN spaces past the output's colorant limit and regions past the separated spot inks
2026-10-24 16:15:41 -04:00
aiongg 96f8ae3d5a docking: a double-click on a minimized group's active tab leaves it expanded (#289)
* docking: keep a minimized group expanded when its active tab is double-clicked

* docking: no extra SetMinimized for a click on the active tab
2026-10-24 16:15:20 -04:00
aiongg 5a371c978e docking: cancel the OS close of a native window that may not close (#288) 2026-10-24 16:14:58 -04:00
aiongg a0679c1dfc docking: dock column groups give way to the filler from their default heights (DockContent::dock_heights) (#287)
* docking: default, compact and fill heights for dock column groups

DockContent::dock_heights gives a panel's body heights in a dock column while the
user has not resized its group. Groups at their default give way down to their
compact heights so the filling group gets its fill height; then every group gives
way down to its minimum. Saved heights stay. Without it, layouts are unchanged.

* docking: dock column defaults give way nearest the filler first

The group nearest the filling group shrinks to its compact height before the next
one gives way, so groups further up keep their defaults.

* docking: shared groups with different minimums split the rest evenly above their minimums

* docking: a group asking for a fill height fills the column beside groups without dock heights
2026-10-24 16:14:38 -04:00
aiongg 16fb3d46e1 docking: the window under the pointer draws the undock hint (#285) 2026-10-24 14:49:29 -04:00
aiongg 47e630e98a Merge pull request storytold/wordcraft#3562 from FedeOlivares/spanish-proofing
storytold/wordcraft#3360, storytold/wordcraft#3553: Spanish proofing (es-ES, es-MX, es-419, es-UY)
2026-10-24 14:43:59 -04:00
aiongg 420d21ad2b Merge pull request #264 from aiongg/docking-batch
docking batch: shared panel docking and native floating windows
2026-10-24 14:25:35 -04:00
Federico Olivares 03bbefd484 Merge origin/main 2026-10-24 14:34:57 -03:00
aiongg 2ab6049d90 krilla: link annotations and page boxes refuse non-finite geometry (patch 13) (#283)
* krilla: link annotations refuse non-finite or degenerate geometry (patch 13)

Link quad points that are empty or non-finite, link rects and page boxes
that overflow when flipped into PDF coordinates, and XYZ destinations with
a missing page or a non-finite point made krilla panic, which aborts a
WASM app. Document::finish returns InvalidAnnotationGeometry,
InvalidPageBox or InvalidDestination instead. Constructor signatures are
unchanged.

* krilla: error messages format page indices without overflow (patch 13)
2026-10-24 09:38:24 -04:00
aiongg 72ca5ea0eb docking: windows sharing the dragged window's title stay faded on macOS 2026-10-24 13:12:58 +00:00
aiongg 6680968a83 docking: DockArea::locked draws a locked workspace; a lock still records native windows 2026-10-24 13:12:58 +00:00
aiongg 5bbf45a20a panes: a bottom pane dragged to a side previews the width it docks at 2026-10-24 13:12:49 +00:00
aiongg 3ddbb0b181 docking: Placement::Split's doc describes the half split 2026-10-24 13:07:07 +00:00
aiongg 95be650aba Merge upstream main into docking-batch 2026-10-24 12:55:36 +00:00
aiongg e179d42418 craft-pdf: write URI links only for http(s) with a host and mailto (#282)
* craft-pdf: write URI links only for http(s) with a host and mailto

A document from someone else can carry any link target, and an export
that turns every hyperlink into a PDF URI action hands those targets to
whoever opens the file: script and data URIs, OS protocol handlers,
UNC paths, //host/share, file:// and smb:// links to a host, and
Windows Search links naming a share.

safe_url allows only http and https URLs with a host and mailto with an
address, after trimming whitespace and comparing the scheme
case-insensitively. It is the rule DeckCraft uses for its slide show
and export, and is available without any feature.

write::uri_target returns the Krilla link target for an allowed URL and
None otherwise, so an app that builds its URI links with it writes no
link annotation for a refused target and leaves the page content as
drawn.

* craft-pdf: safe_url needs a real http(s) host and a mailto recipient
2026-10-24 08:47:54 -04:00
aiongg 734eb11826 Merge pull request #279 from aiongg/pdf-reader-sync
pdf: reader-sync batch, PdfCraft's reader fixes parts 2–5
2026-10-24 08:23:26 -04:00
aiongg fcbf08de14 Merge remote-tracking branch 'upstream/main' into pdf-reader-sync
# Conflicts:
#	vendor/hayro-syntax/SOURCE.json
2026-10-24 12:11:27 +00:00
skylartaylor 25da1314d4 Preserve fractional ink endpoints in aligned text bounds 2026-10-24 12:01:36 +02:00
dotCooCoo 51090dd6e5 Proofing: web addresses after a no-break space are not marked as misspelled
The spelling check found the start of the white-space-delimited token around
a word one byte after the last white-space character. After a no-break space
(two bytes in UTF-8) or an ideographic space (three bytes) that offset is
inside the character, so the token could not be read and was never classified
as a web or e-mail address. With Ignore web and file addresses on, the words
of such an address were still marked as misspelled.

The token now starts after the whole white-space character. A proofing test
covers a web address, a www. address and an e-mail address after the two
spaces, with the option on and off.
2026-10-24 01:54:29 -07:00
skylartaylor a415e404d6 fix(ui): preserve bounds and paint origins for aligned text 2026-10-24 10:51:20 +02:00
Dylan M. TaylorandClaude Sonnet 5.5 a75a9383b3 Sonar S1612: pass PoisonError::into_inner instead of a closure (storytold/wordcraft#4102)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-24 04:33:12 -04:00
Dylan M. TaylorandClaude Opus 5.5 13d766e074 security: bound vendored hayro CCITT rows of unknown height and JPEG 2000 codestream size (#255)
* security: bound vendored hayro CCITT rows of unknown height and JPEG 2000 codestream size

- CCITT: `/Rows 0` decoded to the end of the data; a bit of 2D code can repeat a whole row,
  so a small stream produced gigabytes. Rows are now capped by the output limit (in the
  stream's 1-bit rows) and 2^28 pixels.
- JPEG 2000: the decoder allocated its output from the codestream's own dimensions,
  infallibly. Over 2^28 pixels or the output limit (at least 64 MiB) is refused before
  decoding, and the buffer is reserved fallibly.
- SOURCE.json local hashes updated (ascii_85.rs's had not been after #253).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ccitt): pass the output limit in the columns test

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 04:29:32 -04:00
aiongg d0f4ab75a8 hayro-interpret, hayro-syntax: review fixes for the reader-sync batch
- Patch-mesh triangles are rebuilt after inflation, so the stored dot products match
  the vertices.
- A stencil image restores the fill alpha itself when the saved-state bound skipped
  its save.
- A resolved JP2 palette decodes to 8 bits whatever /BitsPerComponent says.
2026-10-24 08:07:16 +00:00
ab30d37997 chore: resolve SonarCloud maintainability issues (storytold/wordcraft#4083)
* chore: resolve SonarCloud maintainability issues

* fix: restore compiling refactors, revert behaviour changes, run cargo fmt

- fontdb::norm: keep the closure for filter (char::is_alphanumeric takes
  self, not &self, so it does not fit Iterator::filter).
- story.rs: write_mark_rpr needs &mut self (rev_attrs, rpr_change).
- Revert the viewport meta change and the return 0 added to
  workspace_version/sha256, which changed behaviour or masked failures.
- cargo fmt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Import Document in odt page_parts after dropping glob re-export

* fix: apply_kind_properties binds ContentControl; cell_ref keeps char closures

* fix(engine): pass case helpers by value (clippy needless borrows)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Dylan Taylor <dylan@craftmaker.dev>
2026-10-24 03:54:19 -04:00
skylartaylor 8c05070a63 MIG-037: integrate current shared-library main and clarify performance reports
Preserve the reviewed current-main print/vendor changes, render the benchmark
font path as readable JSON, and document the implemented example harness.
2026-10-24 09:48:06 +02:00
aiongg 4bd8b167da Merge part 5: ICC profile cache key 2026-10-24 07:46:44 +00:00
aiongg 89a5b2cf2e hayro-interpret: port PdfCraft's ICC profile cache key (#3020)
Cache the color space of an /ICCBased profile by its stream object, and on
a miss by its dictionary key and raw bytes, instead of the dictionary's
bytes alone, so two profiles with the same /N and /Length each draw through
their own profile. InterpreterCache::icc_profile_reads counts profile reads.
PdfCraft's two raster tests are adapted in tests/upstream_raster.rs.
2026-10-24 07:46:17 +00:00
aiongg 3648e60159 pdf: keep both sides' upstream_raster tests from the /Default color spaces merge 2026-10-24 07:43:00 +00:00
aiongg f8cbf0e1c2 pdf: keep both sides' upstream_raster tests from the /Default color spaces merge 2026-10-24 07:42:37 +00:00