2013-01-14 21 views
3

我有一组正常RGB颜色的PDF。他们将从转换到8位以减少文件大小中受益。是否有任何API或工具允许我在保留PDF中的非栅格元素的同时执行此操作?将PDF图像实际转换为8位

回答

2

这是一个有趣的。使用PDF Rasterizer和dotPdf的Atalasoft dotImage可以做到这一点(免责声明:我为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对象的Image资源集合中,并删除现有的图像资源并将其替换为新的。

现在我要做一些你可能不会看到任何供应商在谈论他们自己的产品时做的事情 - 我会在很多层面上批评它。首先,它不是很便宜。抱歉。当您看到价格时,您可能会感到不安,但价格包含诚实无瑕的员工的技术支持。

您可以使用iTextPdf Sharp或Bit Miracle的Docotic PDF库或高级组件PDF库做很多工作。后两者也花钱。 Bit Miracle的工程师已被证明是非常有用的,你很可能会在这里看到他们(你好!)。也许他们也可以帮助你。iTextPdfSharp存在问题,因为您确实需要理解PDF规范才能做正确的事情,否则您可能会输出垃圾PDF - 我已经使用iTextPdfSharp并行处理了自己的库,并发现了一些要求深入了解要修复的PDF规范的常见任务的痛点。我试图在我的高级工具中做出决定,以至于您不需要知道PDF规范,也不需要担心创建不好的PDF。

我不特别喜欢这样的事实,即我们的代码库中有几个显然不同的工具可以做类似的事情。由于历史原因,PageCollection是我们的PDF光栅化器的一部分。 PdfDocument严格用于处理页面,并尝试使用内存轻量级和小气。 PdfGeneratedDocument用于操作/创建页面内容。 PdfDecoder用于从现有PDF生成光栅图像。 PdfEncoder用于从图像生成仅限图像的PDF。让所有这些看起来重叠的利基工具都可能令人望而生畏,但是对于他们以及他们彼此的关系来说,这是一种逻辑。