feat: Endlose Fläche als Seitenanordnung (Issue #30, Schritt 2)

Ein endloses Notizbuch hat eine einzige Seite von 10 × 10 m; Eingabe,
Treffertests, Undo und Speichern bleiben dadurch unverändert. Die
Ansicht zeigt bei 100 % einen DIN-A4-Rahmen in der Mitte, Bilder und
Textfelder richten sich nach A4. Wählbar beim Anlegen und im ☰-Menü,
umschalten nur bei einer Seite ohne PDF bzw. zurück, solange alles im
Startausschnitt liegt. Seiten-Knöpfe sind auf der endlosen Fläche aus.

Der Export enthält nur den beschriebenen Bereich mit 10 mm Rand, auf
das Musterraster gelegt; Bilder einer Auswahl rastern nur noch den
Ausschnitt statt der ganzen Seite. Formatversion 9.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
haraldandClaude Opus 5.5 committed 2026-10-18 22:14:45 +02:00
1 parent 8bc29faddc
commit 3c793fe659
11 files changed
+461 -66

No files matched your search

+65
View File
@@ -191,6 +191,28 @@ pub fn export_notebook_to_bytes(
assets: Option<&AssetStore>,
pattern: PatternColor,
) -> Result<Vec<u8>, Box<dyn Error>> {
// Endlose Seite: nur der beschriebene Ausschnitt wird zur PDF-Seite
if notebook.pages.iter().any(|p| p.is_endless()) {
let pages = notebook
.pages
.iter()
.map(|p| {
if p.is_endless() {
let (origin, size) = p.endless_export_bounds();
p.cropped(origin, size)
} else {
p.clone()
}
})
.collect();
let cropped = Notebook::from_pages(
pages,
notebook.current_page_index,
notebook.next_stroke_id,
notebook.next_page_id,
);
return export_notebook_to_bytes(&cropped, strategy, assets, pattern);
}
let doc = generate_pdf_document(notebook, strategy, assets, pattern)?;
let bytes = add_alpha_states(fix_text_encoding(doc.save_to_bytes()?, notebook)?, notebook)?;
@@ -1772,6 +1794,49 @@ mod tests {
.is_ok());
}
#[test]
fn test_endless_page_exports_only_the_written_area() {
let mut notebook = Notebook::new();
assert!(notebook.make_endless());
let sample = |x: f32, y: f32| StylusSample {
position: glam::Vec2::new(x, y),
pressure: 0.5,
timestamp: 0.0,
};
let page = &mut notebook.pages[0];
page.add_stroke(Stroke::new(
1,
vec![sample(20_000.0, 30_000.0), sample(23_000.0, 31_000.0)],
vec![
InkVertex::new(20_000.0, 30_000.0, [0.0, 0.0, 0.0, 1.0]),
InkVertex::new(23_000.0, 31_000.0, [0.0, 0.0, 0.0, 1.0]),
InkVertex::new(23_000.0, 31_010.0, [0.0, 0.0, 0.0, 1.0]),
],
[0.0, 0.0, 0.0, 1.0],
));
let (_, size) = notebook.pages[0].endless_export_bounds();
let bytes = export_notebook_to_bytes(
&notebook,
ExportStrategy::FilledPolygon,
None,
PatternColor::default(),
)
.unwrap();
let sizes = crate::pdf_doc::page_sizes(bytes).unwrap();
assert_eq!(sizes.len(), 1);
// Seiteneinheiten (10 = 1 mm) in Punkt
let pt = |units: f32| units / 10.0 / 25.4 * 72.0;
assert!(
(sizes[0].0 - pt(size.x)).abs() < 1.0,
"{sizes:?} statt {size:?}"
);
assert!(
(sizes[0].1 - pt(size.y)).abs() < 1.0,
"{sizes:?} statt {size:?}"
);
assert!(sizes[0].0 < pt(4_000.0), "nicht die ganze endlose Seite");
}
#[test]
fn test_page_titles_become_pdf_bookmarks() {
let mut notebook = Notebook::new();