24

我在 Google 上搜索了很多关于如何压缩现有pdf(大小)的信息。我的问题是

  1. 我不能使用任何应用程序,因为它需要由 C# 程序完成。

  2. 我不能使用任何付费图书馆,因为我的客户不想超出预算。所以付费图书馆肯定是不行

我在过去 2 天完成了我的家庭作业,并使用 iTextSharp、BitMiracle 找到了一个解决方案,但无济于事,因为前者仅减少了 1% 的文件,而后者是付费的。

我还遇到了 PDFcompressNET 和 pdftk,但我找不到它们的 .dll。

实际上,pdf 是包含 2-3 张图像(黑白)和大约 70 页的保险单,大小为 5 MB。

我只需要 pdf 格式的输出(不能是任何其他格式)

4

4 回答 4

15

这是一种执行此操作的方法(无论您使用的工具包,这都应该有效):

如果您有 24 位 rgb 或 32 位 cmyk 图像,请执行以下操作:

  • 确定图像是否真的如此。如果是 cmyk,则转换为 rgb。如果它是 rgb 并且真的是灰色的,则转换为灰色。如果它是灰色或调色板并且只有 2 种真实颜色,则转换为 1 位。如果它是灰色的并且灰度变化的方式相对较少,请考虑使用合适的二值化技术转换为 1 位。
  • 测量与放置在页面上的方式相关的图像尺寸 - 如果它是 300 dpi 或更大,请考虑根据图像的位深度将图像重新采样为更小的尺寸 - 例如,您可能可以从 300 dpi灰色或 rgb 到 200 dpi,不会丢失太多细节。
  • 如果您有一个真正彩色的 rgb 图像,请考虑将其调色。
  • 检查图像的内容,看看您是否可以帮助使其更可压缩。例如,如果您浏览一个彩色/灰色图像并细化很多聚集的颜色,请考虑对它们进行平滑处理。如果它是灰色或黑白并包含许多斑点,请考虑去除斑点。
  • 明智地选择最终压缩。JPEG2000 可以比 JPEG 做得更好。JBIG2 比 G4 做得好得多。Flate 可能是灰色的最佳非破坏性压缩。JPEG2000 和 JBIG2 的大多数实现都不是免费的。
  • 如果您是摇滚明星,您想尝试分割图像并将其分成真正黑白和真正彩色的区域。

也就是说,如果您确实可以在无人监督的情况下完成所有这些工作,那么您就拥有了自己的商业产品。

我会说您可以使用Atalasoft dotImage完成大部分操作(免责声明:它不是免费的;我在那里工作;我编写了几乎所有的 PDF 工具;我曾经在 Acrobat 上工作)。

使用 dotImage 的一种特殊方法是提取所有仅是图像的页面,重新压缩它们并将它们保存到新的 PDF 中,然后通过从原始文档中获取所有页面并将它们替换为重新压缩的页面来构建新的 PDF,然后再次保存。这并不难。

List<int> pagesToReplace = new List<int>();
PdfImageCollection pagesToEncode = new PdfImageCollection();

