65.9K
CodeProject 正在变化。 阅读更多。
Home

创建高度压缩的可搜索 PDF

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2008年8月1日

CPOL

8分钟阅读

viewsIcon

25801

使用 Pegasus Imaging SDK 将扫描的 TIFF 图像转换为带有图像之上文本的可搜索 PDF,并将文件大小压缩到其原始大小的一小部分。节省服务器空间,并搜索原本无法搜索的文档。我们将向您展示我们的实现方法,并提供 C# 示例代码。

压缩 PDF 的优势

将电子文档图像压缩到最小文件大小可带来许多好处,例如节省存储空间、减小电子邮件附件大小以及通过内部网络或互联网更快地检索文档。然而,文件大小只是创建这些优化文档时需要考虑的一个因素。还必须维护质量,因为一个小的、不可读的、没有可搜索文本的文件在大多数情况下用处不大。最佳解决方案是提供原始文档图像的高质量渲染,并提供完全可搜索的文本。

为了在保持图像质量和可搜索文本的同时创建尽可能小的文件大小,必须依赖成像工具包来提供对文档成像各个级别的访问。通过使用 Pegasus Imaging 的行业领先 .NET 工具包套件,开发人员可以从文档图像创建高度压缩且完全可搜索的 PDF 文档。

PIC_TIFF2PDF.gif

如何实现!

如果没有准确的全页 OCR 识别功能和允许开发人员分块组合 PDF 文档的 PDF 工具包,创建可搜索文本 PDF 文件的选项将非常有限。典型的方法很可能涉及压缩整个文档图像,并将该图像插入到仅包含文本的 PDF 之上。此方法会导致文件大于必要,并且通常会在文本周围产生伪影,使其难以阅读。

一个更好的解决方案是根据图像不同部分的内容对其进行压缩。通过将原始图像分割成不同的文本和图片组件,我们可以就压缩做出更明智的决定,并创建一个更优化的 PDF 文档。然后,我们需要一个 PDF 工具包,允许我们访问生成 PDF 文件的最低级别,以便可以单独压缩图像数据,然后将原始文档图像的各个组件插入到完全可搜索的文本之上。此方法将生成高度压缩的 PDF 文档,大小仅为原始大小的一小部分,同时仍保持原始图像质量。

将这些技术结合使用,可以创建更小但完全可搜索的 PDF 文档。Pegasus Imaging 的 OCR Xpress TM 工具包能够对文档进行全页 OCR,并获取单个文本和图片区域的坐标。ImagXpress® 提供了对 Pegasus Imaging 行业领先的压缩算法的访问。Pegasus Imaging 的 PDF Xpress TM 产品提供了对 PDF 文档最低级别的访问,允许开发人员分块构建 PDF 文档。

接下来的讨论将详细介绍如何从扫描图像创建高度压缩、文本可搜索的 PDF 文档。

编码细节

Pegasus 对象

当我们把 Pegasus 组件放到窗体上时,创建了以下对象。我们将在本白皮书其余部分的代码片段中使用这些对象。

private PegasusImaging.WinForms.ImagXpress9.ImagXpress imagXpress1;
private PegasusImaging.WinForms.OcrXpress1.OcrXpress ocrXpress1;
private PegasusImaging.WinForms.PdfXpress2.PdfXpress pdfXpress1;

全页 OCR

创建高度压缩的 PDF 文档的关键之一是能够使用像 OCR Xpress 这样的全页 OCR 工具包来准确识别文本,并将原始文档图像分割成单独的图片和文本区域。在我们的例子中,OCR Xpress 返回的坐标可以被 ImagXpress 用于裁剪原始文档的图像,并为每个单独的片段做出压缩决策。然后,可以将单独压缩的片段插入到 OCR Xpress 生成的文本 PDF 之上,形成文档图像的原始视图。

第一步是对原始扫描文档图像执行全页 OCR 识别。生成的文本可以使用 OCR Xpress 导出为仅包含文本的 PDF。导出的 PDF 将成为我们要插入单独裁剪图像片段的新 PDF 文档。让我们看看使用 OCR Xpress 进行识别和导出的步骤。

using (ImageX image = ImageX.FromFile(imagXpress1, sourceImageBox.Text))
{
    //temp file for OCR results
    string tempOCRFile = System.IO.Path.GetTempPath() + "tempPDFText.pdf";
    System.IO.FileInfo exportedFile = new System.IO.FileInfo(tempOCRFile);
    if (exportedFile.Exists)
        exportedFile.Delete();
    ocrXpress1.Document.ClearPages();
    using (System.Drawing.Bitmap inputImage = image.ToBitmap(false))
    {
        PegasusImaging.WinForms.OcrXpress1.Page exportPage = 
            ocrXpress1.Document.AddPage(inputImage);
        //turning off pictureIdentification will cause OCR Xpress to 
        //ignore picture regions, providing more accurate text recognition 
        //results.
        ocrXpress1.Document.PictureIdentification = false;
        //segment the image into text and picture regions
        ocrXpress1.Document.Locate(exportPage);
        //perform OCR on the image
        ocrXpress1.Document.Recognize(exportPage);
        //export OCRed text as a PDF.
        ocrXpress1.Document.Export(PegasusImaging.WinForms.OcrXpress1.ExportFormat.Pdf, 
            tempOCRFile);
    }
    return tempOCRFile;
}

