我可以通过多种方式从PDF中的页面中提取文本:
String pageText = PdfTextExtractor.GetTextFromPage(reader, i);
这可用于获取页面上的任何文字。
可替换地:
byte[] contentBytes = iTextSharp.text.pdf.parser.ContentByteUtils.GetContentBytesForPage(reader, i);
可能性是无穷无尽的。
现在,我想删除/编辑某个字词,例如明确的单词,敏感的信息(在他们身上放置黑盒子显然是一个坏主意:)或者PDF中的任何内容(这只是简单的文本)。我可以使用上面的方法找到这个词。我可以算一下它的出现等等......
我不关心布局,或者PDF并不是真正意图以这种方式操纵。
我只想知道是否有一种机制可以让我以这种方式操纵PDF的原始内容。你可以说我正在寻找“SetContentBytesForPage()”......
答案 0 :(得分:4)
如果要更改页面内容,则仅更改页面的内容流是不够的。页面可能包含对包含要删除的内容的表单XObject的引用。
次要问题包括图像。例如:假设您的文档包含已经过OCR的扫描文档。在这种情况下,仅删除(矢量)文本是不够的,您还需要操纵图像中的(像素)文本。
假设您的次要问题不存在,您需要采用双重方法:
从你的问题,我认为你已经解决了问题1.解决问题2并不是那么微不足道。在我的书的第15章中,我有一个示例,其中提取文本返回“Hello World”,但当您查看内容流时,您会看到:
BT
/F1 12 Tf
88.66 367 Td
(ld) Tj
-22 0 Td
(Wor) Tj
-15.33 0 Td
(llo) Tj
-15.33 0 Td
(He) Tj
ET
在您从此流代码段中删除“Hello World”之前,您需要一些启发式方法,以便您的程序能够识别此语法中的文本。
找到文本后,您需要重写该流。如需灵感,您可以查看itext-xtra包中的OCG remover functionality。
长话短说:如果您的PDF相对简单,那就是:可以在不同的内容流(页面内容和Form XObject内容)中轻松检测到文本,然后只需要在一些字符串操作后重写这些流
我已经为您创建了一个名为ReplaceStream
的简单示例,它将"Hello World"
替换为PDF中的"HELLO WORLD"
。
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
PdfDictionary dict = reader.getPageN(1);
PdfObject object = dict.getDirectObject(PdfName.CONTENTS);
if (object instanceof PRStream) {
PRStream stream = (PRStream)object;
byte[] data = PdfReader.getStreamBytes(stream);
stream.setData(new String(data).replace("Hello World", "HELLO WORLD").getBytes());
}
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
stamper.close();
reader.close();
}
一些警告:
object
是否为流。它也可以是流的数组。在这种情况下,您需要遍历该数组。Hello World
。在现实生活中,PDF文件从未如此简单,并且随着文档中使用的每个特殊功能,项目的复杂性将大大增加。
答案 1 :(得分:3)
Bruno代码的C#等价物:
static void manipulatePdf(String src, String dest)
{
PdfReader reader = new PdfReader(src);
PdfDictionary dict = reader.GetPageN(1);
PdfObject pdfObject = dict.GetDirectObject(PdfName.CONTENTS);
if (pdfObject.IsStream()) {
PRStream stream = (PRStream)pdfObject;
byte[] data = PdfReader.GetStreamBytes(stream);
stream.SetData(System.Text.Encoding.ASCII.GetBytes(System.Text.Encoding.ASCII.GetString(data).Replace("Hello World", "HELLO WORLD")));
}
FileStream outStream = new FileStream(dest, FileMode.Create);
PdfStamper stamper = new PdfStamper(reader, outStream);
reader.Close();
}
如果结果仍然包含错误,我会更新。
答案 2 :(得分:0)
在我之前的C#代码和Bruno的注释中,GetDirectObject(PdfName.CONTENTS)也可能返回一个数组而不是一个数据流:在我的特殊情况下,结果证明这是真的。
对于IsArray(),返回的PdfObject返回“true”。我检查过,数组元素都是PdfIndirectReference。
进一步了解API会产生两个有用的信息:
从那里开始,您将获得一个新的PdfObject,您可以使用IsStream()进行检查,并根据之前发布的代码进行修改。
所以它适用于此(请注意,这很快,很脏,但它适用于我的特定用途......):
// Get the contents of my page...
PdfObject pdfObject = pageDict.GetDirectObject(PdfName.CONTENTS);
// Check that this is, in fact, an array or something else...
if (pdfObject.IsArray())
{
PdfArray streamArray = pageDict.GetAsArray(PdfName.CONTENTS);
for (int j = 0; j < streamArray.Size; j++)
{
PdfIndirectReference arrayEl = (PdfIndirectReference)streamArray[j];
PdfObject refdObj = reader.GetPdfObject(arrayEl.Number);
if (refdObj.IsStream())
{
PRStream stream = (PRStream)refdObj;
byte[] data = PdfReader.GetStreamBytes(stream);
stream.SetData(System.Text.Encoding.ASCII.GetBytes(System.Text.Encoding.ASCII.GetString(data).Replace(targetedText, newText)));
}
}
}