几年以来,我已经成功地从PDF中提取图像了。 我使用itextsharp来做到这一点。 我得到一个PdfImageObject并获得过滤器。 这个过滤器大多是“/ FlateDecode”。 在这种情况下,我使用pdf.PdfReader.FlateDecode(bytes,True)来解码原始字节。
但是最近我遇到了带有滤镜的PdfImageObjects的pdf:“[/ FlateDecode,/ RunLengthDecode]”。
所以我猜原始字节必须解码两次!?!?
我在互联网上找到了/ RunLengthDecode部分的一些代码: https://github.com/kusl/itextsharp/blob/master/tags/iTextSharp_5_4_5/src/core/iTextSharp/text/pdf/FilterHandlers.cs
我尝试在图像上应用两种解码选项。 First / FlateDecode然后/ RunLengthDecode。 第二个是/ RunLengthDecode,然后是/ FlateDecode。
但/ RunLengthDecode代码在两个场景中都给出了错误。
答案 0 :(得分:0)
这实际上不是问题的答案,而是对导致这个问题的问题的分析。
在对该问题的评论中,结果发现iText中的错误是OP尝试手动过滤原始流并提取图像的原因:某些图像是在小错误的情况下提取的。 OP将有问题的图像识别为具有过滤器[/FlateDecode, /RunLengthDecode]
的图像。
有问题的错误确实是iText对 RunLengthDecode 过滤器的实现,此处来自iText for .Net 5.5.x:
private class Filter_RUNLENGTHDECODE : IFilterHandler {
public byte[] Decode(byte[] b, PdfName filterName, PdfObject decodeParams, PdfDictionary streamDictionary) {
// allocate the output buffer
MemoryStream baos = new MemoryStream();
sbyte dupCount = -1;
for (int i = 0; i < b.Length; i++){
dupCount = (sbyte)b[i];
if (dupCount == -128) break; // this is implicit end of data
if (dupCount >= 0 && dupCount <= 127){
int bytesToCopy = dupCount+1;
baos.Write(b, i, bytesToCopy);
i+=bytesToCopy;
} else {
// make dupcount copies of the next byte
i++;
for (int j = 0; j < 1-(int)(dupCount);j++){
baos.WriteByte(b[i]);
}
}
}
return baos.ToArray();
}
}
更确切地说是这一行:
baos.Write(b, i, bytesToCopy);
它应该在 索引bytesToCopy
之后复制下一个i
字节 - 在索引i
处有计数值毕竟 - 但此命令从 索引bytesToCopy
开始复制下一个i
字节 。因此,对于每次运行的字节,一旦iText复制,首先复制计数字节,然后复制运行的最后一个字节。
相反,该行应为
baos.Write(b, i+1, bytesToCopy);
由于重复字节的运行被正确提取,即使对于长的,非重复的运行,也有许多正确的字节(在一个位置上),提取的图像iText只是看起来有点错误,例如:< / p>
图片损坏:
未损坏的图片:
这个bug多年来一直在iText 5.x for .Net中。此外,它已经存在于iText 5.x for Java中多年,并且仍然是,例如,这里来自当前的5.5.13-SNAPSHOT:
private static class Filter_RUNLENGTHDECODE implements FilterHandler{
public byte[] decode(byte[] b, PdfName filterName, PdfObject decodeParams, PdfDictionary streamDictionary) throws IOException {
// allocate the output buffer
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte dupCount = -1;
for(int i = 0; i < b.length; i++){
dupCount = b[i];
if (dupCount == -128) break; // this is implicit end of data
if (dupCount >= 0 && dupCount <= 127){
int bytesToCopy = dupCount+1;
baos.write(b, i, bytesToCopy);
i+=bytesToCopy;
} else {
// make dupcount copies of the next byte
i++;
for(int j = 0; j < 1-(int)(dupCount);j++){
baos.write(b[i]);
}
}
}
return baos.toByteArray();
}
}
并在iText 7中,例如这里来自Java的当前7.1.2-SNAPSHOT:
public class RunLengthDecodeFilter implements IFilterHandler {
@Override
public byte[] decode(byte[] b, PdfName filterName, PdfObject decodeParams, PdfDictionary streamDictionary) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte dupCount;
for (int i = 0; i < b.length; i++) {
dupCount = b[i];
if (dupCount == (byte) 0x80) { // this is implicit end of data
break;
}
if (dupCount >= 0) {
int bytesToCopy = dupCount + 1;
baos.write(b, i, bytesToCopy);
i += bytesToCopy;
} else { // make dupcount copies of the next byte
i++;
for (int j = 0; j < 1 - (int) (dupCount); j++) {
baos.write(b[i]);
}
}
}
return baos.toByteArray();
}
}
很可能这个错误可能会持续很长时间,因为 RunLengthDecode 过滤器几乎没有被使用多年。