text_mirror/convert/
pdf.rs1use crate::segments::Segment;
11
12use super::anydoc_log::capture_anydoc;
13use super::{ConvertError, markdown_to_plain, normalize_text};
14
15pub struct PdfConversion {
17 pub text: String,
19 pub warnings: Vec<String>,
21 pub segments: Vec<Segment>,
23}
24
25fn error_code(error: &anydoc::ConvertError) -> &'static str {
29 if matches!(error, anydoc::ConvertError::Unsupported(_)) {
30 return "pdf_no_text_layer";
31 }
32 match error.code() {
33 "unsupported" => "unsupported",
34 "malformed" => "malformed",
35 "encrypted" => "encrypted",
36 "resourceLimit" => "resourceLimit",
37 "missingPart" => "missingPart",
38 _ => "io",
39 }
40}
41
42fn promote_warning(message: &str) -> String {
46 if message.contains("pages need OCR") {
47 let head = message.split(" and ").next().unwrap_or(message);
48 return format!("pdf_partial_text: {head}");
49 }
50 format!("anydoc_recovery: {message}")
51}
52
53pub fn convert_pdf(source: &[u8]) -> Result<PdfConversion, ConvertError> {
55 let (converted, captured) =
56 capture_anydoc(|| anydoc::to_markdown_bytes(source, anydoc::Format::Pdf));
57 let markdown = converted.map_err(|error| ConvertError {
58 code: error_code(&error),
59 message: error.to_string(),
60 })?;
61 let text = normalize_text(&markdown_to_plain(&markdown));
62 if !source.is_empty() && text.is_empty() {
63 return Err(ConvertError {
64 code: "empty_output",
65 message: format!(
66 "source is {} bytes but conversion produced no text",
67 source.len()
68 ),
69 });
70 }
71 let warnings = captured
72 .iter()
73 .map(|message| promote_warning(message))
74 .collect();
75 let segments = vec![Segment::span(0, text.len(), "document")];
76 Ok(PdfConversion {
77 text,
78 warnings,
79 segments,
80 })
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn partial_pdf_warnings_keep_their_prefix() {
89 assert_eq!(
90 promote_warning("1 of 2 pages need OCR and were not extracted"),
91 "pdf_partial_text: 1 of 2 pages need OCR"
92 );
93 assert_eq!(
94 promote_warning("recovered a broken xref"),
95 "anydoc_recovery: recovered a broken xref"
96 );
97 }
98}