Skip to main content

koprogo_api/domain/services/
pdf_writer.rs

1use printpdf::{
2    BuiltinFont, Mm, Op, PdfDocument, PdfFontHandle, PdfPage, PdfSaveOptions, Point, Pt, TextItem,
3};
4
5/// Adaptateur autour de l'API "Ops" de printpdf 0.11 (réécriture complète vs 0.7 —
6/// plus de `Layer`/`PdfLayerReference`, tout passe par une liste d'`Op` empilés sur
7/// `PdfPage`). Centralise le pattern "texte positionné en absolu" utilisé par les
8/// exporteurs PDF `domain/services/*_exporter.rs` pour éviter une réimplémentation
9/// par fichier. Migration liée à la fermeture de #658 (build cassé) + #636 (RUSTSEC
10/// lopdf) — cf. `docs/agent-activity/2026-07-26-sync-audit-feature-dev.md`.
11pub struct PdfPageBuilder {
12    ops: Vec<Op>,
13}
14
15impl Default for PdfPageBuilder {
16    fn default() -> Self {
17        Self::new()
18    }
19}
20
21impl PdfPageBuilder {
22    pub fn new() -> Self {
23        Self { ops: Vec::new() }
24    }
25
26    /// Équivalent de l'ancien `layer.use_text(text, size, Mm(x), Mm(y), &font)` (API 0.7).
27    pub fn text(
28        &mut self,
29        text: impl Into<String>,
30        size_pt: f32,
31        x_mm: f32,
32        y_mm: f32,
33        font: &PdfFontHandle,
34    ) {
35        self.ops.push(Op::StartTextSection);
36        self.ops.push(Op::SetFont {
37            font: font.clone(),
38            size: Pt(size_pt),
39        });
40        self.ops.push(Op::SetTextCursor {
41            pos: Point {
42                x: Mm(x_mm).into(),
43                y: Mm(y_mm).into(),
44            },
45        });
46        self.ops.push(Op::ShowText {
47            items: vec![TextItem::Text(text.into())],
48        });
49        self.ops.push(Op::EndTextSection);
50    }
51
52    pub fn into_page(self, width_mm: f32, height_mm: f32) -> PdfPage {
53        PdfPage::new(Mm(width_mm), Mm(height_mm), self.ops)
54    }
55}
56
57/// Police intégrée (Helvetica, Times, ...) — pas de résolution via `resources`
58/// nécessaire pour `PdfFontHandle::Builtin` (contrairement à une police externe).
59pub fn builtin_font(font: BuiltinFont) -> PdfFontHandle {
60    PdfFontHandle::Builtin(font)
61}
62
63pub fn new_document(title: &str) -> PdfDocument {
64    PdfDocument::new(title)
65}
66
67pub fn save_document(mut doc: PdfDocument, page: PdfPage) -> Vec<u8> {
68    doc.pages.push(page);
69    let mut warnings = Vec::new();
70    doc.save(&PdfSaveOptions::default(), &mut warnings)
71}