No es un ejemplo de cómo find and replace images in an existing PDF por el creator of iText. En realidad es un pequeño extracto del his book. Ya que está en Java, he aquí una simple sustitución:
public void ReduceResolution(PdfReader reader, long quality) {
int n = reader.XrefSize;
for (int i = 0; i < n; i++) {
PdfObject obj = reader.GetPdfObject(i);
if (obj == null || !obj.IsStream()) {continue;}
PdfDictionary dict = (PdfDictionary)PdfReader.GetPdfObject(obj);
PdfName subType = (PdfName)PdfReader.GetPdfObject(
dict.Get(PdfName.SUBTYPE)
);
if (!PdfName.IMAGE.Equals(subType)) {continue;}
PRStream stream = (PRStream)obj;
try {
PdfImageObject image = new PdfImageObject(stream);
PdfName filter = (PdfName) image.Get(PdfName.FILTER);
if (
PdfName.JBIG2DECODE.Equals(filter)
|| PdfName.JPXDECODE.Equals(filter)
|| PdfName.CCITTFAXDECODE.Equals(filter)
|| PdfName.FLATEDECODE.Equals(filter)
) continue;
System.Drawing.Image img = image.GetDrawingImage();
if (img == null) continue;
var ll = image.GetImageBytesType();
int width = img.Width;
int height = img.Height;
using (System.Drawing.Bitmap dotnetImg =
new System.Drawing.Bitmap(img))
{
// set codec to jpeg type => jpeg index codec is "1"
System.Drawing.Imaging.ImageCodecInfo codec =
System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders()[1];
// set parameters for image quality
System.Drawing.Imaging.EncoderParameters eParams =
new System.Drawing.Imaging.EncoderParameters(1);
eParams.Param[0] =
new System.Drawing.Imaging.EncoderParameter(
System.Drawing.Imaging.Encoder.Quality, quality
);
using (MemoryStream msImg = new MemoryStream()) {
dotnetImg.Save(msImg, codec, eParams);
msImg.Position = 0;
stream.SetData(msImg.ToArray());
stream.SetData(
msImg.ToArray(), false, PRStream.BEST_COMPRESSION
);
stream.Put(PdfName.TYPE, PdfName.XOBJECT);
stream.Put(PdfName.SUBTYPE, PdfName.IMAGE);
stream.Put(PdfName.FILTER, filter);
stream.Put(PdfName.FILTER, PdfName.DCTDECODE);
stream.Put(PdfName.WIDTH, new PdfNumber(width));
stream.Put(PdfName.HEIGHT, new PdfNumber(height));
stream.Put(PdfName.BITSPERCOMPONENT, new PdfNumber(8));
stream.Put(PdfName.COLORSPACE, PdfName.DEVICERGB);
}
}
}
catch {
// throw;
// iText[Sharp] can't handle all image types...
}
finally {
// may or may not help
reader.RemoveUnusedObjects();
}
}
}
Se dará cuenta de que es solamente el manejo de JPEG. La lógica se invierte (en lugar de tratar explícitamente solo DCTDECODE
/JPEG) por lo que puede descomentar algunos de los tipos de imágenes ignoradas y experimentar con el PdfImageObject
en el código anterior. En particular, la mayoría de las imágenes FLATEDECODE
(.bmp, .png y .gif) se representan como PNG (confirmadas en el método DecodeImageBytes
del código fuente PdfImageObject
). Por lo que sé, .NET no es compatible con la codificación PNG. Hay algunas referencias para admitir esto here y here. Puede probar un PNG optimization executable autónomo, pero también tiene que averiguar cómo configurar PdfName.BITSPERCOMPONENT
y PdfName.COLORSPACE
en el PRStream
.
Para completarlo, ya que su pregunta se refiere específicamente acerca de la compresión PDF, aquí es cómo comprimir un archivo PDF con iTextSharp:
PdfStamper stamper = new PdfStamper(
reader, YOUR-STREAM, PdfWriter.VERSION_1_5
);
stamper.Writer.CompressionLevel = 9;
int total = reader.NumberOfPages + 1;
for (int i = 1; i < total; i++) {
reader.SetPageContent(i, reader.GetPageContent(i));
}
stamper.SetFullCompression();
stamper.Close();
También puede intentar y ejecutar el archivo PDF a través PdfSmartCopy para obtener el tamaño del archivo. Elimina recursos redundantes, pero al igual que la llamada al RemoveUnusedObjects()
en el bloque finally
, puede o no ayudar. Eso dependerá de cómo se creó el PDF.
IIRC iText [Sharp] no funciona bien con JBIG2DECODE
, por lo que la sugerencia de @ Alasdair se ve bien, si desea tomarse el tiempo para aprender la biblioteca de Jasper y usar el enfoque de fuerza bruta.
Buena suerte.
EDITAR - 2012-08-17, comentario de @Craig:
Para guardar el PDF después de comprimir los archivos JPEG usando el método anterior ReduceResolution()
:
a. Una instancia de un objeto PdfReader
:
PdfReader reader = new PdfReader(pdf);
b. Pase el PdfReader
al ReduceResolution()
método anterior.
c. Pase el PdfReader
modificado a PdfStamper
.He aquí una forma usando un MemoryStream
:
// Save altered PDF. then you can pass the btye array to a database, etc
using (MemoryStream ms = new MemoryStream()) {
using (PdfStamper stamper = new PdfStamper(reader, ms)) {
}
return ms.ToArray();
}
O puede utilizar cualquier otro Stream
si no es necesario para mantener el PDF en la memoria. P.ej. utilice un FileStream
y guárdelo directamente en el disco.
ver este http://stackoverflow.com/questions/5296667/pdftk-compression-option también podría usar ImageMagick – Guillaume
ya que es .NET, el problema @Guillaume está hablando de http://imagemagick.codeplex.com/ – balexandre
@balexandre El problema no es el remuestreo de las imágenes, sino que volviendo a poner las imágenes en la secuencia de pdf, no puede guardar las imágenes en el disco, ya que creará problemas con la transparencia, etc. – user1053237