diff --git a/crates/engine/src/cmd/book.rs b/crates/engine/src/cmd/book.rs index 18f554d57..6cd4a8b48 100644 --- a/crates/engine/src/cmd/book.rs +++ b/crates/engine/src/cmd/book.rs @@ -340,8 +340,9 @@ pub fn specs() -> Vec { let _fonts = super::file::load_document_fonts(&mut d, path).0; let cache = designcraft_compose::Cache::new(); let r = designcraft_pdf::export_pdf_with_report(&super::interchange::with_pdf_boxes(&d), &cache, &designcraft_pdf::PdfOptions::default()).map_err(|e| EngineError::Other(e.to_string()))?; - let n = designcraft_render::pdf_page_count(&r.bytes).unwrap_or(0); - let sizes: Vec<(f32, f32)> = (0..n).filter_map(|i| designcraft_render::pdf_page_size(&r.bytes, i)).map(|(w, h)| (w as f32, h as f32)).collect(); + // Every page's size from one opening of the chapter's PDF (not one per page). + let sizes: Vec<(f32, f32)> = + designcraft_render::pdf_page_sizes(&r.bytes).unwrap_or_default().into_iter().map(|(w, h)| (w as f32, h as f32)).collect(); parts.push((r.bytes, sizes)); } let pages: usize = parts.iter().map(|p| p.1.len()).sum(); diff --git a/crates/render/src/lib.rs b/crates/render/src/lib.rs index 8a23bb004..25207b248 100644 --- a/crates/render/src/lib.rs +++ b/crates/render/src/lib.rs @@ -50,7 +50,7 @@ pub use decode::{decode_pixmap, decode_pixmap_page, image_mime, image_size, natu use pdf_raster::PDF_RENDERS; pub use pdf_raster::{ PdfRasterError, check_pdf_input_size, crop_pixmap, is_pdf, pdf_crop_box, pdf_page_box, pdf_page_count, pdf_page_png, pdf_page_png_checked, - pdf_page_png_options_checked, pdf_page_size, render_pdf_page, shown_box, + pdf_page_png_options_checked, pdf_page_size, pdf_page_sizes, render_pdf_page, shown_box, }; pub(crate) use types::Frame; pub use types::{DisplayQuality, FrameStats, MAX_PAGE_PIXELS, MAX_SIDE, Placed, RenderOptions, Rendered, default_threads, effect_outset}; diff --git a/crates/render/src/pdf_raster.rs b/crates/render/src/pdf_raster.rs index 76160be96..bf1571a24 100644 --- a/crates/render/src/pdf_raster.rs +++ b/crates/render/src/pdf_raster.rs @@ -21,6 +21,20 @@ pub fn pdf_page_size(bytes: &[u8], page: usize) -> Option<(f64, f64)> { Some((w as f64, h as f64)) } +/// Sizes of every page of a PDF, as [`pdf_page_size`] gives each: the file is opened once. +pub fn pdf_page_sizes(bytes: &[u8]) -> Option> { + let pdf = craft_pdf::open_slice(bytes, None).ok()?; + Some( + pdf.pages() + .iter() + .map(|p| { + let (w, h) = p.render_dimensions(); + (w as f64, h as f64) + }) + .collect(), + ) +} + /// A placed page could not be rasterized completely for export. #[derive(Debug, thiserror::Error)] pub enum PdfRasterError { diff --git a/crates/render/src/text.rs b/crates/render/src/text.rs index 51271b577..52d49a505 100644 --- a/crates/render/src/text.rs +++ b/crates/render/src/text.rs @@ -91,6 +91,9 @@ pub(crate) struct GlyphCache { map: HashMap<(usize, ItemId), Entry>, elements: usize, clock: u64, + /// The clock at which eviction last found nothing it may drop (everything is in use by the + /// render then going on): until the next render, looking again would find the same. + spent: Option, } /// Roughly 56 bytes per element: ~110 MB of cached outlines. @@ -111,7 +114,7 @@ impl GlyphCache { let g = Arc::new(build(cs, ft)); self.elements += g.elements; self.map.insert(key, Entry { _story: Arc::downgrade(cs), glyphs: g.clone(), stamp }); - if self.elements > MAX_ELEMENTS { + if self.elements > MAX_ELEMENTS && self.spent != Some(self.clock) { self.evict(); } g @@ -123,12 +126,20 @@ impl GlyphCache { let mut stamps: Vec<(u64, usize, (usize, ItemId))> = self.map.iter().map(|(k, e)| (e.stamp, e.glyphs.elements, *k)).collect(); stamps.sort_unstable_by_key(|s| s.0); for (stamp, n, k) in stamps { - if self.elements <= MAX_ELEMENTS / 2 || stamp >= self.clock { - break; + if self.elements <= MAX_ELEMENTS / 2 { + return; + } + if stamp >= self.clock { + // The rest belong to this render: nothing more to drop until the next one. + self.spent = Some(self.clock); + return; } self.map.remove(&k); self.elements -= n; } + if self.elements > MAX_ELEMENTS / 2 { + self.spent = Some(self.clock); + } } pub fn clear(&mut self) { @@ -174,6 +185,8 @@ fn push_bar(bars: &mut Vec<(String, f32, Rect)>, color: &str, tint: f32, r: Rect fn build_line(db: &FontDb, cs: &ComposedStory, l: &Line, vertical: bool) -> LineGlyphs { let mut runs: Vec<(u32, BezPath)> = Vec::new(); + // Where each style's run is in `runs` (a line can hold many styles). + let mut run_of: HashMap = HashMap::new(); let mut underlines: Vec<(String, f32, Rect)> = Vec::new(); let mut strikes: Vec<(String, f32, Rect)> = Vec::new(); let mut glyphs = 0; @@ -207,13 +220,10 @@ fn build_line(db: &FontDb, cs: &ComposedStory, l: &Line, vertical: bool) -> Line // Upright in vertical text. a = turn * a; } - let k = match runs.iter().position(|r| r.0 == g.style) { - Some(k) => k, - None => { - runs.push((g.style, BezPath::new())); - runs.len() - 1 - } - }; + let k = *run_of.entry(g.style).or_insert_with(|| { + runs.push((g.style, BezPath::new())); + runs.len() - 1 + }); let bp = &mut runs[k].1; for el in outline.elements() { bp.push(transform_el(a, *el));