こぶたのラッパ » 2014 年 4 月 9 日

Daily Archives: 2014 年 4 月 9 日

Java プログラミング

PDF内のすべての画像に、一気に透かし文字(ウォーターマーク)を入れる

Published by:

あまりなさそうな状況ではありますが、せっかくプログラムを作ったので公開しておきます。
実装言語はJavaです。

必要なライブラリ

※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");
    }
}

あーつかれた。