lopdf icon indicating copy to clipboard operation
lopdf copied to clipboard

Added a text to a existing pdf... been trying for an hour.

Open ahtremblay opened this issue 3 years ago • 4 comments

How does one add a text to an existing pdf? The examples cover create a new pdf, or replacing the text of an existing pdf, but not to add to an existing pdf.

let bytes = include_bytes!("file.pdf");
let mut doc = Document::load_mem(bytes).unwrap();
for (page_number, page_id) in doc.get_pages() {
        let content = Content {
            operations: vec![
                Operation::new("BT", vec![]),
                Operation::new("Tf", vec!["F1".into(), 100.into()]),
                Operation::new("Td", vec![80.into(), 400.into()]),
                Operation::new("Tj", vec![Object::string_literal("Hello World")]),
                Operation::new("ET", vec![]),
            ],
        };
 doc.insert_form_object(page_id, barcode).unwrap();
}
doc.save("test.pdf").unwrap();

No sure if I am even close to achieving the goal or not with this. test.pdf is a save of the origin file, there is no hello world.

Would be nice to have a minimum example of added hello world to an existing pdf file.

ahtremblay avatar Nov 17 '21 04:11 ahtremblay

Getting closer...

few problems:

  1. Some pdfs crap out with F1 as the font, and others work (they show a square instead of the letters indicating unknown font). I assume it conflicts. So I need to loop in the dictionary is get the next unsafe font name... such as F1 or F2?
  2. the letters from a string_literral and print on top of each other. For instance the e is printed directly on top of the h.
    let bytes = include_bytes!("../dummy.pdf");
    let mut doc = Document::load_mem(bytes).unwrap();
    let pages_id = doc.new_object_id();

    let mut existing_page_id = vec![];
    for (_page_number, page_id) in doc.get_pages() {
        let page_id2 = doc.add_object(doc.get_object(page_id).unwrap().clone());
        existing_page_id.push(page_id2);
    }

    let font_id = doc.add_object(dictionary! {
        "Type" => "Font",
        "Subtype" => "Type1",
        "BaseFont" => "Courier",
    });
    let resources_id = doc.add_object(dictionary! {
        "Font" => dictionary! {
            "F1" => font_id,
        },
    });

    for page_id in &existing_page_id {
        let content = Content {
            operations: vec![
                Operation::new("BT", vec![]),
                Operation::new("Tf", vec!["F1".into(), 20.into()]),
                Operation::new("Td", vec![10.into(), 10.into()]),
                Operation::new("Tj", vec![Object::string_literal("he")]),
                Operation::new("ET", vec![]),
            ],
        };
        let text = xobject::form(
            vec![0.0, 0.0, 595.0, 842.0],
            vec![1.0, 0.0, 0.0, 1.0, 0.0, 0.0],
            content.encode().unwrap(),
        );
        doc.insert_form_object(*page_id, text).unwrap();
    }

    let p_o = existing_page_id.iter().next().unwrap();
    let p_oo = *p_o;

    let pages = dictionary! {
        "Type" => "Pages",
        "Kids" => vec![p_oo.into()],
        "Count" => 1,
        "Resources" => resources_id,
        "MediaBox" => vec![0.into(), 0.into(), 595.into(), 842.into()],
    };

    doc.objects.insert(pages_id, Object::Dictionary(pages));
    let catalog_id = doc.add_object(dictionary! {
        "Type" => "Catalog",
        "Pages" => pages_id,
    });
    doc.trailer.set("Root", catalog_id);
    doc.compress();

    doc.save("test.pdf").unwrap();

ahtremblay avatar Nov 17 '21 19:11 ahtremblay

GOT IT, after 72 hours I figured it out.

I used the export function of the preview tool in macOS to save the pdf as a png file, then I added the crate image-rs to my cargo.toml file. I then rendered images of text on the png. I now add the png to the pdf.

To add text to a pdf, I now create 700 micro-images on average on the filesystem, then recombine them into a final pdf, its beautiful.

ahtremblay avatar Nov 17 '21 22:11 ahtremblay

Getting closer...

few problems:

1. Some pdfs crap out with F1 as the font, and others work (they show a square instead of the letters indicating unknown font). I assume it conflicts. So I need to loop in the dictionary is get the next unsafe font name... such as F1 or F2?

2. the letters from a string_literral and print on top of each other. For instance the e is printed directly on top of the h.
    let bytes = include_bytes!("../dummy.pdf");
    let mut doc = Document::load_mem(bytes).unwrap();
    let pages_id = doc.new_object_id();

    let mut existing_page_id = vec![];
    for (_page_number, page_id) in doc.get_pages() {
        let page_id2 = doc.add_object(doc.get_object(page_id).unwrap().clone());
        existing_page_id.push(page_id2);
    }

    let font_id = doc.add_object(dictionary! {
        "Type" => "Font",
        "Subtype" => "Type1",
        "BaseFont" => "Courier",
    });
    let resources_id = doc.add_object(dictionary! {
        "Font" => dictionary! {
            "F1" => font_id,
        },
    });

    for page_id in &existing_page_id {
        let content = Content {
            operations: vec![
                Operation::new("BT", vec![]),
                Operation::new("Tf", vec!["F1".into(), 20.into()]),
                Operation::new("Td", vec![10.into(), 10.into()]),
                Operation::new("Tj", vec![Object::string_literal("he")]),
                Operation::new("ET", vec![]),
            ],
        };
        let text = xobject::form(
            vec![0.0, 0.0, 595.0, 842.0],
            vec![1.0, 0.0, 0.0, 1.0, 0.0, 0.0],
            content.encode().unwrap(),
        );
        doc.insert_form_object(*page_id, text).unwrap();
    }

    let p_o = existing_page_id.iter().next().unwrap();
    let p_oo = *p_o;

    let pages = dictionary! {
        "Type" => "Pages",
        "Kids" => vec![p_oo.into()],
        "Count" => 1,
        "Resources" => resources_id,
        "MediaBox" => vec![0.into(), 0.into(), 595.into(), 842.into()],
    };

    doc.objects.insert(pages_id, Object::Dictionary(pages));
    let catalog_id = doc.add_object(dictionary! {
        "Type" => "Catalog",
        "Pages" => pages_id,
    });
    doc.trailer.set("Root", catalog_id);
    doc.compress();

    doc.save("test.pdf").unwrap();

Operation::new("Tf", vec!["/F1".into(), 20.into()]), instead of Operation::new("Tf", vec!["F1".into(), 20.into()]), worked for me.

stanleydesu avatar Jan 27 '22 23:01 stanleydesu

Any updates? Still unclear how to add text to existing pdf template

LegendarySaiyan avatar Mar 02 '23 13:03 LegendarySaiyan