mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-09 04:22:20 +00:00
Repair a page's damaged THSarabunPSK map without grouped text
The repair reads the page's own fonts. It ran only after a group was expanded, so the same damaged map on text that was never grouped kept reading wrong. Preparation now repairs it whenever it finds it, recorded in the repair log as before, and PrepareText (text_prepare) succeeds when it repaired a map even with nothing to expand. A repaired map no longer matches the damage's fingerprint, so preparing again changes nothing.
This commit is contained in:
1 parent
90b88ddcb6
commit
cc9f7977bf
5 files changed
+42
-12
No files matched your search
@@ -229,8 +229,9 @@ summary it returns what this edit did:
|
||||
| `repaired_fonts` | integer | Page fonts whose damaged legacy THSarabunPSK Thai map was repaired on a page-owned copy. |
|
||||
| `refused_groups` | array of strings | One `name: reason` per group left as it was: unsafe compositing, tags, malformed content, or text grouped more than eight levels deep. |
|
||||
|
||||
It fails, changing nothing, when no group on the page can be prepared (the error gives the first
|
||||
reason) or the page is out of range.
|
||||
The page's own fonts with that damaged map are repaired too, whether their text was grouped or
|
||||
not. It fails, changing nothing, when the page has nothing to prepare or repair (the error gives
|
||||
the first reason a group was left as it was) or the page is out of range.
|
||||
|
||||
### Superscript and subscript normalization
|
||||
|
||||
|
||||
@@ -1591,7 +1591,7 @@ pub fn tools() -> Vec<ToolDef> {
|
||||
t("text_fonts", "List document fonts", "List validated embedded source faces and approved full preview faces for a page. Full registered resources and bundled choices can be selected by text_edit.font_resource. builtin:thai explicitly replaces a paragraph or range with the bundled Noto Sans Thai; a build without that face never lists it and text_edit refuses it there (a page whose fonts fill the listing budget may also omit it, but text_edit still accepts it). preview_only faces cannot be selected.")
|
||||
.ro().cmd("edit.edit_text")
|
||||
.with(schema(json!({"doc":doc(), "page":{"type":"integer","minimum":1}}), &["doc","page"])),
|
||||
t("text_prepare", "Prepare grouped text", "Make existing text inside supported Form XObjects directly editable on one page. Replaces placements with their original operators and resources, preserving position, clipping and other pages. Refuses unsafe transparency, tags or malformed content. Adds no text or cover boxes. Returns prepared_groups (placements made editable), repaired_fonts (damaged legacy Thai maps repaired) and refused_groups ('name: reason' for each group left as it was). Fails when nothing on the page can be prepared. Undoable; list text_paragraphs again afterwards.")
|
||||
t("text_prepare", "Prepare grouped text", "Make existing text inside supported Form XObjects directly editable on one page. Replaces placements with their original operators and resources, preserving position, clipping and other pages. Refuses unsafe transparency, tags or malformed content. Adds no text or cover boxes. Returns prepared_groups (placements made editable), repaired_fonts (damaged legacy Thai maps repaired) and refused_groups ('name: reason' for each group left as it was). Also repairs the page's own damaged legacy Thai maps, grouped or not. Fails when the page has nothing to prepare or repair. Undoable; list text_paragraphs again afterwards.")
|
||||
.cmd("edit.edit_text")
|
||||
.with(schema(json!({"doc":doc(), "page":{"type":"integer","minimum":1}}), &["doc","page"])),
|
||||
t("text_paragraphs", "List paragraphs", "The paragraphs on a page (adjacent source runs grouped into aligned visual rows, including mixed fonts, sizes and colours): number, text, editor_text (visual rows separated by newlines), rows and source line numbers, box (top-left-origin points), font and size. Use the number with text_edit's paragraph: until you list the page again, a number names the paragraph listed here, even after edits regroup the page.")
|
||||
|
||||
@@ -13023,6 +13023,30 @@ fn grouped_legacy_thai_map_repair_follows_every_block_of_the_damaged_map() {
|
||||
assert!(lines.iter().any(|l| l.text == "ฮา"), "{:?}", lines.iter().map(|l| &l.text).collect::<Vec<_>>());
|
||||
}
|
||||
|
||||
/// The damaged THSarabunPSK map of text that was never grouped is repaired by preparation too,
|
||||
/// recorded in the repair log; with nothing to prepare or repair, preparation changes nothing.
|
||||
#[test]
|
||||
fn legacy_thai_map_repair_needs_no_grouped_text() {
|
||||
let damaged = b"begincmap 1 begincodespacerange <0000> <ffff> endcodespacerange \
|
||||
1 beginbfrange <00ff><01ff><0032> endbfrange 17 beginbfrange \
|
||||
<00d9><00d9><0e04> <00e2><00e2><0e0d> <00fa><00fa><0e25> <0100><0100><0e2b> <0102><0102><0e2d> \
|
||||
<0164><0164><0e4c> <0166><0166><0e31> <0168><0168><0e47> \
|
||||
<00d6><00d6><000f> <00f0><00f0><0029> <00f8><00f8><0023> <0105><0105><003e> <0107><0107><0040> \
|
||||
<0109><0109><003e> <0111><0111><004e> <0160><0160><004e> <0161><0161><004f> endbfrange endcmap";
|
||||
// ฮา: codes 259 and 263, drawn by the page itself.
|
||||
let mut doc = page_with_forms("BT /F1 14 Tf 40 500 Td <01030107> Tj ET", &[]);
|
||||
let font = legacy_thai_font(&mut doc, "ABCDEF+THSarabunPSK", &legacy_thai_program(), damaged);
|
||||
doc.set(pdfcraft_cos::ObjRef::new(5, 0), Object::Dict(font));
|
||||
let result = text::forms::prepare(&mut doc, 0).unwrap();
|
||||
assert_eq!((result.expanded, result.repaired_fonts), (0, 1));
|
||||
assert!(text::text_lines(&doc, 0).unwrap().iter().any(|l| l.text == "ฮา"));
|
||||
assert!(doc.repair_log().iter().any(|l| l.contains("THSarabunPSK")), "{:?}", doc.repair_log());
|
||||
// Repaired once: the map no longer has the damage, and nothing else is to be done.
|
||||
let before = pdfcraft_cos::write_full(&doc, &Default::default()).unwrap();
|
||||
assert_eq!(text::forms::prepare(&mut doc, 0).unwrap().repaired_fonts, 0);
|
||||
assert_eq!(pdfcraft_cos::write_full(&doc, &Default::default()).unwrap(), before);
|
||||
}
|
||||
|
||||
/// A Thai run whose glyphs a subsetter split across two subsets of the face is kept in that
|
||||
/// face: each cluster comes from a subset that has it. A cluster no subset has is refused with
|
||||
/// the reason, and the document is unchanged.
|
||||
|
||||
@@ -33,7 +33,8 @@ struct Inlined {
|
||||
waiting: Vec<(usize, String)>,
|
||||
}
|
||||
|
||||
/// Prepare grouped source text atomically, within eight levels and 64 placements per page.
|
||||
/// Prepare grouped source text atomically, within eight levels and 64 placements per page, and
|
||||
/// repair the page fonts' damaged legacy Thai maps (`repair_thai_fonts`), grouped or not.
|
||||
/// Shared forms and other pages are untouched. Preparation is a distinct undoable engine edit.
|
||||
pub fn prepare(doc: &mut Document, page: usize) -> Result<Preparation, EditError> {
|
||||
let mut next = doc.clone();
|
||||
@@ -179,16 +180,20 @@ pub fn prepare(doc: &mut Document, page: usize) -> Result<Preparation, EditError
|
||||
}
|
||||
result.refused.sort();
|
||||
result.refused.dedup();
|
||||
if result.expanded > 0 {
|
||||
// The page's own damaged maps are repaired too, those of text that was never grouped
|
||||
// included: its text reads wrong all the same.
|
||||
result.repaired_fonts = repair_thai_fonts(&mut next, page)?;
|
||||
if result.expanded > 0 || result.repaired_fonts > 0 {
|
||||
for reason in &result.refused {
|
||||
next.note_repair(format!("Grouped text left intact: {reason}"));
|
||||
}
|
||||
result.repaired_fonts = repair_thai_fonts(&mut next, page)?;
|
||||
next.note_repair(format!(
|
||||
"Prepared {} grouped text placement(s) on page {} for direct source-text editing",
|
||||
result.expanded,
|
||||
page.saturating_add(1)
|
||||
));
|
||||
if result.expanded > 0 {
|
||||
next.note_repair(format!(
|
||||
"Prepared {} grouped text placement(s) on page {} for direct source-text editing",
|
||||
result.expanded,
|
||||
page.saturating_add(1)
|
||||
));
|
||||
}
|
||||
*doc = next;
|
||||
}
|
||||
Ok(result)
|
||||
|
||||
@@ -734,7 +734,7 @@ pub(crate) fn run_edit(doc: &mut pdfcraft_cos::Document, edit: &Edit, cx: &mut E
|
||||
}
|
||||
Edit::PrepareText { page } => {
|
||||
let result = pdfcraft_edit::text::forms::prepare(doc, *page)?;
|
||||
if result.expanded == 0 {
|
||||
if result.expanded == 0 && result.repaired_fonts == 0 {
|
||||
return Err(EditError::Invalid(
|
||||
result.refused.first().cloned().unwrap_or_else(|| "this page has no supported grouped text to prepare".into()),
|
||||
));
|
||||
|
||||
Reference in new issue
Block a user