23 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| a994213e8b |
feat(pdf): tagged structure tree, and the BMC bug that turned red green
Phase 8 #7 of REVIEWS/DART_PDF_VS_MAKEPAD_PDF_GAP_ANALYSIS.md ("PDF/UA structure tree"). Design and merge criteria in REVIEWS/adr/0011-pdf-structure-tree.md. Investigating the accessibility gap surfaced four defects in the marked-content operators the structure tree depends on. The first is not an accessibility problem at all. 1. BMC never parsed, and corrupted the colour of everything after it. The dispatcher matched b'B'+b'M' only when the third byte was a delimiter; BMC's third byte is 'C', so the arm never fired. Because the operator was never recognised it never CLEARED ITS OPERAND, and the leftover /Tag shifted the operands of whatever came next: 1 0 0 rg -> RgbFill(1.0, 0.0, 0.0) red /Span BMC 1 0 0 rg -> RgbFill(0.0, 1.0, 0.0) green Tagged documents are precisely the ones containing BMC, so the documents that tried hardest to be accessible rendered wrong colours. This is the fifth instance of the operator-shadowing family already fixed for cm, rg, gs, b/b* and end-of-stream text operators. ADR 0009's every_multi_char_operator_parses_as_itself test exists to stop exactly this - and would have caught it, except BMC was one of two operators excluded from its table as "genuinely unimplemented". Excluding a known-broken operator from the test whose job is finding broken operators is how it survived. The exclusion list is gone. 2. BDC discarded its property list, which carries /MCID - the only link between a run of page content and the structure element describing it. Without it a tree can be parsed but never attached to anything. 3. The op produced by BDC was named MarkContentBmc, and BMC produced nothing. The names were the wrong way round, which is how the missing arm survived review: the enum looked like it had a BMC case. 4. Found while fixing 2: the content lexer had no dictionary support at all. It read `<` as a hex string without checking for a second `<`, so `<</MCID 0>>` parsed as the string "0C0D0". Content streams now parse direct objects properly, bounded at 16 levels; a single `<` is still a hex string and a test pins that. On top of that, new pdf-document/src/structure.rs: /StructTreeRoot, /StructElem trees, /RoleMap resolution, depth-first reading order, /Alt, /ActualText, /E, /Lang, and MCID-to-element lookup. Cycles in /K are cut at the first repeat and reported by object number rather than expanded to the depth bound. Accessibility findings are mechanical checks reported as findings, NOT a conformance verdict: there is no is_pdf_ua and no ComplianceReport, and a mutation-checked tripwire test fails if either appears. Real PDF/UA conformance needs human judgement - whether /Alt text is accurate is not mechanically decidable - so claiming it from six checks would be exactly the overclaim this codebase keeps removing. 7 corpus fixtures, 14 acceptance tests, 22 unit tests, and a parse_content_dict fuzz target for the new object parser. TEST_TARGET=pdf 536 -> 575, TEST_TARGET=pdf-ui 580 -> 619. rustfmt and clippy -D warnings clean. |
|||
| e45a717ce7 |
fix(pdf-ui): enable the makepad-widgets test feature so ui.rs compiles
|
|||
|
|
5e714577fb |
ci: require full 40-character SHAs for git dependency revs
Some checks failed
Payment domain, storage, platform and UI / isolated-payment-tests (push) Has been cancelled
Payment domain, storage, platform and UI / payment-ui-tests (push) Has been cancelled
PDF engine / engine (push) Has been cancelled
PDF engine / makepad-integration (push) Has been cancelled
PDF engine / fuzz (push) Has been cancelled
repo hygiene / hygiene (push) Has been cancelled
nigig-build (CAD) / supply-chain (push) Has been cancelled
nigig-build (CAD) / cad-module (push) Has been cancelled
nigig-build (CAD) / full-crate-check (push) Has been cancelled
nigig-map / test (push) Has been cancelled
Upstream adopted the makepad fork fix (
|
||
| 456cfa5a68 |
fix: use re-exported makepad-test from makepad-widgets
Some checks failed
nigig-build (CAD) / supply-chain (push) Has been cancelled
nigig-build (CAD) / cad-module (push) Has been cancelled
nigig-build (CAD) / full-crate-check (push) Has been cancelled
nigig-map / test (push) Has been cancelled
PDF engine / engine (push) Has been cancelled
PDF engine / makepad-integration (push) Has been cancelled
PDF engine / fuzz (push) Has been cancelled
repo hygiene / hygiene (push) Has been cancelled
Remove direct dependencies on makepad-test and use the re-exported version from makepad-widgets instead. This avoids path dependency issues and follows the correct pattern for using Makepad crates. Changes: - Add 'test' feature to makepad-widgets dependencies - Remove direct makepad-test dependencies - Update imports to use makepad_widgets::makepad_test Affected crates: - crates/apps/map - crates/apps/pdf/pdf-makepad - crates/apps/spreadsheet/spreadsheet-ui |
|||
| 8fdff3ff55 |
Update makepad fork to a79f0dc (remove duplicate dependencies)
Some checks failed
nigig-build (CAD) / supply-chain (push) Has been cancelled
nigig-build (CAD) / cad-module (push) Has been cancelled
nigig-build (CAD) / full-crate-check (push) Has been cancelled
repo hygiene / hygiene (push) Has been cancelled
nigig-map / test (push) Has been cancelled
PDF engine / engine (push) Has been cancelled
PDF engine / makepad-integration (push) Has been cancelled
PDF engine / fuzz (push) Has been cancelled
Payment domain, storage, platform and UI / isolated-payment-tests (push) Has been cancelled
Payment domain, storage, platform and UI / payment-ui-tests (push) Has been cancelled
Commit a79f0dc fixes the duplicate dependency declarations that were causing TOML parsing errors. This is the correct commit to use after the parallel fixes in 5eda8056 and 11375214. All 34 Cargo.toml files updated to reference the correct commit. |
|||
|
|
80425bbfb8 |
fix: repair the makepad fork and unblock the build
Some checks failed
nigig-build (CAD) / supply-chain (push) Has been cancelled
nigig-build (CAD) / cad-module (push) Has been cancelled
nigig-build (CAD) / full-crate-check (push) Has been cancelled
nigig-map / test (push) Has been cancelled
Payment domain, storage, platform and UI / isolated-payment-tests (push) Has been cancelled
Payment domain, storage, platform and UI / payment-ui-tests (push) Has been cancelled
PDF engine / engine (push) Has been cancelled
PDF engine / makepad-integration (push) Has been cancelled
PDF engine / fuzz (push) Has been cancelled
The repository has been uncompilable since the d6d1f99c fork bump. Root cause was in gitdab.com/andodeki/makepad, not here, so the fix went there first: commit 5eda8056 on portallist_flow_adaptive_view. TWO defects, both introduced by the "Update fork to upstream dev 5d4483f" merge, both pure losses rather than intentional changes: 1. widgets/Cargo.toml: the makepad-gltf / makepad-csg / makepad-test dependency lines were relocated from [dependencies] to below [features]. Cargo then parses each as a feature whose value should be an array, giving "invalid type: map, expected a sequence", and the gltf/csg/test/maps features cease to exist. 2. widgets/src/lib.rs: the feature-gated re-export block for those same crates (plus makepad_fast_inflate and makepad_mbtile_reader) was deleted outright. Fixing only the manifest surfaced this as "no `makepad_csg` in the root". Both restored verbatim from 2c5cd97, the last rev that resolved. Neither is a judgement call: the moved lines are byte-identical and the deleted block is copied back unchanged. This repo is then repinned from d6d1f99c to the fixed rev, full 40-char SHA per the pinning convention CI enforces. Verified end to end after removing the local git redirect used during development, so this resolves against the real remote: cargo metadata resolves nigig-build --lib 685 passed cad_integration 154 passed spreadsheet-engine 225 passed doc-engine 53 passed nigig-map (maps feature) compiles Cargo.lock unchanged, --locked passes Also resolved committed conflict markers in two workflow files, which had made nigig-build.yml invalid YAML -- the CI config could not be parsed at all: - nigig-build.yml: kept --include='*.rs' on the by-value-getter gate. Without it the gate scans ARCHITECTURE.md and fails on its own documentation, which is the bug fixed in 4f32b1c. - pdf.yml: kept upstream's side. Enumerating targets via `cargo fuzz list` and failing when the list is empty is strictly better than a hardcoded target list that silently passes vacuously if a target is renamed. That makes four files in three commits now carrying committed conflict markers from this merge. Worth checking how they are reaching main -- `git diff --check` catches exactly this and is already a step in the nigig-build workflow, but it only runs on paths under that workflow's filter. |
||
| 4eebae14f2 |
Update makepad fork to 11375214 (Cargo.toml fix)
Some checks failed
nigig-build.yml / Update makepad fork to 11375214 (Cargo.toml fix) (push) Failing after 0s
pdf.yml / Update makepad fork to 11375214 (Cargo.toml fix) (push) Failing after 0s
nigig-map / test (push) Has been cancelled
Payment domain, storage, platform and UI / isolated-payment-tests (push) Has been cancelled
Payment domain, storage, platform and UI / payment-ui-tests (push) Has been cancelled
- Fixed TOML parsing error where fork-specific dependencies were in wrong section - Dependencies now correctly placed in [dependencies] before [features] - Maps feature should now be properly recognized |
|||
| d8d29c226d |
feat(pdf): transparency, and four operator-parsing bugs it exposed
Phase 8 #5 of REVIEWS/DART_PDF_VS_MAKEPAD_PDF_GAP_ANALYSIS.md ("Advanced transparency"). Design and merge criteria in REVIEWS/adr/0009-pdf-transparency.md. The headline defect was not that transparency was missing. `gs` was MIS-PARSED as CloseStroke: /GS0 gs 1 0 0 rg 100 100 200 200 re f -> [CloseStroke, RgbFill, Rectangle, FillWinding] so every page setting a graphics state gained a stroked path the document never asked for, and lost its alpha, blend mode and soft mask silently. Downstream everything was dead: StrokeExtGState/FillExtGState were never constructed, set_fill_opacity was never called and emitted no command when it was, and PdfPage::ext_gstate was read by no code at all. New pdf-graphics/src/transparency.rs: full /ExtGState (ca, CA, BM, SMask, LW, LC, LJ, ML, D, AIS, TK), all sixteen blend modes including the four non-separable ones, soft masks with /S, /G, /BC and a /TR evaluated through ADR 0006's PdfFunction, and /Group parsing. Wired end to end: parser -> PdfOp::SetExtGState -> device -> RenderCommand -> Makepad renderer. Compositing is NOT claimed. Backdrop blending needs render-to-texture, which an engine-neutral crate has no framebuffer for. Constant alpha is applied because it needs no backdrop; blend modes and soft masks are reported through TransparencyError::Unsupported rather than dropped, because a silently ignored /Multiply looks exactly like a correct /Normal. The ADR required a test enumerating every multi-character operator, on the grounds that fixing the third instance of a shadowing bug (after cm and rg) without preventing the fourth is not a fix. It immediately found three more live bugs, none of them transparency-related: - `b` and `b*` dropped their close-path, mapping to FillStroke* instead of CloseFillStroke*, so every closed-and-stroked path drew with a gap. - Text operators within three bytes of the end of a content stream were mis-parsed: the b'T' arm guarded `*i + 3 < len` while reading only two bytes, so a stream ending in `/F1 12 Tf` parsed as FillWinding. And the transparency-group fixture found a fourth: - PdfPage::xobjects was empty for every page of every document. extract_xobjects called doc.resolve(), which follows the reference, then asked the resolved object for as_ref() - always None - and only accepted a bare dict when every XObject is a stream. No `Do` operator could be resolved through the page model. Same defect as the one that once destroyed annotation object references; it now has its own regression test. T* and BMC are genuinely unimplemented and are deliberately excluded from the guard's table rather than papered over. 7 corpus fixtures, 12 acceptance tests, 32 unit tests asserting the §11.3.5 formulas (not our own output), and a parse_ext_gstate fuzz target. TEST_TARGET=pdf 451 -> 497, TEST_TARGET=pdf-ui 495 -> 541. rustfmt and clippy -D warnings clean. |
|||
| 8c9ccb92cc |
Update makepad fork to latest dev branch (d6d1f99c)
Some checks failed
nigig-build.yml / Update makepad fork to latest dev branch (d6d1f99c) (push) Failing after 0s
pdf.yml / Update makepad fork to latest dev branch (d6d1f99c) (push) Failing after 0s
nigig-map / test (push) Has been cancelled
Payment domain, storage, platform and UI / isolated-payment-tests (push) Has been cancelled
Payment domain, storage, platform and UI / payment-ui-tests (push) Has been cancelled
- Sync with upstream commit 5d4483f (latest map improvements + platform updates) - Include location API, audio echo cancellation, bridge-dz overlay - Add new libraries: geodata, map_nav, i_float, i_shape, i_tree, converse, llama vision - Preserve all fork-specific re-exports (gltf, csg, test) - All 102+ map improvements now available: 2D/3D toggle, shadows, labels, overlays, pattern fills |
|||
| bea1fd884e |
chore: update makepad fork to include upstream map improvements
Some checks failed
nigig-build (CAD) / supply-chain (push) Has been cancelled
nigig-build (CAD) / cad-module (push) Has been cancelled
nigig-build (CAD) / full-crate-check (push) Has been cancelled
nigig-map / test (push) Has been cancelled
Payment domain, storage, platform and UI / isolated-payment-tests (push) Has been cancelled
Payment domain, storage, platform and UI / payment-ui-tests (push) Has been cancelled
PDF engine / engine (push) Has been cancelled
PDF engine / makepad-integration (push) Has been cancelled
PDF engine / fuzz (push) Has been cancelled
Updated all makepad dependencies from rev 2c5cd97 to 817d881 which includes: - Terrain hillshade landcover draping (drape.rs) - Route overlays, markers, and position puck (overlay.rs) - Map icon management system (icons.rs + 50 SVG icons) - 3D road elevation and seamless joins - Building shadow geometry and terrain shadows - Night themes and emissive roads - Water, grass, and shrub rendering - Optimized road geometry with 2D/3D mode transitions - i_overlay library for polygon boolean operations This brings nigig-map in sync with the latest makepad dev branch improvements. |
|||
| 7d21532ebf |
feat(pdf): real colour spaces, ICC profiles and PDF functions
Phase 8 #4 of REVIEWS/DART_PDF_VS_MAKEPAD_PDF_GAP_ANALYSIS.md ("Advanced colour"). Design and merge criteria in REVIEWS/adr/0006-pdf-advanced-color.md. The interpreter tracked only the *name* of the active colour space and then passed sc/scn operands to the device as if they were already RGBA. Every non-device space therefore rendered a confident wrong colour with no error: /Spot cs 1.0 scn full tint of a spot ink -> pure red /Idx cs 3 scn palette entry 3 -> near-black /Lab cs 50 0 0 scn mid gray -> white (clamped) /DevN cs (5 inks) five colorants -> inks 5+ discarded /ICCBased profile-defined colour -> profile discarded DeviceCMYK also used the additive 1-c-k conversion, which crushes any colour printed over black. Three new modules in pdf-graphics: - function.rs PDF functions, all four types. Type 4 runs on a bounded interpreter: depth 32, 32768 tokens, stack 100, 100000 steps, and an unknown operator is an error rather than a no-op that would leave a plausible wrong colour. - icc.rs ICC matrix/TRC and gray kTRC profiles, applied exactly. LUT-class profiles are reported as such and the caller falls back to /Alternate; they are never pretended to be matrix profiles. - colorspace.rs All eleven families, converting through XYZ with Bradford adaptation and a real sRGB transfer function. Wiring: - PdfDevice gains set_stroke_components/set_fill_components, so SC/SCN reach the device as components of the active space instead of being read positionally as RGBA. - cs/CS now resets to the space's initial colour (table 74), which is why golden/colors.txt gains a line. - PdfPage::color_spaces carries /Resources /ColorSpace fully dereferenced with streams decoded; a half-resolved space would make every ICC profile, palette and type 0/4 transform silently fall back. - A space that cannot be resolved keeps the previous colour and records a typed ColorError. No colour is invented, and no error is swallowed. Tests: 12 corpus fixtures under tests/corpus/color/, 14 acceptance tests in pdf-document/tests/color.rs asserting numeric RGB (the broken code produced a colour for every one of these; only the value was wrong), plus unit tests per function type and per curve type. Two fuzz targets added: eval_function and parse_colorspace. TEST_TARGET=pdf 387 -> 447 passing, TEST_TARGET=pdf-ui 431 -> 491. rustfmt and clippy -D warnings clean. |
|||
| 60db0f21db |
build: update Nigig to Makepad dev reexport fork
Some checks failed
nigig-build (CAD) / supply-chain (push) Has been cancelled
nigig-build (CAD) / cad-module (push) Has been cancelled
nigig-build (CAD) / full-crate-check (push) Has been cancelled
PDF engine / engine (push) Has been cancelled
PDF engine / makepad-integration (push) Has been cancelled
PDF engine / fuzz (push) Has been cancelled
|
|||
|
|
b5471e32e3 |
fix(cad): make the crate buildable, testable and safe to ship
Some checks failed
nigig-build (CAD) / supply-chain (push) Has been cancelled
nigig-build (CAD) / cad-module (push) Has been cancelled
nigig-build (CAD) / full-crate-check (push) Has been cancelled
PDF engine / engine (push) Has been cancelled
PDF engine / makepad-integration (push) Has been cancelled
PDF engine / fuzz (push) Has been cancelled
Phases 0-2 of CAD_ASSESSMENT_AND_PLAN.md. The crate did not compile and no
test had ever run; it now builds clean with a green suite.
Build and CI (Phase 0)
- Pin all 33 git dependency manifests to an explicit rev. A branch
dependency re-resolves on every build and is a code-execution path into
CI if force-pushed.
- Commit Cargo.lock (540 packages). Producing it required fixing three
resolution failures the workspace had always had: a non-existent
makepad-widgets feature, two rusqlite versions both linking sqlite3, and
four missed CellId call sites in spreadsheet-ui.
- Add .forgejo/workflows/nigig-build.yml.
- Replace five stale CAD docs that contradicted the code with one
ARCHITECTURE.md; add PHASE0/1/2_STATUS.md and TEST_BASELINE.md.
Correctness (Phase 1)
- Rotation units: transform_point bound sin_cos() backwards, transposed X
and Z, and applied axes in reverse order, so every exported STL was wrong
even at zero rotation. It now shares the renderer's matrix helpers.
- GLB quaternions had norm 0.125 (half-angle applied to cos/sin, degrees
read as radians) - invalid per the glTF spec.
- PDF wall/door/window yaw fed degrees to cos/sin.
- Fix a TOCTOU unwrap in touch picking; viewport.rs now has no unwrap().
- CommandContext gains update_node/insert_node_at/node_index: resize and
modify were delete+create, silently moving nodes to the end of the scene.
- Wire MAX_UNDO_LEVELS (defined, exported, never read) and switch the undo
stack to VecDeque; this also made the existing drag-merge logic reachable.
- CadNode::size() returned a fake 1x1x1 for CSG and extruded solids, making
them unpickable outside a 1x1x1 box at their origin.
- Reject non-finite script input; makepad_csg clamps NaN rather than
propagating it, so bad input produced silently wrong geometry.
Test baseline: 0 -> 722 passing, 0 failing
- 17 pre-existing failures fixed: 10 real defects (dependency-cycle
detection, over-allocation of unassigned tasks, quote/backslash
corruption on save, default rooms lost for all but the first region,
RGA text ordering) and 7 tests that were themselves wrong, each checked
against its production caller first.
Security (Phase 2)
- env!("CARGO_MANIFEST_DIR") was used as a runtime path in three places,
including as the AI agent's working directory. All runtime data now goes
under app_data_dir().
- Remove the hardcoded LAN LLM endpoint. It is now opt-in via
NIGIG_CAD_LOCAL_OPENAI_URL/_MODEL and refuses plaintext HTTP to anything
but loopback.
- Bound and content-sniff AI image attachments (8 MB cap, magic bytes);
the MIME type came from the filename extension.
- Escape SVG/HTML output, and add SRI to the exported viewer's script tag.
The pinned model-viewer@3.5.1 does not exist, so every exported viewer
was silently broken; now 4.0.0 with a verified hash.
- Stop embedding $USER in exported PDFs and logging document content in
release builds.
- CI now rejects reintroducing the runtime-path and hardcoded-endpoint
classes; both gates were verified to fail on a reintroduced defect.
Add system_prompt.md and embed it with include_str!. The file was missing
from the repository, so the agent silently used a one-line fallback.
|
||
| d3ccc2e00f |
test(pdf): close the three gaps carried from Phases 4 to 7
Three items were carried forward as known gaps rather than quietly dropped. This addresses all three; two are closed outright and one is bounded by an environment limit that is now documented rather than implied. 1. Fuzzing had never actually run (Phase 6 step 6.3). The five cargo-fuzz targets were only compile-checked, so "zero panics on arbitrary input" was an aspiration. They have now been run under nightly libFuzzer: parse_object 1,970,750 runs parse_xref 2,471,345 runs decode_stream 1,120,019 runs parse_content_stream 2,655,663 runs parse_document 2,381,367 runs About 10.6 million executions in total, no crashes and no new findings. That is a real result rather than a green checkmark: the three crashes the corpus found in Phase 6 were the ones worth finding, and the fuzzer confirms the fixes hold under adversarial input. 2. Combo dropdown overlay (Phase 4 step 4.3). A combo box that cannot be opened is a text field with extra steps, so the open list is real state, not a rendering detail. Clicking a combo box opens its options; the dropdown takes a click before any field underneath it, matching the draw order; choosing a row sets the value through DocumentFormEditor; clicking elsewhere dismisses it without changing the value. render_open_combo() returns placement data so the drawing code stays trivial and the geometry is testable without a renderer. 3. Makepad event delivery. Upstream added a makepad_test framework, so this is now testable in principle. Adds a test host binary and six UI tests that drive the widget through the Studio protocol: a real click on the fixture link must surface OpenUri on the host, typing must reach the field, and a click on empty space must emit nothing so the positive assertions are not vacuous. They are #[ignore] by default because the Studio hub cannot start an app in this sandbox: the harness launches with --stdin-loop, which Makepad refuses without a Studio websocket, and the build exits 101 before startup. Upstream own spreadsheet-ui and map UI suites fail identically here with the same error, so this is the environment rather than this code. The tests are checked in and compiled by cargo test so they cannot rot, CI runs them where a hub exists, and the module documents how to run them by hand. Getting there also fixed a real defect in the test host: it copied ui.main_view.render() from the spreadsheet app startup hook, but a plain View has no render method, so the app errored at startup. Validation: TEST_TARGET=pdf ./tools/test-rust-clean.sh (270 tests) TEST_TARGET=pdf-ui ./tools/test-rust-clean.sh (321 tests, 6 ignored) cargo +nightly fuzz run <target> -- -max_total_time=60 (5 targets) Both rustfmt and clippy -D warnings clean. |
|||
| 1c7dc9e9c0 |
feat(pdf): complete Phase 7 performance work
Phase 7 of REVIEWS/DART_PDF_VS_MAKEPAD_PDF_GAP_ANALYSIS.md, taken only now because the review is explicit that it comes after correctness is proven. The caching and threading live in pdf-graphics rather than the Makepad crate because none of it needs a GPU. That is what lets the staleness, eviction and cancellation rules be tested without a window; the widget keeps only the parts that genuinely need a Cx. Step 7.1, off-thread parsing (new pdf-graphics/src/worker.rs): - RenderWorker interprets content streams on a background thread and returns results tagged with the Generation they were requested for. - PendingPages tracks in-flight pages so the widget can draw a placeholder and never queues the same page twice. - Drop joins the thread rather than detaching it: a detached thread writing into a dropped channel is the kind of shutdown race that surfaces as a flaky test months later. - A content stream that fails to parse yields an empty page, so one broken page cannot take down the document. Step 7.2, texture and memory management (new pdf-graphics/src/cache.rs): - PageCache is an LRU keyed by page index with a byte budget, not an entry count: one image-heavy page can outweigh fifty text pages, so counting entries would evict the wrong things. - retain_around() releases pages that scrolled out of view, keeping a margin so a small scroll does not immediately re-render. - Decoding produces DecodedImage bytes off-thread; GPU upload stays on the UI thread. - A page larger than the whole budget is still stored, since refusing it would mean re-rendering it every frame. Step 7.3, render command cache: - CachedPage holds the interpreted Vec<RenderCommand>, so replaying a page skips re-parsing its content stream. - invalidate_appearance() marks a form edit dirty without discarding the commands, because a form edit changes what is drawn over the page, not the page content stream. Widget wiring: PdfPageWidget carries a generation, refuses PageContent from a superseded document, and draws a placeholder while a page is still rendering. Exit criterion (new pdf-document/tests/phase7_exit_criterion.rs, 9 tests) against a new 60-page corpus fixture. Measured here: first page 3ms against the 200ms budget, and a warm cache read 389x faster than re-parsing (1us vs 389us). The budget is deliberately loose because a shared CI runner is unpredictable and a flaky performance test gets muted, and a muted test is worse than none; it still catches the order-of-magnitude regression the review is guarding against. Memory is asserted bounded across 30 document switches, and every page of an abandoned document is refused. Validation: TEST_TARGET=pdf ./tools/test-rust-clean.sh (270 tests) TEST_TARGET=pdf-ui ./tools/test-rust-clean.sh (307 tests) Both rustfmt and clippy -D warnings clean. |
|||
| eacc86077e |
feat(pdf): complete Phase 5 font engine and text metrics
Phase 5 of REVIEWS/DART_PDF_VS_MAKEPAD_PDF_GAP_ANALYSIS.md. Step 5.1, real metrics (new pdf-graphics/src/sfnt.rs): - Reads head, hhea, OS/2 and hmtx from embedded TrueType/OpenType programs, which the review asks for by name. sTypoAscender/Descender are preferred over hhea when non-zero, since subset fonts often zero them; sCapHeight and sxHeight are only read from OS/2 version 2 and later, because reading them from a v1 table returns whatever bytes follow it. - Every read is bounds-checked. A table pointing outside the file, a truncated directory or a zero unitsPerEm yields None rather than a panic or a divide by zero. These are untrusted embedded programs. - resolve_font() now reads FontFile/FontFile2/FontFile3 and falls back to the descriptors declared Ascent/Descent only when no program is embedded. - renderer.rs looks up a per-font ascent instead of leaving ascent_em permanently None, which had left the fallback ratio always in effect. Step 5.2, accumulated advances (new pdf-graphics/src/advance.rs): - measure_advance() implements PDF 32000-1 9.4.4 properly: (w0/1000 * Tfs + Tc + Tw) * Th/100, with Tw restricted to single-byte code 32. Applying Tw to a two-byte code whose low byte is 32 is a classic composite-font bug and is now covered by a test. - glyph_advances() gives per-glyph widths so a caret or a partial selection rectangle no longer assumes even spacing. - GlyphAdvanceCache is keyed by font name and size as the review specifies, and carries a generation so it rebuilds when page data changes rather than every frame. Redefining a font drops its stale measurements. Step 5.3, composite fonts: - The old build_cid_widths truncated every CID to u8, so any glyph above 255 silently took the default width. It is replaced by CidWidths keyed by the real CID, with the CMap resolved from a predefined name or an embedded stream. - ResolvedFont::decode_text returns None for a composite font with no ToUnicode map instead of guessing Latin-1 and producing plausible nonsense. Tests: new tests/phase5_exit_criterion.rs asserts the exit criterion. Selection across a proportional and a monospaced run verifies the second run starts at the first ones true end; per-glyph advances sum to the run advance and H measures wider than l, which an even-spacing estimate would not. Copy returns correct Unicode through ToUnicode including a non-ASCII scalar, and through WinAnsi for simple fonts. Search finds both hits across two lines with rectangles inside their own runs and the right vertical order. Cache reuse is asserted through hit and miss counts. Validation: TEST_TARGET=pdf ./tools/test-rust-clean.sh (203 tests) TEST_TARGET=pdf-ui ./tools/test-rust-clean.sh (238 tests) Both rustfmt and clippy -D warnings clean. |
|||
| 8bc94253ff |
feat(pdf): complete Phase 4 with an interactive page widget
Phase 4 of REVIEWS/DART_PDF_VS_MAKEPAD_PDF_GAP_ANALYSIS.md. The previous commit added the interaction logic but left it an orphan: nothing routed Makepad events into it, which is the exact defect the review names when it says PdfPageView declares is_interactive() -> false and an empty handle_event. New pdf-makepad/src/page_view.rs, PdfPageWidget: - is_interactive() returns true and handle_event routes real events. - Step 4.1 ordering: form field events, then annotation hit testing, then text selection. The order is enforced inside InteractionState::click so it cannot drift between the widget and the tests. - Step 4.2: link underlines and form widget borders are drawn, and the cursor changes over links and text fields. - Step 4.3: field values draw inside their widget rects, a focused field is highlighted and shows its uncommitted buffer. - Step 4.4: navigation is emitted as PdfPageAction to the host. The widget never opens a URL (rule 5). - Key focus loss commits the pending edit, so an edit is never silently lost, while Escape still abandons it. - Only keys the editor handles are consumed; everything else falls through to the host instead of being swallowed. The widget keeps no decision logic of its own: it syncs the viewport from its on-screen rect and delegates. That keeps the part that needs a live Cx as small as possible, because it is the part that cannot be unit tested. Also adds Selection to interaction.rs so drag-to-select and copy read through the Phase 5 PageText API rather than re-deriving positions. Tests: new tests/phase4_exit_criterion.rs drives the exit criterion against the real two-page AcroForm fixture, not synthetic dictionaries. Click a link gives OpenUri; click a field, type, commit, and the value changes, the appearance regenerates with the new text and the draw list shows it. It also asserts an abandoned edit changes nothing, the checkbox uses the /On state declared in the file, and form fields route before annotations. Validation: TEST_TARGET=pdf-ui ./tools/test-rust-clean.sh 202 tests pass; rustfmt and clippy -D warnings clean. |
|||
| 66c7590d66 |
feat(pdf): start Phases 4 and 5, and make pdf-makepad compile
Some checks failed
Payment domain and storage / isolated-payment-tests (push) Has been cancelled
Phases 4 and 5 of REVIEWS/DART_PDF_VS_MAKEPAD_PDF_GAP_ANALYSIS.md. pdf-makepad had never compiled. It was missing eight PdfDevice methods, had a non-exhaustive RenderCommand match, called a function through the wrong path and held a borrow conflict. Everything previously claimed about the Makepad rendering path was therefore unverified. It now builds and is tested under a new pdf-ui target; the makepad build needs system GUI libraries, so that target checks for them with pkg-config and names the missing packages rather than failing in the linker. Phase 4 (new pdf-makepad/src/interaction.rs): - Viewport maps between PDF space (y up) and screen space (y down) in one place, so hit testing and rendering cannot disagree. Inverted /Rect corners are normalised. - Click routing follows the review order: form fields first, then annotation hit testing. A field overlapping a link takes the click. - Clicking a link emits PdfAction::OpenUri to the host. The viewer never opens a URL itself (rule 5). - Text editing is buffered: typing changes a working copy and only Enter, Tab or blur commits it, so Escape abandons an edit with the document untouched. Caret arithmetic is in characters, not bytes, so a non-ASCII value cannot panic. - Checkboxes toggle on click using the state the widget declares. - Hover reporting for cursor feedback; read-only fields are not targets. - render_fields() returns placement data so a focused field shows its uncommitted buffer while drawing stays trivial. Phase 5: - New pdf-graphics/src/cid.rs: composite font support. Codespace ranges give variable-width code decoding (the previous CMap was u8 to char, so every two-byte CID font decoded as garbage), plus cidchar/cidrange, ToUnicode bfchar/bfrange including array destinations and multi-character values such as ligatures, /W and /DW CID widths, and Identity-H/V. An unknown predefined CMap returns None instead of silently substituting Identity; unmapped codes decode to U+FFFD instead of vanishing. Width ranges are bounded so a malformed /W cannot exhaust memory. - New pdf-graphics/src/text.rs: the PageText extraction API. Segments carry origin, advance and font size; find() returns matches with rectangles; selection spans runs and works in either drag direction. Runs sharing a baseline stay on one line even with mixed font sizes. - renderer.rs: the two unexplained `fs * 0.75` constants are replaced by a named DEFAULT_EM_TO_CAP_RATIO used only as a fallback, with the real ascent preferred when the descriptor supplies one. Ts (text rise) was stored but never applied, so superscripts drew on the baseline. Also fixes clippy across pdf-makepad, which had never been linted. Validation: TEST_TARGET=pdf ./tools/test-rust-clean.sh (167 tests) TEST_TARGET=pdf-ui ./tools/test-rust-clean.sh (191 tests) Both rustfmt and clippy -D warnings clean. |
|||
| af4ef6f170 |
fix(pdf): restore a compiling, warning-free PDF baseline
Some checks failed
Payment domain and storage / isolated-payment-tests (push) Has been cancelled
Phase 0 of REVIEWS/DART_PDF_VS_MAKEPAD_PDF_GAP_ANALYSIS.md requires a clean baseline before any feature work. The four pdf crates did not compile at all, so every test claim about them was unverified. Compile fixes: - decode_lzw/decode_run_length returned Vec<u8> where callers expected PdfResult, so decode_stream_with_params did not type-check. - interpret_ops called a current_state_mut() method that PdfDevice does not have; colour-space tracking now goes through explicit device hooks. - image.rs used miniz_oxide without depending on it; PNG inflate now reuses the COS crate through a new pdf_cos::filter::inflate_zlib. Correctness fixes found while making the code build: - LZW and RunLength silently truncated malformed input and indexed unchecked; both now return typed errors (rule 6: no silent degradation). - Tw/Tc/Tz were parsed and thrown away, and the " operator dropped its word and character spacing, so every advance after them drifted. - TJ attached each kern to the preceding string instead of the following one and discarded a trailing kern entirely. - GlyphWidths::width() fell back to default_width for out-of-range codes; PDF 32000-1 9.6.2.1 requires /MissingWidth. - Text advances silently substituted a guessed font_size * 0.6 when no width table was present; ShowTextWithMetrics now carries advance_is_measured so callers can distinguish a measurement from an unknown. Tests: two tests had never compiled and were wrong once they ran (WinAnsi 0x99 is U+2122 not U+2019; q/cm/l/Q records four commands not three). The document test asserted only that the writer emits a %PDF header; replaced with real page-tree, out-of-range and malformed-input coverage. Adds a pdf target to tools/test-rust-clean.sh that tests the three UI-independent crates bottom-up under rustfmt and clippy -D warnings. Validation: TEST_TARGET=pdf ./tools/test-rust-clean.sh 72 tests pass; rustfmt and clippy -D warnings clean. |
|||
| 62dada264e | build: consume Makepad sibling APIs through widgets | |||
| e73def6d1b | build: align Makepad dependencies with Robrix fork | |||
| a2ea0ffc7c | updated map | |||
| cc05abdc71 | Initial commit |