using (Document doc = new Document(sourceStream, password)) {

    for (int i=0; i < doc.Pages.Count; i++) {
        Page page = doc.Pages[i];
        if (page.SingleImageOnly) {
            pagesToReplace.Add(i);
            // a PDF image encapsulates an image an compression parameters
            PdfImage image = ProcessImage(sourceStream, doc, page, i);
            pagesToEncode.Add(i);
        }
    }

    PdfEncoder encoder = new PdfEncoder();
    encoder.Save(tempOutStream, pagesToEncode, null); // re-encoded pages
    tempOutStream.Seek(0, SeekOrigin.Begin);

    sourceStream.Seek(0, SeekOrigin.Begin);
    PdfDocument finalDoc = new PdfDocument(sourceStream, password);
    PdfDocument replacementPages = new PdfDocument(tempOutStream);

    for (int i=0; i < pagesToReplace.Count; i++) {
         finalDoc.Pages[pagesToReplace[i]] = replacementPages.Pages[i];
    }

    finalDoc.Save(finalOutputStream);

这里缺少的是 ProcessImage()。ProcessImage 将对页面进行光栅化(您无需了解图像可能已被缩放到 PDF 上)或提取图像(并跟踪图像上的转换矩阵),然后执行上面列出的步骤。这不是微不足道的,但它是可行的。

于 2012-12-07T16:21:27.027 回答
7

我认为您可能想让您的客户意识到您提到的任何库都不是完全免费的:

  • iTextSharp 已获得 AGPL 许可,因此您必须发布解决方案的源代码或购买商业许可。
  • PDFcompressNET 是一个商业图书馆。
  • pdftk 是 GPL 许可的,因此您必须发布解决方案的源代码或购买商业许可证。
  • Docotic.Pdf 是一个商业图书馆。

鉴于以上所有情况,我认为我可以放弃免费软件要求。

Docotic.Pdf 可以在不引入任何破坏性更改的情况下不同程度地减小压缩和未压缩 PDF的大小。

收益取决于 PDF 的大小和结构:对于小文件或主要是扫描图像的文件,减少可能不是那么好,因此您应该尝试使用文件库并亲自查看。

如果您最关心文件的大小并且文件中有很多图像,并且您可以丢失这些图像的某些质量,那么您可以使用 Docotic.Pdf 轻松重新压缩现有图像。

这是使所有图像双层并使用传真压缩进行压缩的代码:

static void RecompressExistingImages(string fileName, string outputName)
{
    using (PdfDocument doc = new PdfDocument(fileName))
    {
        foreach (PdfImage image in doc.Images)
            image.RecompressWithGroup4Fax();

        doc.Save(outputName);
    }
}

还有RecompressWithFlate,RecompressWithGroup3FaxRecompressWithJpeg方法。

如果需要,该库会将彩色图像转换为双层图像。您可以指定放气压缩级别、JPEG 质量等。

Docotic.Pdf 还可以在 PDF 中调整大图像的大小(并同时重新压缩它们)。如果文档中的图像实际上比需要的大,或者图像质量不那么重要,这可能很有用。

下面是缩放所有宽度或高度大于或等于 256 的图像的代码。然后使用 JPEG 压缩对缩放的图像进行编码。

public static void RecompressToJpeg(string path, string outputPath)
{
    using (PdfDocument doc = new PdfDocument(path))
    {
        foreach (PdfImage image in doc.Images)
        {
            // image that is used as mask or image with attached mask are
            // not good candidates for recompression
            if (!image.IsMask && image.Mask == null && (image.Width >= 256 || image.Height >= 256))
                image.Scale(0.5, PdfImageCompression.Jpeg, 65);
        }

        doc.Save(outputPath);
    }
}

可以使用其中一种ResizeTo方法将图像大小调整为指定的宽度和高度。请注意,该ResizeTo方法不会尝试保留图像的纵横比。您应该自己计算适当的宽度和高度。

免责声明:我为 Bit Miracle 工作。

于 2012-12-07T05:30:41.750 回答
3

GhostScript是 AGPL 许可的软件,可以压缩 PDF。在 github 上还有一个 AGPL 许可的 C# 包装

您可以使用该GhostscriptProcessor包装器中的类将自定义命令传递给 GhostScript,就像在描述 PDF 压缩的AskUbuntu 答案中找到的命令一样。

于 2015-10-12T02:55:45.853 回答
2

使用PdfSharp

public static void CompressPdf(string targetPath)
{
    using (var stream = new MemoryStream(File.ReadAllBytes(targetPath)) {Position = 0})
    using (var source = PdfReader.Open(stream, PdfDocumentOpenMode.Import))
    using (var document = new PdfDocument())
    {
        var options = document.Options;
        options.FlateEncodeMode = PdfFlateEncodeMode.BestCompression;
        options.UseFlateDecoderForJpegImages = PdfUseFlateDecoderForJpegImages.Automatic;
        options.CompressContentStreams = true;
        options.NoCompression = false;
        foreach (var page in source.Pages)
        {
            document.AddPage(page);
        }

        document.Save(targetPath);
    }
}
于 2019-08-07T02:58:07.803 回答