C# 使用iText 7,什么';导出平面编码图像的正确方法是什么?
我正在尝试创建代码,以便使用iText版本7.19导出PDF中的图像。我对平面编码图像有一些问题。我正在使用的Microsoft free book中的所有Flate编码图像作为示例(请参阅),它们总是显示为粉红色,这取决于我如何尝试复制它们可能出现扭曲的字节 如果我试图一次复制所有图像字节(请参阅下面代码中的SaveFlateEncodedImage2方法),它们会像下面这样失真: 如果我试图逐行复制它们(请参阅下面代码中的SaveFlateEncodedImage方法),它们会像这样呈粉红色 下面是我用来导出它们的代码:C# 使用iText 7,什么';导出平面编码图像的正确方法是什么?,c#,pdf,itext7,C#,Pdf,Itext7,我正在尝试创建代码,以便使用iText版本7.19导出PDF中的图像。我对平面编码图像有一些问题。我正在使用的Microsoft free book中的所有Flate编码图像作为示例(请参阅),它们总是显示为粉红色,这取决于我如何尝试复制它们可能出现扭曲的字节 如果我试图一次复制所有图像字节(请参阅下面代码中的SaveFlateEncodedImage2方法),它们会像下面这样失真: 如果我试图逐行复制它们(请参阅下面代码中的SaveFlateEncodedImage方法),它们会像这样呈粉红
using iText.Kernel;
using iText.Kernel.Pdf;
using iText.Kernel.Pdf.Filters;
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
namespace ITextPdfStuff
{
public class MyPdfImageExtractor
{
private readonly string _pdfFileName;
public MyPdfImageExtractor(string pdfFileName)
{
_pdfFileName = pdfFileName;
}
public void ExtractToDirectory(string directoryName)
{
using (var reader = new PdfReader(_pdfFileName))
{
// Avoid iText.Kernel.Crypto.BadPasswordException: https://stackoverflow.com/a/48065052/97803
reader.SetUnethicalReading(true);
using (var pdfDoc = new PdfDocument(reader))
{
ExtractImagesOnAllPages(pdfDoc, directoryName);
}
}
}
private void ExtractImagesOnAllPages(PdfDocument pdfDoc, string directoryName)
{
Console.WriteLine($"Number of pdf {pdfDoc.GetNumberOfPdfObjects()} objects");
// Extract objects https://itextpdf.com/en/resources/examples/itext-7/extracting-objects-pdf
for (int objNumber = 1; objNumber <= pdfDoc.GetNumberOfPdfObjects(); objNumber++)
{
PdfObject currentObject = pdfDoc.GetPdfObject(objNumber);
if (currentObject != null && currentObject.IsStream())
{
try
{
ExtractImagesOneImage(currentObject as PdfStream, Path.Combine(directoryName, $"image{objNumber}.png"));
}
catch (Exception ex)
{
Console.WriteLine($"Object number {objNumber} is NOT an image! -- error: {ex.Message}");
}
}
}
}
private void ExtractImagesOneImage(PdfStream someStream, string fileName)
{
var pdfDict = (PdfDictionary)someStream;
string subType = pdfDict.Get(PdfName.Subtype)?.ToString() ?? string.Empty;
bool isImage = subType == "/Image";
if (isImage == false)
return;
bool decoded = false;
string filter = pdfDict.Get(PdfName.Filter).ToString();
if (filter == "/FlateDecode")
{
SaveFlateEncodedImage(fileName, pdfDict, someStream.GetBytes(false));
}
else
{
byte[] imgData;
try
{
imgData = someStream.GetBytes(decoded);
}
catch (PdfException ex)
{
imgData = someStream.GetBytes(!decoded);
}
SaveNormalImage(fileName, imgData);
}
}
private void SaveNormalImage(string fileName, byte[] imgData)
{
using (var memStream = new System.IO.MemoryStream(imgData))
using (var image = System.Drawing.Image.FromStream(memStream))
{
image.Save(fileName, ImageFormat.Png);
Console.WriteLine($"{Path.GetFileName(fileName)}");
}
}
private void SaveFlateEncodedImage(string fileName, PdfDictionary pdfDict, byte[] imgData)
{
int width = int.Parse(pdfDict.Get(PdfName.Width).ToString());
int height = int.Parse(pdfDict.Get(PdfName.Height).ToString());
int bpp = int.Parse(pdfDict.Get(PdfName.BitsPerComponent).ToString());
// Example that helped: https://stackoverflow.com/a/8517377/97803
PixelFormat pixelFormat;
switch (bpp)
{
case 1:
pixelFormat = PixelFormat.Format1bppIndexed;
break;
case 8:
pixelFormat = PixelFormat.Format8bppIndexed;
break;
case 24:
pixelFormat = PixelFormat.Format24bppRgb;
break;
default:
throw new Exception("Unknown pixel format " + bpp);
}
// .NET docs https://api.itextpdf.com/iText7/dotnet/7.1.9/classi_text_1_1_kernel_1_1_pdf_1_1_filters_1_1_flate_decode_strict_filter.html
// Java docs have more detail: https://api.itextpdf.com/iText7/java/7.1.7/com/itextpdf/kernel/pdf/filters/FlateDecodeFilter.html
imgData = FlateDecodeStrictFilter.FlateDecode(imgData, true);
// byte[] streamBytes = FlateDecodeStrictFilter.DecodePredictor(imgData, pdfDict);
// Copy the image one row at a time
using (var bmp = new Bitmap(width, height, pixelFormat))
{
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, pixelFormat);
int length = (int)Math.Ceiling(width * bpp / 8.0);
for (int i = 0; i < height; i++)
{
int offset = i * length;
int scanOffset = i * bmpData.Stride;
Marshal.Copy(imgData, offset, new IntPtr(bmpData.Scan0.ToInt64() + scanOffset), length);
}
bmp.UnlockBits(bmpData);
bmp.Save(fileName, ImageFormat.Png);
}
Console.WriteLine($"FlateDecode! {Path.GetFileName(fileName)}");
}
/// <summary>This method distorts the image badly</summary>
private void SaveFlateEncodedImage2(string fileName, PdfDictionary pdfDict, byte[] imgData)
{
int width = int.Parse(pdfDict.Get(PdfName.Width).ToString());
int height = int.Parse(pdfDict.Get(PdfName.Height).ToString());
int bpp = int.Parse(pdfDict.Get(PdfName.BitsPerComponent).ToString());
// Example that helped: https://stackoverflow.com/a/8517377/97803
PixelFormat pixelFormat;
switch (bpp)
{
case 1:
pixelFormat = PixelFormat.Format1bppIndexed;
break;
case 8:
pixelFormat = PixelFormat.Format8bppIndexed;
break;
case 24:
pixelFormat = PixelFormat.Format24bppRgb;
break;
default:
throw new Exception("Unknown pixel format " + bpp);
}
// .NET docs https://api.itextpdf.com/iText7/dotnet/7.1.9/classi_text_1_1_kernel_1_1_pdf_1_1_filters_1_1_flate_decode_strict_filter.html
// Java docs have more detail: https://api.itextpdf.com/iText7/java/7.1.7/com/itextpdf/kernel/pdf/filters/FlateDecodeFilter.html
imgData = FlateDecodeStrictFilter.FlateDecode(imgData, true);
// byte[] streamBytes = FlateDecodeStrictFilter.DecodePredictor(imgData, pdfDict);
// Copy the entire image in one go
using (var bmp = new Bitmap(width, height, pixelFormat))
{
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, pixelFormat);
Marshal.Copy(imgData, 0, bmpData.Scan0, imgData.Length);
bmp.UnlockBits(bmpData);
bmp.Save(fileName, ImageFormat.Png);
}
Console.WriteLine($"FlateDecode! {Path.GetFileName(fileName)}");
}
}
}
我正在通过此代码运行以下免费Microsoft书籍:
有问题的图片在第10页,是黑白的(不是粉色的)
我不是PDF专家,我花了几天的时间来研究这段代码,现在我挑选了一些例子来尝试将其拼凑起来。任何能帮助我克服粉色图像的帮助,我都将不胜感激
-------更新日期:2020年2月4日------
以下是MKL建议更改后的修订版本。他的改变提取了比我更多的图像,并产生了在我上面提到的书中出现的看起来合适的图像:
using iText.Kernel.Pdf;
using iText.Kernel.Pdf.Canvas.Parser;
using iText.Kernel.Pdf.Canvas.Parser.Data;
using iText.Kernel.Pdf.Canvas.Parser.Listener;
using iText.Kernel.Pdf.Xobject;
using System;
using System.Collections.Generic;
using System.IO;
namespace ITextPdfStuff
{
public class MyPdfImageExtractor
{
private readonly string _pdfFileName;
public MyPdfImageExtractor(string pdfFileName)
{
_pdfFileName = pdfFileName;
}
public void ExtractToDirectory(string directoryName)
{
using (var reader = new PdfReader(_pdfFileName))
{
// Avoid iText.Kernel.Crypto.BadPasswordException: https://stackoverflow.com/a/48065052/97803
reader.SetUnethicalReading(true);
using (var pdfDoc = new PdfDocument(reader))
{
ExtractImagesOnAllPages(pdfDoc, directoryName);
}
}
}
private void ExtractImagesOnAllPages(PdfDocument pdfDoc, string directoryName)
{
Console.WriteLine($"Number of pdf {pdfDoc.GetNumberOfPdfObjects()} objects");
IEventListener strategy = new ImageRenderListener(Path.Combine(directoryName, @"image{0}.{1}"));
PdfCanvasProcessor parser = new PdfCanvasProcessor(strategy);
for (var i = 1; i <= pdfDoc.GetNumberOfPages(); i++)
{
parser.ProcessPageContent(pdfDoc.GetPage(i));
}
}
}
public class ImageRenderListener : IEventListener
{
public ImageRenderListener(string format)
{
this.format = format;
}
public void EventOccurred(IEventData data, EventType type)
{
if (data is ImageRenderInfo imageData)
{
try
{
PdfImageXObject imageObject = imageData.GetImage();
if (imageObject == null)
{
Console.WriteLine("Image could not be read.");
}
else
{
File.WriteAllBytes(string.Format(format, index++, imageObject.IdentifyImageFileExtension()), imageObject.GetImageBytes());
}
}
catch (Exception ex)
{
Console.WriteLine("Image could not be read: {0}.", ex.Message);
}
}
}
public ICollection<EventType> GetSupportedEvents()
{
return null;
}
string format;
int index = 0;
}
}
使用iText.Kernel.Pdf;
使用iText.Kernel.Pdf.Canvas.Parser;
使用iText.Kernel.Pdf.Canvas.Parser.Data;
使用iText.Kernel.Pdf.Canvas.Parser.Listener;
使用iText.Kernel.Pdf.Xobject;
使用制度;
使用System.Collections.Generic;
使用System.IO;
命名空间ITEXTPDFSTAFF
{
公共类MyPdfImageExtractor
{
私有只读字符串_pdfFileName;
公共MyPdfImageExtractor(字符串PdfileName)
{
_PdfileName=PdfileName;
}
public void extractodirectory(字符串directoryName)
{
使用(变量读取器=新的PdfReader(_pdfFileName))
{
//避免iText.Kernel.Crypto.BadPasswordException:https://stackoverflow.com/a/48065052/97803
读者。设置不道德阅读(正确);
使用(var pdfDoc=新的PdfDocument(读卡器))
{
ExtractImagesOnAllPages(pdfDoc,目录名);
}
}
}
私有void ExtractImagesOnAllPages(PdfDocument pdfDoc,字符串directoryName)
{
WriteLine($“pdf{pdfDoc.GetNumberOfPdfObjects()}对象的数量”);
IEventListener strategy=newImageRenderListener(Path.Combine(directoryName,@“image{0}.{1}”);
PdfCanvasProcessor parser=新的PdfCanvasProcessor(策略);
对于(var i=1;iPDF内部支持非常灵活的位图图像格式,特别是对于不同的颜色空间而言
iText在其解析API中支持导出其中的一个子集,基本上是可以轻松导出为常规JPEG或PNG的图像子集
因此,首先尝试使用iText解析API进行导出是有意义的。您可以按如下方式进行:
Directory.CreateDirectory(@"extract\");
using (PdfReader reader = new PdfReader(@"Moving to Microsoft Visual Studio 2010 ebook.pdf"))
using (PdfDocument pdfDocument = new PdfDocument(reader))
{
IEventListener strategy = new ImageRenderListener(@"extract\Moving to Microsoft Visual Studio 2010 ebook-i7-{0}.{1}");
PdfCanvasProcessor parser = new PdfCanvasProcessor(strategy);
for (var i = 1; i <= pdfDocument.GetNumberOfPages(); i++)
{
parser.ProcessPageContent(pdfDocument.GetPage(i));
}
}
在您的示例文档中,它成功导出了近400幅图像,其中包括上面的示例图像:
但也有不到30个图像无法导出,在standard out上,您会发现“无法读取图像:不支持颜色空间/设备…”有什么原因让你处理原始数据而不使用itext解析框架吗?@mkl我不确定我是否理解。有没有更简单的方法来实现这一点?itext文档不是很好,而且我似乎找不到任何.NET C#示例来实现这一点。我发现了一些将文件作为原始数据转储,但没有一个将其导出为图像。这些事情我确实发现它们非常古老,而且在iText 5和7之间,该库发生了很大变化。此外,如果有其他不太昂贵的.NET PDF库可以做到这一点,我也愿意接受。“有没有更简单的方法来做到这一点?”-是的,您可以使用解析框架(通常用于提取文本,但实际上还具有访问位图和矢量图形的功能)。当我下周回到办公室时,我可以写一个更详细的解释。@mkl当你有机会的时候,我仍然对任何建议感兴趣。我仍然标记了你的问题。不幸的是,我标记了许多问题,所以我无法查看所有问题。你的方式提取的图像也比我的多。我走了t 318图像和您的方式导致399。谢谢!@DavidYates这些额外的图像中的一些可能是重复的。我的答案中的代码导出图像的每次使用。因此,多次使用的图像会被导出多次。
Directory.CreateDirectory(@"extract\");
using (PdfReader reader = new PdfReader(@"Moving to Microsoft Visual Studio 2010 ebook.pdf"))
using (PdfDocument pdfDocument = new PdfDocument(reader))
{
IEventListener strategy = new ImageRenderListener(@"extract\Moving to Microsoft Visual Studio 2010 ebook-i7-{0}.{1}");
PdfCanvasProcessor parser = new PdfCanvasProcessor(strategy);
for (var i = 1; i <= pdfDocument.GetNumberOfPages(); i++)
{
parser.ProcessPageContent(pdfDocument.GetPage(i));
}
}
public class ImageRenderListener : IEventListener
{
public ImageRenderListener(string format)
{
this.format = format;
}
public void EventOccurred(IEventData data, EventType type)
{
if (data is ImageRenderInfo imageData)
{
try
{
PdfImageXObject imageObject = imageData.GetImage();
if (imageObject == null)
{
Console.WriteLine("Image could not be read.");
}
else
{
File.WriteAllBytes(string.Format(format, index++, imageObject.IdentifyImageFileExtension()), imageObject.GetImageBytes());
}
}
catch (Exception ex)
{
Console.WriteLine("Image could not be read: {0}.", ex.Message);
}
}
}
public ICollection<EventType> GetSupportedEvents()
{
return null;
}
string format;
int index = 0;
}