使用 OCR Xpress 分割图像

OCR Xpress 中的 Locate() 方法通过定位文本区域和图片区域来分割图像。这些区域的信息随后可以被 ImagXpress 用于创建单独裁剪的图像片段。然后可以对每个图像片段单独进行压缩,使开发人员能够更精细地控制诸如质量与大小之类的决策。

与之前的 OCR 操作不同,这次我们要打开 PictureIdentification,以确保我们获得准确的图片区域坐标。为了获得最佳分割结果,应先调用 Locate 方法,然后调用 Recognize 方法。生成的区域片段将存储在 RegionCollection 中,然后我们可以通过循环处理这些片段进行最后的几个步骤。

ocrXpress1.Document.PictureIdentification = true;
//locate and then call GetRegions to return a RegionCollection. 
//RegionCollection contains TextRegions and PictureRegions.
ocrXpress1.Document.Locate(thePage);
ocrXpress1.Document.Recognize(thePage);
PegasusImaging.WinForms.OcrXpress1.RegionCollection segmentRegions = 
    new RegionCollection();
segmentRegions = thePage.GetRegions();

现在我们有了一个区域集合,我们可以使用 ImagXpress 将原始图像裁剪成由 RegionCollection 中存储的坐标定义的单独图像片段。当我们遍历 RegionCollection 时,其余的步骤将对每个区域执行。包含其余步骤的循环如下所示:

