/*
* 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;
}
}