mirror of
https://github.com/wmjordan/PDFPatcher.git
synced 2026-10-18 21:52:34 +08:00
141 lines
4.6 KiB
C#
141 lines
4.6 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Text;
|
||
using iTextSharp.text.pdf;
|
||
using PDFPatcher.Model;
|
||
using PDFPatcher.Processor.Imaging;
|
||
|
||
namespace PDFPatcher.Processor
|
||
{
|
||
sealed class ImageRecompressor : IPageProcessor
|
||
{
|
||
static readonly PdfName[] __IgnoreFilters = new PdfName[] { PdfName.DCTDECODE, PdfName.JBIG2DECODE };
|
||
static readonly ImageExtracterOptions _imgExpOption = new ImageExtracterOptions() {
|
||
OutputPath = System.IO.Path.GetTempPath(),
|
||
MergeImages = false
|
||
};
|
||
|
||
int _processedImageCount;
|
||
int _optimizedImageCount;
|
||
|
||
#region IPageProcessor 成员
|
||
public string Name => "优化压缩黑白图片";
|
||
|
||
public void BeginProcess(DocProcessorContext context) {
|
||
_processedImageCount = 0;
|
||
_optimizedImageCount = 0;
|
||
}
|
||
public bool EndProcess(PdfReader pdf) {
|
||
Tracker.TraceMessage(Tracker.Category.Notice, Name + "功能:");
|
||
Tracker.TraceMessage(" 处理了 " + _processedImageCount + " 幅图片。");
|
||
Tracker.TraceMessage(" 优化了 " + _optimizedImageCount + " 幅图片的压缩率。");
|
||
return false;
|
||
}
|
||
|
||
public int EstimateWorkload(PdfReader pdf) {
|
||
return pdf.NumberOfPages * 10;
|
||
}
|
||
|
||
public bool Process(PageProcessorContext context) {
|
||
Tracker.IncrementProgress(10);
|
||
var _imgExp = new ImageExtractor(_imgExpOption, context.Pdf);
|
||
var images = context.Page.Locate<PdfDictionary>(PdfName.RESOURCES, PdfName.XOBJECT);
|
||
if (images == null) {
|
||
return false;
|
||
}
|
||
foreach (var item in images) {
|
||
var im = PdfReader.GetPdfObject(item.Value) as PRStream;
|
||
if (im == null
|
||
|| PdfName.IMAGE.Equals(im.GetAsName(PdfName.SUBTYPE)) == false) {
|
||
continue;
|
||
}
|
||
_processedImageCount++;
|
||
var l = im.GetAsNumber(PdfName.LENGTH);
|
||
if (l == null || l.IntValue < 400 /*忽略小图片*/) {
|
||
continue;
|
||
}
|
||
var f = im.Get(PdfName.FILTER);
|
||
PdfName fn = null;
|
||
if (f.Type == PdfObject.ARRAY) {
|
||
var fl = f as PdfArray;
|
||
fn = fl.GetAsName(fl.Size - 1);
|
||
}
|
||
else if (f.Type == PdfObject.NAME) {
|
||
fn = f as PdfName;
|
||
}
|
||
if (fn != null && __IgnoreFilters.Contains(fn)) {
|
||
continue;
|
||
}
|
||
|
||
if (OptimizeBinaryImage(item.Value as PdfIndirectReference, im, l.IntValue)
|
||
/*|| ReplaceJ2kImage(item.Value as PdfIndirectReference, im, fn)*/) {
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
private bool OptimizeBinaryImage(PdfIndirectReference imgRef, PRStream imgStream, int length) {
|
||
var bpc = imgStream.GetAsNumber(PdfName.BITSPERCOMPONENT);
|
||
var mask = imgStream.GetAsBoolean(PdfName.IMAGEMASK);
|
||
if (bpc == null && (mask == null || mask.BooleanValue == false)
|
||
|| bpc != null && bpc.IntValue != 1) {
|
||
return false;
|
||
}
|
||
|
||
var info = new ImageInfo(imgRef);
|
||
var bytes = info.DecodeImage(_imgExpOption);
|
||
using (var fi = ImageExtractor.CreateFreeImageBitmap(info, ref bytes, false, false)) {
|
||
var sb = JBig2Encoder.Encode(fi);
|
||
if (sb.Length > length) {
|
||
return false;
|
||
}
|
||
imgStream.SetData(sb, false);
|
||
imgStream.Put(PdfName.FILTER, PdfName.JBIG2DECODE);
|
||
if (imgStream.GetAsArray(PdfName.COLORSPACE) == null) {
|
||
imgStream.Put(PdfName.COLORSPACE, PdfName.DEVICEGRAY);
|
||
}
|
||
imgStream.Put(PdfName.BITSPERCOMPONENT, new PdfNumber(1));
|
||
imgStream.Put(PdfName.LENGTH, new PdfNumber(sb.Length));
|
||
imgStream.Remove(PdfName.K);
|
||
imgStream.Remove(PdfName.ENDOFLINE);
|
||
imgStream.Remove(PdfName.ENCODEDBYTEALIGN);
|
||
imgStream.Remove(PdfName.COLUMNS);
|
||
imgStream.Remove(PdfName.ROWS);
|
||
imgStream.Remove(PdfName.ENDOFBLOCK);
|
||
imgStream.Remove(PdfName.BLACKIS1);
|
||
imgStream.Remove(PdfName.PREDICTOR);
|
||
imgStream.Remove(PdfName.COLORS);
|
||
imgStream.Remove(PdfName.COLUMNS);
|
||
imgStream.Remove(PdfName.EARLYCHANGE);
|
||
imgStream.Remove(PdfName.DECODEPARMS);
|
||
imgStream.Remove(PdfName.DECODE);
|
||
_optimizedImageCount++;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
private bool ReplaceJ2kImage(PdfIndirectReference imgRef, PRStream imgStream, PdfName filter) {
|
||
if (PdfName.JPXDECODE.Equals(filter) == false) {
|
||
return false;
|
||
}
|
||
|
||
var info = new ImageInfo(imgRef);
|
||
byte[] jpg;
|
||
using (var ms = new System.IO.MemoryStream(info.DecodeImage(_imgExpOption)))
|
||
using (var js = new System.IO.MemoryStream())
|
||
using (var fi = new FreeImageAPI.FreeImageBitmap(ms)) {
|
||
fi.Save(js, FreeImageAPI.FREE_IMAGE_FORMAT.FIF_JPEG, FreeImageAPI.FREE_IMAGE_SAVE_FLAGS.JPEG_BASELINE | FreeImageAPI.FREE_IMAGE_SAVE_FLAGS.JPEG_QUALITYNORMAL | FreeImageAPI.FREE_IMAGE_SAVE_FLAGS.JPEG_PROGRESSIVE);
|
||
jpg = js.ToArray();
|
||
}
|
||
imgStream.SetData(jpg, false);
|
||
imgStream.Put(PdfName.FILTER, PdfName.DCTDECODE);
|
||
imgStream.Put(PdfName.LENGTH, new PdfNumber(jpg.Length));
|
||
_optimizedImageCount++;
|
||
return true;
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
}
|