あまりなさそうな状況ではありますが、せっかくプログラムを作ったので公開しておきます。
実装言語はJavaです。
必要なライブラリ
- itextpdf-5.5.0.jar、itext-asian.jar
- slf4j-api-1.7.7.jar
- logback-core-1.1.2.jar, logback-classic-1.1.2.jar
※slf4jとlogbackはログ出力に使っているだけなので、なくても良い。
コード
package com.lumiere_couleur.java.pdfwatermark; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.awt.image.Raster; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.Iterator; import javax.imageio.ImageIO; import javax.imageio.ImageReader; import javax.imageio.stream.ImageInputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.itextpdf.text.DocumentException; import com.itextpdf.text.pdf.PRStream; import com.itextpdf.text.pdf.PdfDictionary; 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; public class ReplaceImages { private static final Logger log = LoggerFactory .getLogger(ReplaceImages.class); public static float FACTOR = 1.0f; public static void main(String[] args) { if (args.length < 3) { System.err .println("Usage: java ReplaceImages input_pdf output_pdf watermark"); System.exit(1); } String input_pdf = args[0]; String output_pdf = args[1]; String watermark = args[2]; if (input_pdf == null || input_pdf.length() == 0 || !(new File(input_pdf).exists())) { System.err .println("Usage: java ReplaceImages input_pdf output_pdf watermark"); System.exit(1); } try { PdfReader reader = new PdfReader(new FileInputStream(input_pdf)); int n = reader.getXrefSize(); PdfObject object; PRStream stream; for (int i = 0; i < n; i++) { object = reader.getPdfObject(i); if (object == null || !object.isStream()) { continue; } PdfDictionary dic = (PdfDictionary) PdfReader .getPdfObject(object); if (dic == null) { continue; } PdfName type = (PdfName) PdfReader.getPdfObject(dic .get(PdfName.SUBTYPE)); if (!PdfName.IMAGE.equals(type)) { continue; } stream = (PRStream) object; PdfImageObject image = new PdfImageObject(stream); BufferedImage bi = null; try { bi = image.getBufferedImage(); } catch (javax.imageio.IIOException e) { // Unsupported Image Type // CMYK → RGBに変換 // http://stackoverflow.com/questions/2408613/problem-reading-jpeg-image-using-imageio-readfile-file byte[] imgbytes = image.getImageAsBytes(); if (imgbytes == null) { continue; } // Find a suitable ImageReader Iterator<ImageReader> readers = ImageIO .getImageReadersByFormatName("JPEG"); ImageReader imageReader = null; while (readers.hasNext()) { imageReader = (ImageReader) readers.next(); if (imageReader.canReadRaster()) { break; } } // Stream the image file (the original CMYK image) ImageInputStream input = ImageIO .createImageInputStream(new ByteArrayInputStream( imgbytes)); imageReader.setInput(input); // Read the image raster Raster raster = imageReader.readRaster(0, null); // Create a new RGB image bi = new BufferedImage(raster.getWidth(), raster.getHeight(), BufferedImage.TYPE_4BYTE_ABGR); // Fill the new image with the old raster bi.getRaster().setRect(raster); } if (bi == null) { continue; } int width = (int) (bi.getWidth() * FACTOR); int height = (int) (bi.getHeight() * FACTOR); BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); AffineTransform at = AffineTransform.getScaleInstance(FACTOR, FACTOR); Graphics2D g = img.createGraphics(); g.drawRenderedImage(bi, at); // 透かしを入れる g.setPaint(Color.WHITE); Font font = new Font("メイリオ", Font.PLAIN, 18); g.setFont(font); g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); String s = watermark; FontMetrics fm = g.getFontMetrics(); int x = img.getWidth() - fm.stringWidth(s) - 10; int y = img.getHeight() - 10; // 文字背景 g.setColor(new Color(1f, 1f, 1f, 0.5f)); int bgx = x - 10; int bgy = y - 10 - font.getSize() / 2 - 5; g.fillRect(bgx, bgy, img.getWidth() - bgx, img.getHeight() - bgy); // 文字 g.setColor(Color.BLACK); g.drawString(s, x, y); 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); } PdfStamper stamper = new PdfStamper(reader, new FileOutputStream( output_pdf)); stamper.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (DocumentException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } log.debug("done"); } }
あーつかれた。