Skip to main content

pdf_interpret/
device.rs

1use crate::font::Glyph;
2use crate::soft_mask::SoftMask;
3use crate::{BlendMode, ClipPath, Image};
4use crate::{GlyphDrawMode, Paint, PathDrawMode};
5use kurbo::{Affine, BezPath, Rect, Shape};
6
7/// A trait for a device that can be used to process PDF drawing instructions.
8pub trait Device<'a> {
9    /// Set the properties for future stroking operations.
10    /// Set a soft mask to be used for future drawing instructions.
11    fn set_soft_mask(&mut self, mask: Option<SoftMask<'a>>);
12    /// Set the blend mode that should be used for rendering operations.
13    fn set_blend_mode(&mut self, blend_mode: BlendMode);
14    /// Draw a path.
15    fn draw_path(
16        &mut self,
17        path: &BezPath,
18        transform: Affine,
19        paint: &Paint<'a>,
20        draw_mode: &PathDrawMode,
21    );
22    /// Push a new clip path to the clip stack.
23    fn push_clip_path(&mut self, clip_path: &ClipPath);
24    /// Push a new transparency group to the blend stack.
25    fn push_transparency_group(
26        &mut self,
27        opacity: f32,
28        mask: Option<SoftMask<'a>>,
29        blend_mode: BlendMode,
30    );
31    /// Draw a glyph.
32    fn draw_glyph(
33        &mut self,
34        glyph: &Glyph<'a>,
35        transform: Affine,
36        glyph_transform: Affine,
37        paint: &Paint<'a>,
38        // TODO: Move this into outline glyph.
39        draw_mode: &GlyphDrawMode,
40    );
41    /// Draw an image.
42    fn draw_image(&mut self, image: Image<'a, '_>, transform: Affine);
43    /// Pop the last clip path from the clip stack.
44    fn pop_clip_path(&mut self);
45    /// Pop the last transparency group from the blend stack.
46    fn pop_transparency_group(&mut self);
47    /// Draw a rectangle directly, without going through the general path pipeline.
48    fn draw_rect(
49        &mut self,
50        rect: &Rect,
51        transform: Affine,
52        paint: &Paint<'a>,
53        draw_mode: &PathDrawMode,
54    ) {
55        self.draw_path(&rect.to_path(0.1), transform, paint, draw_mode);
56    }
57    /// Called at the beginning of a marked content sequence (BMC/BDC).
58    ///
59    /// The tag is the marked content tag (e.g. b"P", b"Span"). The mcid is
60    /// the marked content identifier from the properties dict, if present.
61    ///
62    /// `actual_text` is the sequence's `/ActualText` (PDF 32000-1 §14.9.4)
63    /// already decoded from its text-string encoding: the characters the
64    /// producer says this sequence *is*, whatever its glyphs decode to. A
65    /// text extractor must prefer it over the glyphs; a rendering device has
66    /// no use for it and the default implementation ignores it.
67    fn begin_marked_content(
68        &mut self,
69        _tag: &[u8],
70        _mcid: Option<i32>,
71        _actual_text: Option<&str>,
72    ) {
73    }
74    /// Called at the end of a marked content sequence (EMC).
75    fn end_marked_content(&mut self) {}
76    /// Text the page carries at a known place with no glyph drawn for it.
77    ///
78    /// `rect` is where that text sits, already in the device's own coordinate
79    /// system. The one producer today is a form field whose widget has no
80    /// appearance stream to draw: the value is in `/V` and nothing paints it,
81    /// so a device that reads glyphs would never see it (#549). A rendering
82    /// device has nothing to draw either -- synthesising the missing
83    /// appearance is a different job -- and the default implementation
84    /// ignores it.
85    fn draw_placed_text(&mut self, _text: &str, _rect: Rect) {}
86    /// Called when a TJ-array numeric adjustment is encountered between
87    /// substrings. Positive values shift text backward, negative values shift
88    /// it forward (1/1000 em units, per PDF §9.4.3). Text extractors use this
89    /// as a high-confidence word-boundary signal.
90    // ANN[r17/TEX1] Raw TJ offset surfaced so TextExtractionDevice can use it
91    // as the highest-confidence signal in the multi-signal space-detection
92    // consensus. Default is no-op to stay transparent to rendering devices.
93    fn text_adjustment(&mut self, _amount: f32) {}
94}
95
96/// A device that discards all drawing operations.
97pub struct DummyDevice;
98
99impl Device<'_> for DummyDevice {
100    fn set_soft_mask(&mut self, _: Option<SoftMask<'_>>) {}
101    fn set_blend_mode(&mut self, _: BlendMode) {}
102    fn draw_path(&mut self, _: &BezPath, _: Affine, _: &Paint<'_>, _: &PathDrawMode) {}
103    fn push_clip_path(&mut self, _: &ClipPath) {}
104    fn push_transparency_group(&mut self, _: f32, _: Option<SoftMask<'_>>, _: BlendMode) {}
105    fn draw_glyph(
106        &mut self,
107        _: &Glyph<'_>,
108        _: Affine,
109        _: Affine,
110        _: &Paint<'_>,
111        _: &GlyphDrawMode,
112    ) {
113    }
114    fn draw_image(&mut self, _: Image<'_, '_>, _: Affine) {}
115    fn pop_clip_path(&mut self) {}
116    fn pop_transparency_group(&mut self) {}
117}