mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-12 00:46:57 -06:00
fix(fonts): subset added TrueType fonts to used glyphs on document save
This commit is contained in:
@@ -4484,12 +4484,14 @@ impl Session {
|
||||
// file so that revision stays in the chain. A full rewrite (redaction, new encryption,
|
||||
// a reconstructed file) still writes the current graph on its own.
|
||||
let rewrite = editor.cos.full_save_required() || editor.cos.encryption_changed() || editor.cos.revisions().is_empty();
|
||||
let mut cos = editor.cos.clone();
|
||||
pdfcraft_edit::text::subset_document_fonts(&mut cos)?;
|
||||
guard(|| {
|
||||
if on_saved || rewrite {
|
||||
write_incremental(&editor.cos, &opts)
|
||||
write_incremental(&cos, &opts)
|
||||
} else {
|
||||
let mut base = pdfcraft_cos::Document::open_with_password(editor.saved.clone(), editor.saved_keys.reopen.as_deref())?;
|
||||
base.overlay_changes_from(&editor.cos)?;
|
||||
base.overlay_changes_from(&cos)?;
|
||||
write_incremental(&base, &opts)
|
||||
}
|
||||
})
|
||||
@@ -4506,7 +4508,9 @@ impl Session {
|
||||
}
|
||||
let editor = doc.editor.as_ref().ok_or_else(|| EditError::ReadOnly(doc.read_only_reason.clone().unwrap_or_default()))?;
|
||||
let opts = SaveOptions { mod_date: self.now().map(pdfcraft_cos::pdf_date), ..SaveOptions::default() };
|
||||
guard(|| write_full(&editor.cos, &opts)).map_err(EditError::Write)?.map(Arc::new).map_err(|e| EditError::Write(e.to_string()))
|
||||
let mut cos = editor.cos.clone();
|
||||
pdfcraft_edit::text::subset_document_fonts(&mut cos)?;
|
||||
guard(|| write_full(&cos, &opts)).map_err(EditError::Write)?.map(Arc::new).map_err(|e| EditError::Write(e.to_string()))
|
||||
}
|
||||
|
||||
/// Check that `bytes`, from [`Self::save_bytes`] or [`Self::save_full_bytes`], reopen as this
|
||||
|
||||
@@ -96,3 +96,50 @@ fn contextual_arabic_marks_ligatures_and_mixed_bidi_keep_logical_text_after_reop
|
||||
}
|
||||
exercise(font.bytes.to_vec(), "سَلَام 123", "NotoSansArabic-Regular");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn added_font_is_subset_to_used_glyphs_on_save() {
|
||||
let font_bytes = std::fs::read(std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../assets/fonts/Inter-Regular.ttf")).unwrap();
|
||||
let font_len = font_bytes.len();
|
||||
assert!(font_len > 300_000, "source font is full sized");
|
||||
|
||||
let mut s = Session::new();
|
||||
let id = s.open("source.pdf", None, document(), None).unwrap();
|
||||
s.apply(id, Edit::RegisterTextFont { page: 0, bytes: Arc::new(font_bytes) }).unwrap();
|
||||
let resource = s.get(id).unwrap().text_editor_fonts(0).into_iter().find(|f| f.face == "Inter-Regular").unwrap().resource;
|
||||
let style = BlockStyle { resource: Some(resource), width: Some(240.0), ..Default::default() };
|
||||
let text = "Three words here";
|
||||
s.apply(id, Edit::EditTextBlock { page: 0, block: 0, text: text.into(), style }).unwrap();
|
||||
|
||||
let saved = s.save_bytes(id).unwrap();
|
||||
assert!(saved.len() < 50_000, "saved PDF with 3 words must be subset and small, was {} bytes", saved.len());
|
||||
|
||||
let saved_doc = pdfcraft_cos::Document::open(saved.clone()).unwrap();
|
||||
let mut subset_found = false;
|
||||
for (_, obj) in saved_doc.scan_objects() {
|
||||
if let pdfcraft_cos::Object::Dict(d) = obj.as_ref()
|
||||
&& d.name(b"Type") == Some(b"Font")
|
||||
&& d.name(b"Subtype") == Some(b"Type0")
|
||||
&& let Some(base) = d.name(b"BaseFont")
|
||||
{
|
||||
let base_str = String::from_utf8_lossy(base);
|
||||
if base_str.ends_with("Inter-Regular") {
|
||||
assert!(base_str.len() >= 7 && base_str.as_bytes()[6] == b'+', "font base name {base_str} must have subset prefix");
|
||||
subset_found = true;
|
||||
}
|
||||
}
|
||||
if let pdfcraft_cos::Object::Stream(stream) = obj.as_ref()
|
||||
&& stream.dict.get(b"PCFullFont").is_some()
|
||||
{
|
||||
panic!("saved PDF must not retain PCFullFont flag");
|
||||
}
|
||||
}
|
||||
assert!(subset_found, "Type0 subset font must be present in saved document");
|
||||
|
||||
// Also verify reopening and rendering matches:
|
||||
let re = s.open("saved.pdf", None, saved, None).unwrap();
|
||||
assert_eq!(s.get(re).unwrap().text_blocks(0)[0].editor_text, text);
|
||||
let re_pixels = pixels(s.get(re).unwrap().display.clone());
|
||||
let orig_pixels = pixels(s.get(id).unwrap().display.clone());
|
||||
assert_eq!(re_pixels, orig_pixels, "reopened subset font renders identically to original");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user