Repository navigation
Conversation
6 of 7 tasks
Each slide was one raster image under an invisible text layer: large files (0.1 to 2 MB a slide at the default 200 dpi), blurry when zoomed or printed. Slides are now drawn by the same renderer through a vector canvas, so shapes, outlines (width, dashes, caps, joins, arrowheads), solid and linear/radial gradient fills, transparency, rotation, flips, groups, tables and charts become PDF paths and paints, and text is real text in the embedded font subsets (searchable, selectable, mapped back to Unicode cluster by cluster). JPEG and PNG pictures are embedded as they are (JPEG stays DCT), others as lossless pixels; crops and picture transparency are kept. Backgrounds (solid, gradient, picture) are vector too. What a PDF can't express is rasterized locally, at `dpi`, in place: a shape with an outer or inner shadow, glow, soft edges or reflection (its geometry and effects; its text stays vector) and pattern fills. Undecodable pictures (EMF/WMF, SVG) show the placeholder they show on screen. Glyphs from a variable font's non-default named instance are drawn as outlines over invisible real text, since the embedded subset is the default instance. The sample deck goes from 1.04 MB to 0.18 MB, a 21-slide public talk from 10.4 MB to 0.73 MB; `vector: false` (file.export, MCP export) keeps the raster mode, byte for byte. Links, bookmarks, notes pages, handouts and hidden-slide handling are unchanged. - render: `Canvas` trait (canvas.rs) for every drawing call; `vello_cpu`'s RenderContext implements it, so on-screen output is unchanged. `draw_slide` draws a slide on any canvas. Effects need the pixel context; vector canvases get those shapes through `rasterize`, a transparent pixmap covering the shape and its effects. Pictures hand the canvas the original file when it is an unchanged JPEG or PNG. - text: `TextLayout::texts`, each paragraph's characters as laid out (fields filled in), for glyph-to-text mapping. - pdf: krilla canvas (vector.rs) with font and image caches; `PdfOptions::vector` (default true); `dpi` now also sets the resolution of local rasters. - engine, mcp: `vector` export parameter. - tests: decks generated in code; paths and text without a page image, extractable text, a shadow rasterizes only its shape, pattern fills rasterize their area, JPEG embedded byte for byte, links and bookmarks in slides and handouts, size against the raster mode; a recording canvas checks what reaches a vector canvas; drawing on a pixel canvas matches render_slide.
- gaps.md: "PDF export rasterizes slide artwork" removed (top 14 now). - file-format-parity.md, parity-checklist.md (Export to PDF: D, summary regenerated), architecture.md, roadmap.md (share row and beta item 5 now printing only), ROADMAP.md log.
The vector PDF canvas was rebased on renderer code that draws some things only on the pixel context: pattern tiles, rectangle and outline path gradients, blended fill overlays, text shadows and glows, colour emoji (COLR glyphs) and WMF/EMF pictures. The rebase routes them through the per-element raster fallback (canvas::pixels, text effects and colour glyphs rasterized over their area) and draws gradient text glyph by glyph as vector paths; these tests pin it down. - render: a recording canvas checks each case: pixels over that element only, glyph runs kept for the text around effects and emoji, compound outlines as strokes, metafiles as decoded pixels. - pdf: a vector PDF with a pattern, a path gradient, shadowed text with an emoji and a WMF picture exports without panicking, with local images and extractable text. - file-format-parity.md (PDF row) and architecture.md list what rasterizes.
…nd vertical text The renderer now lights 3-D bevels and faces, casts a group's shadow from its children, tiles pictures with flips, alignment and their recorded resolution, draws circle gradients from an off-centre focus to the circle around their tile, and stands East Asian vertical text upright. On a vector canvas: bevels, lit faces and group shadows are local rasters over their area (never dropped); tiles are one embedded picture per tile, the mirrored ones flipped; circle gradients are two-circle PDF gradients; upright glyphs are outlines over the same text drawn invisibly (still searchable and copyable); children filled with the group fill keep it laid out across the group inside a local raster. - render: tests_vector.rs replays the vector route into pixels and compares it with the pixel route on those cases (off-centre, tiled and rotWithShape=0 gradients, flipped and aligned tiles, bevel and lit face, group shadow, grpFill under a shadow, gradient lines and heads), and counts the local rasters. - pdf: upright vertical text exports as invisible text under outlines; horizontal text stays plain text.
anishguduru
pushed a commit
to anishguduru/deckcraft
that referenced
this pull request
Oct 16, 2026
aiongg
added a commit
to aiongg/deckcraft
that referenced
this pull request
Oct 23, 2026
aiongg
added a commit
that referenced
this pull request
Oct 23, 2026
* Media: use craft-media for bitstream, isobmff and matroska
deckcraft-bitstream, deckcraft-isobmff and deckcraft-matroska re-export
craft_media::{bitstream, isobmff, matroska} from craft-libs. The packages
and their public paths stay, so the codec crates build unchanged.
deckcraft-bitstream keeps the decoder memory budget as its own module
(budget). deckcraft-media names the new CodecConfig::Apv entry.
* Media: pin craft-media to storytold/craft-libs at f97c84d2 (#245)
* Media: pin craft-media to storytold/craft-libs at f0ad6c1e (#246)
|
Thanks for this. Exporting vector slides with real text, instead of one raster image per slide, still looks valuable for file size and sharpness. I checked just now: the PR is open, GitHub reports it conflicts with |
Contributor
Author
|
Thanks for picking this up. I don't plan to bring it up to date myself, so please go ahead and open the new PR with my commits and the credit. Feel free to close this one when yours is up. |
This was referenced Oct 27, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
docs/gaps.mdlists "PDF export rasterizes slide artwork" as a beta blocker: each slide is oneraster image at print resolution under an invisible text layer. Files are large (0.1 to 2 MB a
slide at the default 200 dpi) and blurry when zoomed or printed.
What changes
Four commits, rebased on
mainat 9859da3 (after the shared PDF writer, #708): the export, theprogress docs, tests for the drawing
maingained since this PR was opened (pattern fills,rectangle and shape path gradients, fill overlays, compound outlines, text shadows and glows,
gradient text, colour emoji, WMF/EMF pictures, WordArt warps), and tests for the newer bevels,
lit faces, group shadows, circle gradients, picture tiles and upright vertical text.
Slides are drawn by the same renderer through a vector canvas, so shapes, outlines (width, dashes,
caps, joins, arrowheads), solid and linear/radial gradient fills, transparency, rotation, flips,
groups, tables and charts become PDF paths and paints, and text is real text in the embedded font
subsets (searchable, selectable, mapped back to Unicode cluster by cluster). JPEG and PNG pictures
are embedded as they are (JPEG stays DCT), others as lossless pixels; crops and picture
transparency are kept. Backgrounds (solid, gradient, picture) are vector too.
What a PDF can't express is rasterized locally, at
dpi, in place, never dropped: a shape with anouter or inner shadow, glow, soft edges, reflection or a fill overlay (its geometry and effects;
its text stays vector), pattern fills, rectangle and shape path gradients, text shadows and glows
(over the glyphs' area; the text itself stays real text), colour emoji (over the glyph's em box;
the letters around stay text) and WMF/EMF pictures (decoded to pixels, as on screen, at print
resolution). Gradient text is drawn glyph by glyph as vector paths; compound outlines, arrowheads
and tables with file-defined styles are vector. Pictures the renderer can't decode show the
placeholder they show on screen. Glyphs from a variable font's non-default named instance are
drawn as outlines over invisible real text, since the embedded subset is the default instance.
vector: false(file.export, MCPexport) keeps the raster mode, byte for byte. Links,bookmarks, notes pages, handouts and hidden-slide handling are unchanged.
By crate:
canvas.rs,lib.rs,paint.rs,table.rs,chart.rs,warp.rs,images.rs,colr.rs): aCanvastrait for every drawing call;vello_cpu'sRenderContextimplements it, so on-screen output is unchanged.
draw_slidedraws a slide on any canvas(shapes, backgrounds, tables, charts, text, warps).
canvas::pixelsruns drawing that onlyexists in pixels directly on a pixel canvas and rasterizes it locally on a vector one. Shapes
with effects or a fill overlay are rasterized whole over the area
extent::spillcomputes,with the view and a
useBgFillbackground shifted so strokes stay aligned.paint::gradientreturns a
Paint(gradient text no longer needs a paint transform, which vector canvaseslack);
colr::has_colortells colour glyphs apart. Pictures hand the canvas the original filewhen it is an unchanged JPEG or PNG.
TextLayout::texts, each paragraph's characters as laid out (fields filled in), forglyph-to-text mapping.
vector.rs, oncraft_pdf::write::backend) with font and imagecaches;
PdfOptions::vector(default true);dpinow also sets the resolution of localrasters. It keeps Use the shared PDF writer and report font embedding failures #708's contract: the export runs under
write::guard, fonts go throughwrite::font, and a font that can't be embedded fails the export with its error on vectorslides too (never dropped or substituted text). New dependency on
deckcraft-color(workspacecrate, same layer rules);
cargo xtask pdf-depspasses.mainsince: chartex charts (waterfall, funnel, treemap, sunburst,box), radar and stock marks, chart trendlines, 3-D extrusion sides and contours take
&mut dyn Canvasand stay vector; bevels and lit faces, path gradients and patterns go throughcanvas::pixels; the table painter's off-screen text skip uses the slide box on a vectorcanvas; a cropped picture is decoded for its whole size (Boolean intersect blurs raster image output #762) on both routes.
vectorexport parameter.docs/gaps.md(top 14 now); "Export to PDF" goes from P to D in
docs/parity-checklist.md(summary regeneratedby
cargo xtask parity);docs/file-format-parity.md,docs/architecture.md,docs/roadmap.md(share row and beta item 5 now cover printing only) and theROADMAP.mdprogress log are updated.
How it was tested
cargo xtask cipasses on this branch, rebased onmainat 9859da3 (all 7 steps: fmt, pdf-deps,clippy
-D warnings, 1,690 tests passed and 6 ignored, assets, layers, wasm32 check). On Windowsthe
deckcraft-ui-eguilib tests sometimes crash with an access violation when run in parallel,on
mainas well; the run above usedRUST_TEST_THREADS=1.New tests, all on decks generated in code:
crates/pdf:vector_slides_are_paths_and_text_without_a_page_image,vector_text_is_extractable,vector_pdf_is_much_smaller_than_raster,a_shadow_rasterizes_that_shape_only,pattern_fills_rasterize_their_area_only,jpeg_pictures_are_embedded_as_they_are(byte for byte),links_and_bookmarks_survive_vector_export(slides and handouts).
crates/render:draw_slide_on_a_pixel_canvas_matches_render_slide(zero pixel difference), andvector_canvases_get_paths_and_text_and_effects_as_local_pixels, with a recording canvas thatchecks what reaches a vector canvas. Third commit, same recording canvas:
vector_canvases_get_pixel_only_fills_as_local_pixels(pattern, rectangle path gradient, filloverlay over that element only; a triple compound outline as strokes),
vector_canvases_keep_text_and_rasterize_its_effects(text shadow and glow as pixels, glyphruns kept; gradient text as paths),
vector_canvases_get_metafile_pictures_as_pixels(a WMFbuilt in the test),
vector_canvases_get_colour_emoji_as_pixels(when a colour font isinstalled).
crates/pdf(third commit):pixel_only_drawing_exports_as_local_images, a vector PDF with apattern, a path gradient, shadowed text with an emoji and a WMF picture: no panic, local images
only, extractable text.
Checked by hand with the built CLI on this branch: the sample deck exports to 0.14 MB vector
(1.01 MB with
vector: false); its 8 pages render in poppler andpdftotextgives the slide text.A deck generated with
deckcraft-cli run(gradient, shadow and glow, pattern, bevel, chart,table) gives 2 pages, extractable text including the chart labels, and 3 local images (the
shadowed shape, the pattern, the bevel). Earlier, on an older
main: a 21-slide public deck from10.4 MB to 0.73 MB.
crates/pdf(Use the shared PDF writer and report font embedding failures #708 contract):unembeddable_font_returns_error_instead_of_a_pdf_missing_textnow covers vector and raster slides;
text_layer_links_and_bookmarks_survive_serializationcovers both modes.
crates/render/src/tests_vector.rs(fourth commit): replays the vector route into pixels andcompares it with the pixel route (off-centre, tiled and rotWithShape=0 gradients, flipped and
aligned tiles, bevel and lit face, group shadow, grpFill under a shadow, gradient lines and
heads), and counts the local rasters.
Known limits
gradients, text shadows and glows, colour emoji and WMF/EMF pictures are still pixels (per
element, at
dpi).docs/roadmap.mdnow covers only that.Related PRs
errors;
vector: falseis Use the shared PDF writer and report font embedding failures #708's export unchanged.git merge-treeagainstmainat 9859da3 is clean; the overlaps listed before this rebasewere against an older
main.&mut RenderContext.Whichever lands second converts it to
&mut dyn Canvas, as this PR does for the currentpainter and the chartex and radar painters.