feat: PDF importieren und annotiert exportieren

- ☰ → „PDF importieren …“ oder PDF ins Fenster ziehen: neues Notizbuch
  mit einer Seite je PDF-Seite, die Originalseite als Hintergrund
- Querformat-Folien liegen oben auf dem A4-Blatt, darunter ist Platz für
  Notizen; gedrehte Seiten (/Rotate) erscheinen wie im PDF-Betrachter
- Anzeige mit hayro (reines Rust) im Hintergrund-Thread, nächste Seite
  wird vorab gerendert
- PDF-Export legt die Originalseiten als Vektorgrafik (Form-XObject via
  lopdf) unter Bilder und Tinte; nicht übernehmbare Seiten (z. B.
  verschlüsselt) werden als Bild mit 200 dpi eingebettet
- Dateiformat-Version 5 (pdf_background an der Seite)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-07 17:47:49 +02:00
co-authored by Claude Opus 5.5
parent 9a39276da4
commit 66679e51ae
14 changed files with 1771 additions and 147 deletions
+20 -6
View File
@@ -666,10 +666,16 @@ impl CanvasState {
pub fn sync_page(&mut self, page: &mut Page) {
page.rebuild_cache_if_dirty();
self.set_background(page.background);
// PDF-Seite zuerst, darüber die eingefügten Bilder
self.image_layer.set_quads(
page.strokes
page.pdf_background
.iter()
.filter_map(|s| Some((s.image.clone()?, s.image_corners()?)))
.map(|pdf| (pdf.key(), pdf.corners()))
.chain(
page.strokes
.iter()
.filter_map(|s| Some((s.image.clone()?, s.image_corners()?))),
)
.collect(),
);
@@ -698,10 +704,18 @@ impl CanvasState {
);
}
/// Lädt Bilder der aktuellen Seite, die noch nicht auf der GPU liegen. Vor jedem
/// Zeichnen aufrufen; ohne Änderungen kostet das nichts.
pub fn prepare_images(&mut self, assets: &crate::assets::AssetStore) {
self.image_layer.prepare(&self.device, &self.queue, assets);
/// Lädt Bilder und PDF-Seiten der aktuellen Seite, die noch nicht auf der GPU liegen.
/// Vor jedem Zeichnen aufrufen; ohne Änderungen kostet das fast nichts.
pub fn prepare_images(
&mut self,
load: &mut dyn FnMut(&str) -> crate::image_layer::TextureSource,
) {
self.image_layer.prepare(&self.device, &self.queue, load);
}
/// Wird noch eine PDF-Seite im Hintergrund gerendert?
pub fn is_waiting_for_images(&self) -> bool {
self.image_layer.is_waiting()
}
/// Startet einen neuen Live-Strich in DIN-A4-Seitenkoordinaten.