mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 17:56:50 -06:00
PDF import: say when invisible text is left out (part of #2675)
Text in rendering mode 3 (invisible: a scanned page's searchable OCR layer) was dropped with no warning, so a scanned PDF opened and saved again lost its searchable text without a hint. The import now reports INVISIBLE_TEXT_NOTE when it leaves such text out, for type and outlines alike. Keeping the text needs a kind of type that is kept but not drawn, and the PDF writer (krilla, in craft-libs) to write it with `3 Tr`; its text API has fill, stroke and fill-and-stroke only.
This commit is contained in:
@@ -149,6 +149,10 @@ pub const PAST_PAGE_NOTE: &str = "art reaching past the page was clipped to it,
|
||||
|
||||
/// Warning: glyphs were kept as outlines rather than read as type.
|
||||
pub const OUTLINED_TEXT_NOTE: &str = "text was converted to outlines";
|
||||
/// Warning: text drawn invisibly (text rendering mode 3, as a scanned page's OCR layer is) was left
|
||||
/// out: VectorCraft has no type that is kept but not drawn, so a PDF saved from it loses that
|
||||
/// searchable text (#2675).
|
||||
pub const INVISIBLE_TEXT_NOTE: &str = "invisible text (such as a scanned page's searchable OCR layer) was left out";
|
||||
/// Warning: glyphs that differ from the installed font of their name were kept as outlines.
|
||||
pub const UNMATCHED_GLYPHS_NOTE: &str = "text whose glyphs differ from the installed font of the same name was kept as outlines";
|
||||
/// The end of the warning that glyphs whose characters the file doesn't name were kept as
|
||||
@@ -1963,7 +1967,10 @@ impl<'a> Device<'a> for Builder<'_> {
|
||||
) {
|
||||
self.pending = false;
|
||||
let stroke = match draw_mode {
|
||||
GlyphDrawMode::Invisible => return,
|
||||
GlyphDrawMode::Invisible => {
|
||||
self.warn(INVISIBLE_TEXT_NOTE);
|
||||
return;
|
||||
}
|
||||
GlyphDrawMode::Fill => None,
|
||||
GlyphDrawMode::Stroke(p) => Some(p.clone()),
|
||||
};
|
||||
|
||||
@@ -74,8 +74,8 @@ pub use encrypt::Encryption;
|
||||
pub use export::{export, export_with_report, page_areas};
|
||||
pub use file_info::{FileInfo, file_info};
|
||||
pub use import::{
|
||||
OFF_ARTBOARD_NOTE, OUTLINED_TEXT_NOTE, PAST_PAGE_NOTE, UNMATCHED_GLYPHS_NOTE, UNNAMED_CHARACTERS_NOTE, holds_outlines, import,
|
||||
import_with_report, is_glyph_outlines, is_outlines_note,
|
||||
INVISIBLE_TEXT_NOTE, OFF_ARTBOARD_NOTE, OUTLINED_TEXT_NOTE, PAST_PAGE_NOTE, UNMATCHED_GLYPHS_NOTE, UNNAMED_CHARACTERS_NOTE, holds_outlines,
|
||||
import, import_with_report, is_glyph_outlines, is_outlines_note,
|
||||
};
|
||||
pub use pages::{PageInfo, PdfInfo, has_illustrator_data, illustrator_data, info, is_postscript};
|
||||
pub use post::{THUMBNAIL_SIZE, Thumbnail};
|
||||
|
||||
@@ -177,3 +177,21 @@ pub(crate) fn transfer_contract() {
|
||||
fn soft_mask_inversion_uses_quantized_transfer_endpoints() {
|
||||
transfer_contract();
|
||||
}
|
||||
|
||||
/// #2675: text in rendering mode 3 (invisible, as a scanned page's OCR layer) isn't kept, and the
|
||||
/// import now says so, for type and outlines alike. Visible text beside it still comes in, and a
|
||||
/// page without invisible text gets no such warning.
|
||||
#[test]
|
||||
fn invisible_text_is_reported_when_left_out() {
|
||||
let open = |content: &str, text_as: TextAs| import_with_report(&text_pdf(content), &ImportOptions { text_as, ..Default::default() }).unwrap();
|
||||
let invisible = "BT /F1 20 Tf 3 Tr 1 0 0 1 10 40 Tm (AB) Tj 0 Tr 1 0 0 1 10 10 Tm (CA) Tj ET";
|
||||
for text_as in [TextAs::Text, TextAs::Outlines] {
|
||||
let r = open(invisible, text_as);
|
||||
assert!(r.warnings.iter().any(|w| w == crate::INVISIBLE_TEXT_NOTE), "{text_as:?}: {:?}", r.warnings);
|
||||
let mut kept = 0;
|
||||
r.document.walk(|n| kept += usize::from(matches!(n.kind, NodeKind::Text(_) | NodeKind::Path { .. } | NodeKind::Compound { .. })));
|
||||
assert!(kept >= 1, "{text_as:?}: the visible line still comes in");
|
||||
let visible = open("BT /F1 20 Tf 1 0 0 1 10 10 Tm (CA) Tj ET", text_as);
|
||||
assert!(!visible.warnings.iter().any(|w| w == crate::INVISIBLE_TEXT_NOTE), "{text_as:?}: {:?}", visible.warnings);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user