iText:降低图像质量(用于减小生成的 PDF 大小)
iText: Reduce image quality (for reducing the resulting PDF size)
减小使用 iText 新创建的 PDF 文件中 JPEG 图像大小的最佳做法是什么? (我的 objective 是图像质量和文件大小之间的权衡。)
图像创建如下:
Image image = new Image(ImageDataFactory.create(imagePath))
我想提供一个比例因子,例如 0.5
,它将一行中的像素数减半。
假设我用一个 3 MB 的图像生成一个 PDF。我尝试了 image.scale(0.5f, 0.5f)
,但生成的 PDF 文件仍然大约为 3 MB。我预计它会变得更小。
因此我猜嵌入在 PDF 文件中的源图像没有被触及。但这就是我需要的:存储在磁盘上的整个 PDF 文件的像素总数应该减少。
实现此目标的 easiest/recommended 方法是什么?
先缩放图片,再用iText打开缩放后的图片
ImageDataFactory 中有一个接受 AWT 图像的创建方法。先用AWT工具缩放图片,然后这样打开:
String imagePath = "C:\path\to\image.jpg";
java.awt.Image awtImage = ImageIO.read(new File(imagePath));
// scale image here
int scaledWidth = awtImage.getWidth(null) / 2;
int scaledHeight = awtImage.getHeight(null) / 2;
BufferedImage scaledAwtImage = new BufferedImage(scaledWidth, scaledHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g = scaledAwtImage.createGraphics();
g.drawImage(awtImage, 0, 0, scaledWidth, scaledHeight, null);
g.dispose();
/*
Optionally pick a color to replace with transparency.
Any pixels that match this color will be replaced by tansparency.
*/
Color bgColor = Color.WHITE;
Image itextImage = new Image(ImageDataFactory.create(scaledAwtImage, bgColor));
有关如何缩放图像的更好提示,请参阅 How can I resize an image using Java?
如果您在添加到 PDF 时仍然需要原始尺寸,只需重新缩放即可。
itextImage.scale(2f, 2f);
注意:此代码未经测试。
编辑 以回应关于赏金的评论
你让我思考和寻找。看来 iText 将导入 AWT 图像视为原始图像。我认为它对待它的方式与 BMP 相同,只是 writes the pixel data using /FlateDecode,这可能远未达到最佳状态。我能想到的满足您要求的唯一方法是使用 ImageIO 将缩放后的图像写入文件系统或将 ByteArrayOutputStream 作为 jpeg,然后使用结果 file/bytes 用 iText 打开。
这是一个使用字节数组的更新示例。如果您想更喜欢压缩级别等,refer here。
String imagePath = "C:\path\to\image.jpg";
java.awt.Image awtImage = ImageIO.read(new File(imagePath));
// scale image here
int scaledWidth = awtImage.getWidth(null) / 2;
int scaledHeight = awtImage.getHeight(null) / 2;
BufferedImage scaledAwtImage = new BufferedImage(scaledWidth, scaledHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g = scaledAwtImage.createGraphics();
g.drawImage(awtImage, 0, 0, scaledWidth, scaledHeight, null);
g.dispose();
ByteArrayOutputStream bout = new ByteArrayOutputStream()
ImageIO.write(scaledAwtImage, "jpeg", bout);
byte[] imageBytes = bout.toByteArray();
Image itextImage = new Image(ImageDataFactory.create(imageBytes));
此 documentations 中列出了一种方法,它使您可以压缩图像并减少磁盘上存储的整个 PDF 文件。希望对你有帮助。
以下为代码示例:
/*
* This example was written by Bruno Lowagie in answer to the following question:
*
*/
package sandbox.images;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.PRStream;
import com.itextpdf.text.pdf.PdfName;
import com.itextpdf.text.pdf.PdfNumber;
import com.itextpdf.text.pdf.PdfObject;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.parser.PdfImageObject;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.imageio.ImageIO;
import sandbox.WrapToTest;
/**
* @author Bruno Lowagie (iText Software)
*/
@WrapToTest
public class ReduceSize {
public static final String SRC = "resources/pdfs/single_image.pdf";
public static final String DEST = "results/images/single_image_reduced.pdf";
public static final float FACTOR = 0.5f;
public static void main(String[] args) throws DocumentException, IOException {
File file = new File(DEST);
file.getParentFile().mkdirs();
new ReduceSize().manipulatePdf(SRC, DEST);
}
public void manipulatePdf(String src, String dest) throws DocumentException, IOException {
PdfReader reader = new PdfReader(src);
int n = reader.getXrefSize();
PdfObject object;
PRStream stream;
// Look for image and manipulate image stream
for (int i = 0; i < n; i++) {
object = reader.getPdfObject(i);
if (object == null || !object.isStream())
continue;
stream = (PRStream)object;
if (!PdfName.IMAGE.equals(stream.getAsName(PdfName.SUBTYPE)))
continue;
if (!PdfName.DCTDECODE.equals(stream.getAsName(PdfName.FILTER)))
continue;
PdfImageObject image = new PdfImageObject(stream);
BufferedImage bi = image.getBufferedImage();
if (bi == null)
continue;
int width = (int)(bi.getWidth() * FACTOR);
int height = (int)(bi.getHeight() * FACTOR);
if (width <= 0 || height <= 0)
continue;
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
AffineTransform at = AffineTransform.getScaleInstance(FACTOR, FACTOR);
Graphics2D g = img.createGraphics();
g.drawRenderedImage(bi, at);
ByteArrayOutputStream imgBytes = new ByteArrayOutputStream();
ImageIO.write(img, "JPG", imgBytes);
stream.clear();
stream.setData(imgBytes.toByteArray(), false, PRStream.NO_COMPRESSION);
stream.put(PdfName.TYPE, PdfName.XOBJECT);
stream.put(PdfName.SUBTYPE, PdfName.IMAGE);
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);
}
reader.removeUnusedObjects();
// Save altered PDF
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
stamper.setFullCompression();
stamper.close();
reader.close();
}
}
减小使用 iText 新创建的 PDF 文件中 JPEG 图像大小的最佳做法是什么? (我的 objective 是图像质量和文件大小之间的权衡。)
图像创建如下:
Image image = new Image(ImageDataFactory.create(imagePath))
我想提供一个比例因子,例如 0.5
,它将一行中的像素数减半。
假设我用一个 3 MB 的图像生成一个 PDF。我尝试了 image.scale(0.5f, 0.5f)
,但生成的 PDF 文件仍然大约为 3 MB。我预计它会变得更小。
因此我猜嵌入在 PDF 文件中的源图像没有被触及。但这就是我需要的:存储在磁盘上的整个 PDF 文件的像素总数应该减少。
实现此目标的 easiest/recommended 方法是什么?
先缩放图片,再用iText打开缩放后的图片
ImageDataFactory 中有一个接受 AWT 图像的创建方法。先用AWT工具缩放图片,然后这样打开:
String imagePath = "C:\path\to\image.jpg";
java.awt.Image awtImage = ImageIO.read(new File(imagePath));
// scale image here
int scaledWidth = awtImage.getWidth(null) / 2;
int scaledHeight = awtImage.getHeight(null) / 2;
BufferedImage scaledAwtImage = new BufferedImage(scaledWidth, scaledHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g = scaledAwtImage.createGraphics();
g.drawImage(awtImage, 0, 0, scaledWidth, scaledHeight, null);
g.dispose();
/*
Optionally pick a color to replace with transparency.
Any pixels that match this color will be replaced by tansparency.
*/
Color bgColor = Color.WHITE;
Image itextImage = new Image(ImageDataFactory.create(scaledAwtImage, bgColor));
有关如何缩放图像的更好提示,请参阅 How can I resize an image using Java?
如果您在添加到 PDF 时仍然需要原始尺寸,只需重新缩放即可。
itextImage.scale(2f, 2f);
注意:此代码未经测试。
编辑 以回应关于赏金的评论
你让我思考和寻找。看来 iText 将导入 AWT 图像视为原始图像。我认为它对待它的方式与 BMP 相同,只是 writes the pixel data using /FlateDecode,这可能远未达到最佳状态。我能想到的满足您要求的唯一方法是使用 ImageIO 将缩放后的图像写入文件系统或将 ByteArrayOutputStream 作为 jpeg,然后使用结果 file/bytes 用 iText 打开。
这是一个使用字节数组的更新示例。如果您想更喜欢压缩级别等,refer here。
String imagePath = "C:\path\to\image.jpg";
java.awt.Image awtImage = ImageIO.read(new File(imagePath));
// scale image here
int scaledWidth = awtImage.getWidth(null) / 2;
int scaledHeight = awtImage.getHeight(null) / 2;
BufferedImage scaledAwtImage = new BufferedImage(scaledWidth, scaledHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g = scaledAwtImage.createGraphics();
g.drawImage(awtImage, 0, 0, scaledWidth, scaledHeight, null);
g.dispose();
ByteArrayOutputStream bout = new ByteArrayOutputStream()
ImageIO.write(scaledAwtImage, "jpeg", bout);
byte[] imageBytes = bout.toByteArray();
Image itextImage = new Image(ImageDataFactory.create(imageBytes));
此 documentations 中列出了一种方法,它使您可以压缩图像并减少磁盘上存储的整个 PDF 文件。希望对你有帮助。
以下为代码示例:
/*
* This example was written by Bruno Lowagie in answer to the following question:
*
*/
package sandbox.images;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.PRStream;
import com.itextpdf.text.pdf.PdfName;
import com.itextpdf.text.pdf.PdfNumber;
import com.itextpdf.text.pdf.PdfObject;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.parser.PdfImageObject;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.imageio.ImageIO;
import sandbox.WrapToTest;
/**
* @author Bruno Lowagie (iText Software)
*/
@WrapToTest
public class ReduceSize {
public static final String SRC = "resources/pdfs/single_image.pdf";
public static final String DEST = "results/images/single_image_reduced.pdf";
public static final float FACTOR = 0.5f;
public static void main(String[] args) throws DocumentException, IOException {
File file = new File(DEST);
file.getParentFile().mkdirs();
new ReduceSize().manipulatePdf(SRC, DEST);
}
public void manipulatePdf(String src, String dest) throws DocumentException, IOException {
PdfReader reader = new PdfReader(src);
int n = reader.getXrefSize();
PdfObject object;
PRStream stream;
// Look for image and manipulate image stream
for (int i = 0; i < n; i++) {
object = reader.getPdfObject(i);
if (object == null || !object.isStream())
continue;
stream = (PRStream)object;
if (!PdfName.IMAGE.equals(stream.getAsName(PdfName.SUBTYPE)))
continue;
if (!PdfName.DCTDECODE.equals(stream.getAsName(PdfName.FILTER)))
continue;
PdfImageObject image = new PdfImageObject(stream);
BufferedImage bi = image.getBufferedImage();
if (bi == null)
continue;
int width = (int)(bi.getWidth() * FACTOR);
int height = (int)(bi.getHeight() * FACTOR);
if (width <= 0 || height <= 0)
continue;
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
AffineTransform at = AffineTransform.getScaleInstance(FACTOR, FACTOR);
Graphics2D g = img.createGraphics();
g.drawRenderedImage(bi, at);
ByteArrayOutputStream imgBytes = new ByteArrayOutputStream();
ImageIO.write(img, "JPG", imgBytes);
stream.clear();
stream.setData(imgBytes.toByteArray(), false, PRStream.NO_COMPRESSION);
stream.put(PdfName.TYPE, PdfName.XOBJECT);
stream.put(PdfName.SUBTYPE, PdfName.IMAGE);
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);
}
reader.removeUnusedObjects();
// Save altered PDF
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
stamper.setFullCompression();
stamper.close();
reader.close();
}
}