for (int i = 0; i < segmentRegions.Count; i++)
{
    PegasusImaging.WinForms.OcrXpress1.Region currentRegion = segmentRegions[i];
    //get a copy of the original image
    using (ImageX croppedImage = image.Copy())
    {
        //we will crop and compress the image in memory and then pass it 
        //to PDF Xpress for reconstruction
        using (System.IO.MemoryStream croppedImageStream = new System.IO.MemoryStream())
        {
            using (Processor cropIt = new Processor(imagXpress1, croppedImage))
            {
                cropIt.Crop(currentRegion.Area);

使用 ImagXpress 压缩图像

现在,我们可以使用 ImagXpress 中的 Processor 类来检查裁剪图像的图像数据,并选择最合适的压缩技术和设置。对于文本区域和每像素 1 位的图片区域,将使用 JBIG2 压缩。对于所有其他图片区域,我们可以使用 UniqueColorCount 来确定图片中的颜色数据量。如果图片区域包含超过 256 种唯一颜色,我们将使用 JPEG 或 JPEG 2000 创建最佳压缩质量。JPEG 压缩参数可以调整以控制失真量。对于包含少于 256 种唯一颜色的图片区域,我们可以通过将图像设置为 8 位像素,然后使用 TIFF LZW 压缩结果图像来进一步减小尺寸。

using (Processor cropIt = new Processor(imagXpress1, croppedImage))
{
    PegasusImaging.WinForms.ImagXpress9.SaveOptions so = 
        new PegasusImaging.WinForms.ImagXpress9.SaveOptions();
    //if it is a text region or a 1bpp image, use JBIG2
    if (theCurrentRegion is PegasusImaging.WinForms.OcrXpress1.TextRegion 
        || theCroppedImage.ImageXData.BitsPerPixel == 1)
    {
        so.Format = ImageXFormat.Jbig2;
        so.Jbig2.LoosenessCompression = 1;
    }
    //for all other picture regions, we can check the unique color count.  
    //if the unique color count is greater than 256, we should use JPG or 
    //JP2K.
    //if the color count is less than 256, we can use TIFF LZW
    else
    {
        long uniqueColorCount = croppedImageProcessor.UniqueColors();
        if (uniqueColorCount < 256)
        {
            //we can further reduce size by making this 8 bpp
            croppedImageProcessor.ColorDepth(8, PaletteType.Gray, DitherType.NoDither);
            so.Format = ImageXFormat.Tiff;
            so.Tiff.Compression = Compression.Lzw;
        }
        else
        {
            so.Format = ImageXFormat.Jpeg;
            so.Jpeg.Chrominance = 40;
            so.Jpeg.Luminance = 40;
        }
    }
} 

现在我们将压缩和裁剪后的图像数据保存到 MemoryStream 中,以传递给 PDF Xpress。

croppedImage.SaveStream(croppedImageStream, so);
//compressed image from mem stream
croppedImageStream.Flush();
croppedImageStream.Position = 0;
byte[] byteData = new byte[croppedImageStream.Length];
croppedImageStream.Read(byteData, 0, byteData.Length);
croppedImageStream.Close();

使用 PDF Xpress 组合 PDF

此时,我们有一个仅包含 OCR Xpress 生成的可搜索文本的 PDF 文档。我们还有单独裁剪的文本和图片区域,它们存储在内存中,我们使用最适合该片段数据的压缩算法对其进行了压缩。现在,我们只需要从内存中读取每个图像,并使用 PDF Xpress 将它们重新组合在一起。

PDF Xpress 提供了一个简单的方法将单个图像添加到 PDF 文档的页面中。位置和图像适应(拉伸、缩小等)参数为开发人员提供了精确定位页面上图像数据的灵活性。

ocredDoc.AddImage(0, 72 * currentRegion.Area.X / imageDPI,
    pdfY,
    currentRegion.Area.Width * conversionFactor, 
    currentRegion.Area.Height * conversionFactor,
    fitSettings, byteData, 0);

在将所有单独的图像区域插入到我们的可搜索文本之上后,我们可以设置 PDF 保存选项,并保存我们新的图像之上文本 PDF。

string destinationFileName = “destination for the PDF”; 
PegasusImaging.WinForms.PdfXpress2.SaveOptions pdfSaveOpts = 
    new PegasusImaging.WinForms.PdfXpress2.SaveOptions();
                    
pdfSaveOpts.Filename = destinationFileName;
pdfSaveOpts.Overwrite = true;
ocredDoc.Save(pdfSaveOpts);

就是这样!

结论

让我们回顾一下我们所完成的工作。首先,我们使用 OCR Xpress 对原始图像进行了全页 OCR,创建了可搜索文本,并识别了单独的文本和图片图像数据区域。然后,我们能够使用 ImagXpress 裁剪这些单独的图像数据区域。ImagXpress 还通过允许我们检查裁剪区域的图像数据并决定正确的压缩设置来帮助我们保持图像质量。最后,我们使用 PDF Xpress 将我们的图像添加到 OCR 文本结果中,以创建我们高度压缩但完全可搜索的 PDF 文档。

您可以在 Pegasus Imaging 的主页 上找到 Pegasus Imaging 的产品下载和功能。如有更多信息,请联系我们的 销售部门支持部门

提示与技巧

创建空白画布

导出的文本可能包含略有不同的字体大小,这通常会导致比相同文本的实际裁剪图像更大的区域。这会导致下面的文本透过或从文本区域图像的边缘“渗出”。对于最终的 PDF,我们希望可搜索文本完全隐藏,以便查看器只看到原始文档中的图像。

我们可以使用的一种确保可搜索文本隐藏的技术是使用一个小的白色画布位图图像,并使用 PDF Xpress 中的 addImage 方法将其添加到 PDF 中。在调用 addImage 方法时,我们可以设置某些参数,包括 ImageFitSettings 枚举,告诉 PDF Xpress 组件将小的白色画布拉伸到整个 PDF 文档。此方法为我们提供了一个空白画布,我们可以在其上开始插入裁剪的图像片段。

using (PegasusImaging.WinForms.PdfXpress2.Document ocredDoc = 
    new PegasusImaging.WinForms.PdfXpress2.Document(pdfXpress1, tempOCRFile))
{
    //description of the current page
    PegasusImaging.WinForms.PdfXpress2.PageInfo pageInfo =   ocredDoc.GetInfo(0);
    //create our 1 pixel "canvas"
    using (System.Drawing.Bitmap blankCanvas = new Bitmap(1, 1))
    {
        using (System.Drawing.Graphics g = Graphics.FromImage(blankCanvas))
        {
            g.Clear(Color.White);
        }
        using (System.IO.MemoryStream blankMem = new System.IO.MemoryStream())
        {
            blankCanvas.Save(blankMem, System.Drawing.Imaging.ImageFormat.Bmp);
            byte[] blankBytes = new byte[blankMem.Length];
            blankMem.Flush();
            blankMem.Position = 0;
            blankMem.Read(blankBytes, 0, blankBytes.Length);
            //now use PDF Xpress to add the blank canvas image 
            //and stretch it over the entire area of the PDF.
            ocredDoc.AddImage(0, 0, 0, pageInfo.MediaWidth,
                pageInfo.MediaHeight, ImageFitSettings.Stretch, blankBytes, 0);
            blankMem.Close();
        }
    }
}

关于作者

Steve Wilson 于 2007 年加入 Pegasus Imaging。作为产品开发经理,Steve 协调多名工程师的努力,以进一步巩固 Pegasus 在文档成像市场的地位。本着这一目标,Steve 负责领导他的团队扩展多个 Pegasus 产品线的特性集。他为管理团队带来了强大的技术背景,以及管理多样化离岸和 onshore 开发团队的经验。在加入 Pegasus 之前,Steve 在 Viryanet 工作了 7 年。在那里,他从软件工程师晋升为软件开发管理,为公用事业公司交付劳动力管理软件。Steve 获得了南佛罗里达大学计算机科学学士学位。

© . All rights reserved.