实用转换PDF图像到8位

  • 本文关键字:8位 图像 PDF 转换 pdf
  • 更新时间 :
  • 英文 :


我有一组普通RGB颜色的pdf文件。他们将受益于转换为8位以减小文件大小。是否有任何api或工具可以让我在PDF中保留非光栅元素的同时做到这一点?

这个很有趣。Atalasoft dotImage与PDF栅格化和dotPdf可以做到这一点(免责声明:我为Atalasoft工作,编写了大多数PDF工具)。我将首先查找候选页面:

List<int> GetCandidatePages(Stream pdf, string password)
{
    List<int> retVal = new List<int>();
    using (PageCollection pages = new PageCollection(pdf, password)) {
        for (int i=0; i < pages.Count; i++) {
            if (pages[i].SingleImageOnly())
                retVal.Add(i);
        }
    }
    pdf.Seek(0, SeekOrigin.Begin); // restore file pointer
    return retVal;
}

接下来,我将栅格化这些页面,将它们转换为8位图像,但为了保持效率,我将使用一个管理内存良好的ImageSource:

public class SelectPageImageSource : RandomAccessImageSource {
    private List<int> _pages;
    private Stream _stm;
    public SelectPageImageSource(Stream stm, List<int> pages)
    {
        _stm = stm;
        _pages = pages;
    }
    protected override ImageSourceNode LowLevelAcquire(int index)
    {
        PdfDecoder decoder = new PdfDecoder();
        _stm.Seek(0, SeekOrigin.Begin);
        AtalaImage image = PdfDecoder.Read(_stm, _pages[index], null);
        // change to 8 bit
        if (image.PixelFormat != PixelFormat.Pixel8bppIndexed) {
            AtalaImage changed = image.GetChangedPixelFormat(PixelFormat.Pixel8bppIndexed);
            image.Dispose();
            image = changed;
        }
        return new FileReloader(image, new PngEncoder());
    }
    protected override int LowLevelTotalImages() { return _pages.Count; }
}

接下来你需要创建一个新的PDF:

public void Make8BitImagePdf(Stream pdf, Stream outPdf, List<int> pages)
{
    PdfEncoder encoder = new PdfEncoder();
    SelectPageImageSource source = new SelectPageImageSource(pdf, pages);
    encoder.Save(outPdf, source, null);
}

接下来你需要用新的页面替换原来的页面:

public void ReplaceOriginalPages(Stream pdf, Stream image8Bit, Stream outPdf, List<int> pages)
{
    PdfDocument docOrig = new PdfDocument(pdf);
    PdfDocument doc8Bit = new PdfDocument(image8Bit);
    for (int i=0; i < pages.Count; i++) {
        docOrig.Pages[pages[i]] = doc8Bit[i];
    }
    docOrig.Save(outPdf); // this is your final
}

这或多或少会做你想要的。不太理想的一点是,图像页面已经栅格化,这可能不是您想要的。好的是,通过栅格化,生成输出很容易,但它可能不是原始图像的分辨率。这是可以做到的,但这需要更多的工作,因为您需要从SingleImageOnly页面中提取图像,然后更改其像素格式。这样做的问题是,SingleImageOnly并不意味着图像适合整个页面,也不意味着图像被放置在任何特定位置。除了PixelFormat更改(实际上,在更改之前)之外,您还需要将用于将图像放置在页面上的矩阵应用于图像本身,并使用PdfEncoder和适当的页边距集和原始页面大小来获取图像。这都是老生常谈了,但这是相当多的代码。

还有另一种方法也可以使用我们的PDF生成API。它包括打开文档并将图像资源换成8位的文档。这也是可行的,但并非完全无关紧要。你可以这样做:

public void ReplaceImageResources(Stream pdf, Stream outPdf, List<int> pages)
{
    PdfGeneratedDocument doc = new PdfGeneratedDocument(pdf);
    doc.Resources.Images.Compressors.Insert(0, new AtalaImageCompressor());
    foreach (int page in pages) {
        // GetSinglePageImage uses PageCollection, as above, to
        // pull a single image from the page (no need to use the matrix)
        // then converts it to 8 bpp indexed and returns it or null if it
        // is already 8 bpp indexed (or 4bpp or 1bpp).
        using (AtalaImage image = GetSinglePageImage(pdf, page)) {
            if (image == null) continue;
            foreach (string resName in doc.Pages[page].ImportedImages) {
                doc.Resources.Images.Remove(resName);
                doc.Resources.Images.Add(resName, image);
                break;
            }
        }
    }
    doc.Save(outPdf);
}

就像我说的,这是很棘手的——PDF生成套件是为从整块布生成新的PDF或向现有的PDF添加新页面而设计的(在未来,我们希望添加完整的编辑功能)。但是PDF将其所有图像管理为文档中的资源,我们有能力完全替换这些资源。因此,为了使生活更容易,我们添加一个ImageCompressor到图像资源集合,处理AtalaImage对象,并删除现有的图像资源,并用新的图像资源替换它们。

现在我要做一些你可能不会看到任何供应商在谈论他们自己的产品时做的事情——我要在几个层面上批评它。首先,它不是很便宜。对不起。当你看到这个价格时,你可能会感到震惊,但这个价格包括了来自员工的技术支持,他们真的是首屈一指的。

你可以用iTextPdf Sharp或Bit Miracle的Docotic PDF库或Tall Components PDF库做很多这样的事情。后两者也要花钱。事实证明,Bit Miracle的工程师非常有帮助,你可能会在这里看到他们(嗨!)。也许他们也能帮你。iTextPdfSharp是有问题的,因为你真的需要理解PDF规范才能做正确的事情,否则你可能会输出垃圾PDF——我用我自己的库与iTextPdfSharp一起做了这个实验,发现了一些常见任务的痛点,需要对PDF规范的深入了解才能修复。我试着在我的高级工具中做出决定,这样你就不需要知道PDF规范,也不需要担心创建糟糕的PDF。

我特别不喜欢在我们的代码库中有几个明显不同的工具做类似的事情。由于历史原因,PageCollection是我们的PDF光栅化器的一部分。PdfDocument严格用于操作页面,并尝试轻量级和节省内存。PdfGeneratedDocument用于操作/创建页面内容。PdfDecoder用于从现有PDF生成光栅图像。PdfEncoder用于从图像生成仅图像的PDF。拥有所有这些明显重叠的利基工具可能会让人望而生畏,但它们之间以及它们之间的关系是有逻辑的。

最新更新