mirror of
https://github.com/storytold/designcraft.git
synced 2026-11-12 05:12:39 +00:00
fix(render): index line style groups, stop futile glyph-cache eviction, read PDF page sizes once (#2217)
This commit is contained in:
1 parent
c4ed9b2210
commit
64c956f09e
4 files changed
+38
-13
No files matched your search
@@ -340,8 +340,9 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
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();
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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<Vec<(f64, f64)>> {
|
||||
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 {
|
||||
|
||||
+20
-10
@@ -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<u64>,
|
||||
}
|
||||
|
||||
/// 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<u32, usize> = 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));
|
||||
|
||||
Reference in new issue
Block a user