You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Rust的lopdf库向现有PDF文件添加hello world文本

用lopdf给现有PDF添加文本的正确实现

你现有代码的问题是完全新建了页面树和页面对象,覆盖了原PDF的所有页面结构,导致原有内容丢失,新增的内容也因为页面属性匹配问题无法正常显示。正确的做法是直接在原有页面的基础上追加内容,不要修改原有的页面树结构。

依赖配置

Cargo.toml中添加:

lopdf = "0.31"

完整可运行代码

use lopdf::{Document, Object, Stream, dictionary, Content, Operation};

fn main() {
    // 加载原有PDF
    let bytes = include_bytes!("dummy.pdf");
    let mut doc = Document::load_mem(bytes).unwrap();

    // 先创建全局共用的字体对象
    let font_id = doc.add_object(dictionary! {
        "Type" => "Font",
        "Subtype" => "Type1",
        "BaseFont" => "Courier",
    });

    // 构造要追加的文本内容
    let add_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")]),
            Operation::new("ET", vec![]),
            Operation::new("BT", vec![]),
            Operation::new("Tf", vec!["F1".into(), 12.into()]),
            Operation::new("Td", vec![10.into(), 800.into()]),
            Operation::new(
                "Tj",
                vec![Object::string_literal("hello world.")],
            ),
            Operation::new("ET", vec![]),
            Operation::new("BT", vec![]),
            Operation::new("Tf", vec!["F1".into(), 10.into()]),
            Operation::new("Td", vec![10.into(), 780.into()]),
            Operation::new(
                "Tj",
                vec![Object::string_literal("to")],
            ),
            Operation::new("ET", vec![]),
        ],
    };
    let add_content_bytes = add_content.encode().unwrap();
    let add_content_id = doc.add_object(Stream::new(dictionary! {}, add_content_bytes));

    // 遍历所有原有页面,追加内容
    for (_, page_id) in doc.get_pages() {
        let page = doc.get_object_mut(page_id).unwrap().as_dict_mut().unwrap();

        // 给当前页面添加字体资源
        let resources = page.get_mut(b"Resources").and_then(|r| r.as_dict_mut()).ok();
        match resources {
            Some(res) => {
                let fonts = res.get_mut(b"Font").and_then(|f| f.as_dict_mut()).ok();
                match fonts {
                    Some(font_dict) => {
                        font_dict.set("F1", font_id);
                    }
                    None => {
                        res.set("Font", dictionary! {
                            "F1" => font_id
                        });
                    }
                }
            }
            None => {
                page.set("Resources", dictionary! {
                    "Font" => dictionary! {
                        "F1" => font_id
                    }
                });
            }
        }

        // 合并原有内容流和新增内容流
        let original_content = page.get(b"Contents").unwrap();
        let new_contents = if original_content.is_array() {
            let mut arr = original_content.as_array().unwrap().clone();
            arr.push(add_content_id.into());
            Object::Array(arr)
        } else {
            Object::Array(vec![original_content.clone(), add_content_id.into()])
        };
        page.set("Contents", new_contents);
    }

    // 直接保存即可,不需要修改原有Catalog和Root
    doc.save("test.pdf").unwrap();
}

注意事项

  • PDF的坐标原点在页面左下角,Y轴向上,你可以根据需要调整Td操作的坐标值修改文本位置
  • 如果要给不同页面加不同的文本,单独构造每个页面对应的内容流即可
  • 针对有加密的PDF需要先调用解密接口处理后再修改

内容的提问来源于stack exchange,提问作者Anon21

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 13:36:00