Skip to main content

zpdf_display_list/
lib.rs

1use zpdf_core::{Matrix, Point, Rect};
2
3#[derive(Debug, Clone)]
4pub struct DisplayList {
5    pub page_rect: Rect,
6    pub commands: Vec<RenderCommand>,
7}
8
9impl DisplayList {
10    pub fn new(page_rect: Rect) -> Self {
11        Self {
12            page_rect,
13            commands: Vec::new(),
14        }
15    }
16
17    pub fn push(&mut self, cmd: RenderCommand) {
18        self.commands.push(cmd);
19    }
20}
21
22#[derive(Debug, Clone)]
23pub enum RenderCommand {
24    FillPath {
25        path: Path,
26        rule: FillRule,
27        paint: Paint,
28        alpha: f32,
29    },
30    StrokePath {
31        path: Path,
32        style: StrokeStyle,
33        paint: Paint,
34        alpha: f32,
35    },
36    DrawGlyphRun(GlyphRun),
37    DrawImage(ImageDraw),
38    PushClip {
39        path: Path,
40        rule: FillRule,
41    },
42    /// Intersect the clip with a *stroked* path's outline (not its fill).
43    /// Used to clip a pattern/shading paint to a stroke, since stroke geometry
44    /// is the backend's job. Released by the matching [`RenderCommand::PopClip`].
45    PushClipStroke {
46        path: Path,
47        style: StrokeStyle,
48    },
49    PopClip,
50    PushBlendGroup {
51        blend_mode: BlendMode,
52        isolated: bool,
53        knockout: bool,
54        bounds: Rect,
55        /// Group constant alpha applied when compositing onto the backdrop
56        /// (the ExtGState /ca in effect when a transparency group is painted).
57        alpha: f32,
58        /// ExtGState /SMask soft mask modulating the group composite.
59        mask: Option<SoftMask>,
60    },
61    PopBlendGroup,
62}
63
64/// An ExtGState /SMask soft mask: the mask group's content pre-interpreted
65/// into page-space commands (geometry is fixed at `gs` time per PDF
66/// 11.6.5.2), rasterized by the backend at composite resolution.
67#[derive(Debug, Clone)]
68pub struct SoftMask {
69    pub kind: SoftMaskKind,
70    /// The /G transparency group's interpreted content. Font/image ids refer
71    /// to the same caches as the surrounding display list.
72    pub commands: std::sync::Arc<DisplayList>,
73    /// Page-space translation to apply to `commands` when rasterizing. Lets a
74    /// mask built once for a tiling-pattern cell be reused at every tile
75    /// position (the tile CTMs differ only by translation), instead of
76    /// re-interpreting the mask group per tile.
77    pub offset: (f32, f32),
78    /// /BC backdrop luminosity (0..1) for areas the group leaves unpainted.
79    /// Luminosity masks default to 0 (fully masked out).
80    pub backdrop_luma: f32,
81    /// /TR transfer function, pre-sampled over [0,1] into 256 steps.
82    pub transfer: Option<std::sync::Arc<[u8; 256]>>,
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
86pub enum SoftMaskKind {
87    /// Mask value = group luminosity over the /BC backdrop.
88    Luminosity,
89    /// Mask value = group alpha.
90    Alpha,
91}
92
93// -- Path --
94
95#[derive(Debug, Clone)]
96pub struct Path {
97    pub elements: Vec<PathElement>,
98}
99
100impl Path {
101    pub fn new() -> Self {
102        Self {
103            elements: Vec::new(),
104        }
105    }
106
107    pub fn move_to(&mut self, p: Point) {
108        self.elements.push(PathElement::MoveTo(p));
109    }
110
111    pub fn line_to(&mut self, p: Point) {
112        self.elements.push(PathElement::LineTo(p));
113    }
114
115    pub fn curve_to(&mut self, c1: Point, c2: Point, end: Point) {
116        self.elements.push(PathElement::CurveTo(c1, c2, end));
117    }
118
119    pub fn close(&mut self) {
120        self.elements.push(PathElement::Close);
121    }
122
123    pub fn rect(&mut self, r: Rect) {
124        self.move_to(Point::new(r.x0, r.y0));
125        self.line_to(Point::new(r.x1, r.y0));
126        self.line_to(Point::new(r.x1, r.y1));
127        self.line_to(Point::new(r.x0, r.y1));
128        self.close();
129    }
130
131    pub fn is_empty(&self) -> bool {
132        self.elements.is_empty()
133    }
134}
135
136impl Default for Path {
137    fn default() -> Self {
138        Self::new()
139    }
140}
141
142#[derive(Debug, Clone, Copy)]
143pub enum PathElement {
144    MoveTo(Point),
145    LineTo(Point),
146    CurveTo(Point, Point, Point),
147    Close,
148}
149
150#[derive(Debug, Clone, Copy, PartialEq, Eq)]
151pub enum FillRule {
152    NonZero,
153    EvenOdd,
154}
155
156// -- Stroke --
157
158#[derive(Debug, Clone)]
159pub struct StrokeStyle {
160    pub width: f32,
161    pub cap: LineCap,
162    pub join: LineJoin,
163    pub miter_limit: f32,
164    pub dash: Option<DashPattern>,
165}
166
167impl Default for StrokeStyle {
168    fn default() -> Self {
169        Self {
170            width: 1.0,
171            cap: LineCap::Butt,
172            join: LineJoin::Miter,
173            miter_limit: 10.0,
174            dash: None,
175        }
176    }
177}
178
179#[derive(Debug, Clone, Copy, PartialEq, Eq)]
180pub enum LineCap {
181    Butt,
182    Round,
183    Square,
184}
185
186#[derive(Debug, Clone, Copy, PartialEq, Eq)]
187pub enum LineJoin {
188    Miter,
189    Round,
190    Bevel,
191}
192
193#[derive(Debug, Clone)]
194pub struct DashPattern {
195    pub array: Vec<f32>,
196    pub phase: f32,
197}
198
199// -- Paint --
200
201#[derive(Debug, Clone, Copy)]
202pub struct Color {
203    pub r: f32,
204    pub g: f32,
205    pub b: f32,
206    pub a: f32,
207}
208
209impl Color {
210    pub fn rgb(r: f32, g: f32, b: f32) -> Self {
211        Self { r, g, b, a: 1.0 }
212    }
213
214    pub fn rgba(r: f32, g: f32, b: f32, a: f32) -> Self {
215        Self { r, g, b, a }
216    }
217
218    pub fn gray(v: f32) -> Self {
219        Self::rgb(v, v, v)
220    }
221
222    pub fn black() -> Self {
223        Self::gray(0.0)
224    }
225
226    pub fn white() -> Self {
227        Self::gray(1.0)
228    }
229}
230
231#[derive(Debug, Clone)]
232pub enum Paint {
233    Solid(Color),
234    Pattern(u32),
235    Shading(u32),
236}
237
238// -- Text --
239
240pub type FontId = u32;
241pub type ImageId = u32;
242
243#[derive(Debug, Clone)]
244pub struct GlyphRun {
245    pub font_id: FontId,
246    pub font_size: f32,
247    pub glyphs: Vec<PositionedGlyph>,
248    pub paint: Paint,
249    pub alpha: f32,
250    pub transform: Matrix,
251    /// Horizontal text-scaling factor (Tz/100). Scales the glyph *shape* x only;
252    /// per-glyph advances already include it. Almost always 1.0; negative values
253    /// (e.g. Tz -100) mirror glyphs horizontally — see `outline_to_pixel`.
254    pub h_scale: f32,
255}
256
257#[derive(Debug, Clone, Copy)]
258pub struct PositionedGlyph {
259    pub glyph_id: u16,
260    pub x: f32,
261    pub y: f32,
262    pub advance: f32,
263}
264
265// -- Image --
266
267#[derive(Debug, Clone)]
268pub struct ImageDraw {
269    pub image_id: ImageId,
270    pub transform: Matrix,
271    pub alpha: f32,
272}
273
274// -- Blend --
275
276#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
277pub enum BlendMode {
278    #[default]
279    Normal,
280    Multiply,
281    Screen,
282    Overlay,
283    Darken,
284    Lighten,
285    ColorDodge,
286    ColorBurn,
287    HardLight,
288    SoftLight,
289    Difference,
290    Exclusion,
291    Hue,
292    Saturation,
293    Color,
294    Luminosity,
295}