/*
 * Copyright infoTek K.K. 2018 All rights reserved.
 *
 * 引数の PDF からリンクの座標を取得し、インポートしたページ上に枠を描画して出力します。
 * Rotate と CropBox を考慮します。
 *
 * 動作に必要な製品: PDFlib+PDI 9.0
 *
 */
import java.io.File;
import java.util.Arrays;
import com.pdflib.pdflib;
import com.pdflib.PDFlibException;
public class make_pdf {
    // args[0] のページ内容にリンク枠を追加したものを "output_" + args[0] として出力する
    public static void main(String[] args) throws Exception {
        pdflib p;
        int doc, pages;
        String infile, outfile;
        // 入力ファイル名の取得と出力ファイル名を作成する
        if (args.length < 1) {
            System.out.println("引数に入力元の PDF を指定してください。");
            System.exit(1);
        }
        infile = args[0];
        outfile = "output_" + infile;
        // infile のページ数分だけ makePage() を呼び出しページを作成する
        p = new pdflib();
        p.begin_document(outfile, "");
        doc = p.open_pdi_document(infile, "");
        pages = (int) p.pcos_get_number(doc, "length:pages");
        for (int i = 0; i < pages; i++) {
            makePage(p, doc, i);
        }
        p.close_pdi_document(doc);
        p.end_document("");
        p.delete();
    }
    // 入力元の PDF から page_num + 1 ページ目をインポートし、リンク枠を描画する
    private static void makePage(pdflib p, int doc, int page_num) throws Exception {
        int page, annots, rotate;
        String path, obj_type;
        double[] mediabox, cropbox;
        p.begin_page_ext(0, 0, "");
        p.set_graphics_option("linewidth=2 strokecolor=red");
        // page_num + 1 ページ目を出力のページに貼り付ける
        // (ページの指定は 1 からなので + 1 する)
        page = p.open_pdi_page(doc, page_num + 1, "");
        p.fit_pdi_page(page, 0, 0, "adjustpage");
        p.close_pdi_page(page);
        path = "pages[" + page_num + "]";
        // ページの向きを取得
        rotate = (int) p.pcos_get_number(doc, path + "/Rotate");
        // MediaBox (ページサイズ) と CropBox (ページの表示範囲) を取得
        mediabox = getRect(p, doc, path + "/MediaBox");
        cropbox = getCropBox(p, doc, path + "/CropBox", mediabox);
        // page_num + 1 ページ目に含まれる Annotation の数を取得
        // (pages[] の添字は 0 からなので + 1 しない)
        annots = (int) p.pcos_get_number(doc, "length:pages[" + page_num + "]/annots");
        for (int i = 0; i < annots; i++) {
            double[] rect;
            double width, height;
            String annot_path = path + "/annots[" + i + "]";
            // リンク以外の注釈は無視する
            obj_type = p.pcos_get_string(doc, annot_path + "/Subtype");
            if (obj_type.equals("Link") == false) {
                continue;
            }
            // Rotate および CropBox を考慮した見かけ上のリンクの座標を取得する
            rect = getLinkRect(p, doc, annot_path + "/Rect", rotate, mediabox, cropbox);
            // 取得した座標の矩形パスをページ上に作成する
            width = rect[2] - rect[0];
            height = rect[3] - rect[1];
            p.rect(rect[0], rect[1], width, height);
            p.stroke();
        }
        p.end_page_ext("");
    }
    // path の矩形領域の座標を配列で返す
    private static double[] getRect(pdflib p, int doc, String path) throws Exception {
        double[] rect = new double[4];
        for (int i = 0; i < 4; i++) {
            rect[i] = p.pcos_get_number(doc, path + "[" + i + "]");
        }
        return rect;
    }
    // MediaBox も考慮しながら CropBox を取得する
    private static double[] getCropBox(pdflib p, int doc, String path,
                                       double[] mediabox) throws Exception {
        double[] cropbox;
        String obj_type;
        obj_type = p.pcos_get_string(doc, "type:" + path);
        if (obj_type.equals("null")) {
            // CropBox が存在しない場合、MediaBox の座標を使う
            cropbox = Arrays.copyOf(mediabox, mediabox.length);
        } else {
            cropbox = getRect(p, doc, path);
            // CropBox が MediaBox の範囲を超えた場合は MediaBox の座標を使う
            for (int i = 0; i < 2; i++) {  // 左下の座標
                cropbox[i] = (mediabox[i] > cropbox[i]) ? mediabox[i] : cropbox[i];
            }
            for (int i = 2; i < 4; i++) {  // 右上の座標
                cropbox[i] = (mediabox[i] < cropbox[i]) ? mediabox[i] : cropbox[i];
            }
        }
        return cropbox;
    }
    // リンクの座標を Rotate と CropBox を考慮した座標に変換した配列として返す
    private static double[] getLinkRect(pdflib p, int doc,
                                        String path, int rotate,
                                        double[] mediabox,
                                        double[] cropbox) throws Exception {
        double[] annot_rect;
        // リンクの座標を取得
        annot_rect = getRect(p, doc, path);
        // Rotate に応じてリンクの座標を回転させる
        annot_rect = rotateRect(annot_rect, rotate, mediabox);
        // Rotate に応じて CropBox の座標も回転させる
        cropbox = rotateRect(cropbox, rotate, mediabox);
        // CropBox の左下の座標が (0, 0) になるように Rect の座標を移動させる
        annot_rect = moveRect(annot_rect, cropbox);
        return annot_rect;
    }
    // rotate の値に応じて座標を変換します
    private static double[] rotateRect(double[] rect, int rotate,
                                       double[] mediabox) throws Exception {
        double[] ret;
        if (rotate == 0 || rotate % 360 == 0) {
            return rect;
        }
        ret = new double[4];
        if (rotate % 270 == 0) {
            ret[0] = mediabox[3] - rect[3];
            ret[1] = rect[0];
            ret[2] = mediabox[3] - rect[1];
            ret[3] = rect[2];
        } else if (rotate % 180 == 0) {
            ret[0] = mediabox[2] - rect[2];
            ret[1] = mediabox[3] - rect[3];
            ret[2] = mediabox[2] - rect[0];
            ret[3] = mediabox[3] - rect[1];
        } else if (rotate % 90 == 0) {
            ret[0] = rect[1];
            ret[1] = mediabox[2] - rect[2];
            ret[2] = rect[3];
            ret[3] = mediabox[2] - rect[0];
        } else {
            // PDF の仕様上、90 の倍数以外の値はとれません
            throw new Exception("Specification error (Rotate).");
        }
        return ret;
    }
    // cropbox の左下の座標が (0, 0) になるように rect の座標を移動します
    private static double[] moveRect(double[] rect, double[] cropbox) {
        double[] ret = new double[4];
        ret[0] = rect[0] - cropbox[0];
        ret[1] = rect[1] - cropbox[1];
        ret[2] = rect[2] - cropbox[0];
        ret[3] = rect[3] - cropbox[1];
        return ret;
    }
}