c# - 如何读取外观流的文本?

标签 c# pdf itext

我有一个 PDF,其中注释中显示的文本(在 Adob​​e Reader 中呈现)与其 /Contents/RC 条目给出的文本不同。这与我在这个问题中处理的问题有关:

Can't change /Contents of annotation

在这种情况下,我不想更改外观以匹配注释的内容,而是想做相反的事情:获取外观文本并更改 /Contents/RC 要匹配的值。例如,如果注释显示“外观”并且 /Contents 设置为“内容”,我想执行如下操作:

void setContent(PdfDictionary dict)
{
 PdfString str = dict.GetAsString(new PdfName("KeyForAppearanceText"));
 dict.Put(PdfName.CONTENTS,str);
}

但我找不到存储外观文本的位置。我用这段代码得到了 /AP 引用的字典:

private PdfDictionary getAPAnnot(PdfArray annotArray,PdfDictionary annot)
        {
            PdfDictionary apDict = annot.GetAsDict(PdfName.AP);
            if (apDict!=null)
            {
                PdfIndirectReference ap = (PdfIndirectReference)apDict.Get(PdfName.N);
                PdfDictionary apRefDict = (PdfDictionary)pdfController.pdfReader.GetPdfObject(ap.Number);
                return apRefDict;
            }
            else
            {
                return null;
            }
        }

这个字典有下面的hashMap:

{[/BBox, [-38.7578, -144.058, 62.0222, 1]]} 
{[/Filter, /FlateDecode]}   
{[/Length, 172]}    
{[/Matrix, [1, 0, 0, 1, 0, 0]]} 
{[/Resources, Dictionary]}

/Resources 间接引用了字体,但没有内容。所以外观流似乎不包含内容数据。

除了/Contents/RC,注释的数据结构中似乎没有任何地方存储内容数据。我应该在哪里寻找外观内容?

最佳答案

不幸的是,OP 没有提供示例 PDF。不过,考虑到他之前的问题,他很可能对自由文本注释感兴趣。因此,我使用 this example PDF这里作为例子。它有一页带有打字机自由文本注释,如下所示:

sampleTypewriter.pdf screenshot


OP 询问

Other than /Contents and /RC, there doesn't seem to be anywhere in the annotation's data structure that stores content data. Where should I be looking for the appearance contents?

OP 代码的主要缺点是他只考虑了正常外观作为 PdfDictionary:

PdfIndirectReference ap = (PdfIndirectReference)apDict.Get(PdfName.N);
PdfDictionary apRefDict = (PdfDictionary)pdfController.pdfReader.GetPdfObject(ap.Number);

它实际上是一个PdfStream,即一个带有数据流的字典,这个数据流就是外观绘制指令所在的位置。

但即使有了这个数据流,也没有OP想象的那么简单:

PdfString str = dict.GetAsString(new PdfName("KeyForAppearanceText"));

实际上外观流中的文本可以分段绘制,例如在我的示例文件中,流数据如下所示:

0 w
131.2646 564.8243 180.008 30.984 re
n
q
1 0 0 1 0 0 cm
131.2646 564.8243 180.008 30.984 re
W
n
0 g
1 w
BT
/Cour 12 Tf
0 g
131.265 587.96 Td
(This ) Tj
35.999 0 Td
(is ) Tj
21.6 0 Td
(written ) Tj
57.599 0 Td
(using ) Tj
43.2 0 Td
(the ) Tj
-158.398 -16.3 Td
(typewriter ) Tj
79.199 0 Td
(tool.) Tj
ET
Q

此外,编码不需要像此处那样是某种标准编码,而是可以为动态嵌入字体定义。

因此,必须应用完整的文本提取。

这一切都可以这样实现:

for (int page = 1; page <= pdfReader.NumberOfPages; page++)
{
    Console.Write("\nPage {0}\n", page);
    PdfDictionary pageDictionary = pdfReader.GetPageNRelease(page);
    PdfArray annotsArray = pageDictionary.GetAsArray(PdfName.ANNOTS);
    if (annotsArray == null || annotsArray.IsEmpty())
    {
        Console.Write("  No annotations.\n");
        continue;
    }
    foreach (PdfObject pdfObject in annotsArray)
    {
        PdfObject direct = PdfReader.GetPdfObject(pdfObject);
        if (direct.IsDictionary())
        {
            PdfDictionary annotDictionary = (PdfDictionary)direct;
            Console.Write("  SubType: {0}\n", annotDictionary.GetAsName(PdfName.SUBTYPE));
            PdfDictionary appearancesDictionary = annotDictionary.GetAsDict(PdfName.AP);
            if (appearancesDictionary == null)
            {
                Console.Write("    No appearances.\n");
                continue;
            }
            foreach (PdfName key in appearancesDictionary.Keys)
            {
                Console.Write("    Appearance: {0}\n", key);
                PdfStream value = appearancesDictionary.GetAsStream(key);
                if (value != null)
                {
                    String text = ExtractAnnotationText(value);
                    Console.Write("    Text:\n---\n{0}\n---\n", text);
                }
            }
        }
    }
}

用这个辅助方法

public String ExtractAnnotationText(PdfStream xObject)
{
    PdfDictionary resources = xObject.GetAsDict(PdfName.RESOURCES);
    ITextExtractionStrategy strategy = new LocationTextExtractionStrategy();

    PdfContentStreamProcessor processor = new PdfContentStreamProcessor(strategy);
    processor.ProcessContent(ContentByteUtils.GetContentBytesFromContentObject(xObject), resources);
    return strategy.GetResultantText();
}

对于上面的示例文件,代码的输出是

Page 1
  SubType: /FreeText
    Appearance: /N
    Text:
---
This is written using the 
typewriter tool.
---

请注意,有一些注释,特别是复选框和单选按钮的小部件注释,其结构比此处代码预期的要深一些。

关于c# - 如何读取外观流的文本?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37014984/

相关文章:

c# - 选择所有复选框 WPF

javascript - 如何使用 JavaScript 防止 Adob​​e Livecycle 中出现重复的验证消息

asp.net - 如何确定 iTextSharp Basefont 默认样式

c# - 扑克牌 : Should they be enum or struct or class?

c# - 安装的应用程序无法加载MySQL.Data.DLL?

html - 在 A4 页面上打印版本并另存为 PDF

C#:如何使用 iText 7 创建自定义大小的 Document 对象?

c# - iTextSharp Image.GetInstance 的 EMF 格式内存流

c# - 如何将我的异常信息放入 IEnumerable<string>

ruby-on-rails - 在 Rails 中验证 pdf 的数字签名时出现 Origami 和 OpenSSL 错误