Pdftron - 创建与现有元素相同样式的新元素

2 投票
1 回答
1660 浏览
提问于 2025-04-18 00:26

我正在尝试使用PdfTron软件创建一个PDF编辑的原型。

我已经成功创建了一个界面,用户可以点击从PDF生成的图像,选择一个区域,然后会出现一个文本输入框,用户可以在这里输入文本,这些文本将替换PDF文件中的内容。

现在,替换文本的部分遇到了问题。因为Python没有API文档(只有一些示例),所以我正在参考Java / Android的API文档。

目前的进展是,我有以下代码来找出用户选择的矩形区域内的元素。x1y1x2y2是基于用户在前端选择的PDF坐标。

rect = Rect(x1, y1, x2, y2)
text = ''
extractor = TextExtractor()
extractor.Begin(page)
line = extractor.GetFirstLine()
words = []
while line.IsValid():
    word = line.GetFirstWord()
    while word.IsValid():
        elRect = word.GetBBox()
        elRect.Normalize()
        if elRect.IntersectRect(elRect, rect):
            text += ' ' + word.GetString()
            words.append(word)
        word = word.GetNextWord()
    line = line.GetNextLine()

words基本上是一个数组,我在里面存储需要替换的新元素的内容。

现在的问题是,我希望新元素的样式和字体与旧文本相同。API(链接)告诉我,使用

style = words[0].GetStyle()

可以获取单词的样式,而我可以通过样式来获取字体,使用

font = style.GetFont()

文档:https://www.pdftron.com/pdfnet/mobile/docs/Android/pdftron/PDF/TextExtractor.Style.html

但是返回的fontObj类,而不是Font类。

显然,创建新文本元素时需要Font类的对象。

因为

element = eb.CreateTextBegin(font, 10.0);

会产生一个错误:

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/home/alan/.virtualenvs/pdfprint/local/lib/python2.7/site-packages/PDFNetPython2.py", line 5056, in CreateTextBegin
    def CreateTextBegin(self, *args): return _PDFNetPython2.ElementBuilder_CreateTextBegin(self, *args)
NotImplementedError: Wrong number or type of arguments for overloaded function 'ElementBuilder_CreateTextBegin'.
  Possible C/C++ prototypes are:
    pdftron::PDF::ElementBuilder::CreateTextBegin(pdftron::PDF::Font,double)
    pdftron::PDF::ElementBuilder::CreateTextBegin()

也许有更好的方法可以实现相同的结果?

编辑1

在阅读文档时,我发现可以根据Object创建Font对象,例如:

font = Font(style.GetFont())

不过,我仍然在创建带有这些样式的元素时遇到困难。

/编辑1

编辑2

我使用以下代码测试写入文件:

style = elements[0].GetStyle()
font = Font(style.GetFont())
fontsize = style.GetFontSize()
eb = ElementBuilder()
element = eb.CreateTextBegin(font, 10.0)
writer.WriteElement(element)
element = eb.CreateTextRun('My Name')
element.SetTextMatrix(10, 0, 0, 10, 100, 100)
gstate = element.GetGState()
gstate.SetTextRenderMode(GState.e_fill_text)
gstate.SetStrokeColorSpace(ColorSpace.CreateDeviceRGB())
gstate.SetStrokeColor(ColorPt(1, 1, 1))
element.UpdateTextMetrics()
writer.WriteElement(element)
writer.WriteElement(eb.CreateTextEnd())
writer.End()
from core.helpers import ensure_dir
ensure_dir(output_filename)
doc.Save(output_filename, SDFDoc.e_linearized)
doc.Close()

我无法弄清楚的是:

  1. 如何复制现有元素的样式。
  2. 如何在文档中定位新元素。
  3. 为什么这段测试代码没有给我可见的结果。根据我的观察,虽然新文件被创建了,但里面没有任何“我的名字”。

/编辑2

1 个回答

2

根据上面的代码,看起来你想要根据页面上第一个单词的字体样式(字体名称和颜色)来在现有页面上添加一些文本。

不过,上面的代码有几个问题。你设置的是描边颜色,而不是填充颜色:

gstate.SetTextRenderMode(GState.e_fill_text)
gstate.SetStrokeColorSpace(ColorSpace.CreateDeviceRGB());
gstate.SetStrokeColor(ColorPt(1, 1, 1))

尝试一下

gstate.SetTextRenderMode(GState.e_fill_text)
gstate.SetFillColorSpace(ColorSpace.CreateDeviceRGB());
gstate.SetFillColor(ColorPt(1, 0, 0))  // hardcode to red … for testing purposes only

主要的问题可能和字体处理有关。你在使用一个已有的字体,但假设这个字体是用“标准编码”来处理的。然而,这个字体很可能并不是用标准编码的。此外,现有PDF中的字体通常是子集化的(这意味着字体并不包含完整的字符列表,而只是包含文档中存在的字符引用)。因此,你可能会看到notdef或空白,而不是你期待的文本。这个问题和其他一些问题可以在这里找到:

https://groups.google.com/d/msg/pdfnet-sdk/RBTuJG2uILk/pGkrKnqZ_YIJ https://groups.google.com/d/msg/pdfnet-sdk/2y8s5aehq-c/xyknr9W5r-cJ

作为解决方案,你可以不直接使用嵌入的字体,而是找到一个匹配的系统字体(比如根据字体名称和其他属性)并创建一个新字体。PDFNet提供了一个工具方法 Font.Create(doc, font) 或 Font.Create(doc, "字体名称")

这些方法会创建一个Unicode字体,所以你应该使用 eb.CreateUnicodeTextRun() 而不是 eb.CreateTextRun()。

另外,你也可以使用AcroForm作为模板(参见InteractiveForms示例),并使用 pdfdoc.FattenAnnotations() 来得到一个只读版本的文档。

撰写回答