PDFlib+PDI には「リンクをコピーする」機能はありませんが、PDFlib+PDI に内蔵されている pCOS インターフェースを使って既存の PDF ファイルからリンク注釈を取得し、PDFlib の機能で新規 PDF ファイルに注釈を作成することで、リンクのコピーを実現します。
リンクには様々な機能がありますのが、今回対象とするリンクは以下の機能をもつものです。
また文書内の移動には、「ページ番号を使用」する方法と「名前付き移動先を利用」する方法がありますが、今回はどちらも実装しています。
基本的な処理の流れは下記のとおりです。(今回は、Java を使用して記述しています)
/*
* リンク注釈のコピー
*
* 2021 Copyright (c) infoTek K.K. all rights reserved.
*
* 必要な製品:PDFlib+PDI/PPS 9
*/
import com.pdflib.PDFlibException;
import com.pdflib.pdflib;
class linkannots {
public static void main(String[] args) {
pdflib p = null;
String optlist;
try {
/* PDF オブジェクトを生成 */
p = new pdflib();
p.set_option("errorpolicy=return");
/* 新規PDF文書を開く */
if (p.begin_document("new_linkannots.pdf", "") == -1) {
throw new Exception("Error: " + p.get_errmsg());
}
/* 文書情報を設定 */
p.set_info("Creator", "annotation.java");
p.set_info("Author", "infoTek");
p.set_info("Title", "リンクのコピー");
/* 既存 PDF 文書を開く */
int doc = p.open_pdi_document("linkannots.pdf", "");
if (doc == -1) {
throw new Exception("Error: " + p.get_errmsg());
}
/* 既存 PDF 文書のぺージ数を pCOS 関数で取得する */
int endpage = (int)p.pcos_get_number(doc, "length:pages");
/* 既存 PDF 文書が複数ページがある場合、必要分繰り返す */
for (int pageno = 1; pageno <= endpage; pageno++) {
/* 既存 PDF ページを開く */
int page = p.open_pdi_page(doc, pageno, "");
if (page == -1) {
throw new Exception("Error: " + p.get_errmsg());
}
/* 新規 PDF ページをダミーのページサイズで作成する。 */
p.begin_page_ext(10, 10, "");
/* 新規 PDF ページ上に既存 PDF ページをインポートし、サイズを調整する */
p.fit_pdi_page(page, 0, 0, "adjustpage");
/* ページ上のリンク注釈をコピーする */
copy_linkannots(p, doc, pageno);
/* 既存 PDF ページを終了する */
p.close_pdi_page(page);
/* 新規 PDF ページを終了する*/
p.end_page_ext("");
}
/* 既存 PDF 文書を終了する */
p.close_pdi_document(doc);
/* 新規 PDF 文書を終了する */
p.end_document("");
}
catch (PDFlibException e) {
System.err.println("PDFlib exception occurred in hello sample:");
System.err.println("[" + e.get_errnum() + "] " + e.get_apiname() + ": " + e.get_errmsg());
}
catch (Exception e) {
System.err.println(e);
}
finally {
if (p != null) {
p.delete();
}
}
}
public static void copy_linkannots(pdflib p, int doc, int pageno) throws PDFlibException {
/* pageno ページ上の「注釈」の数を pCOS 関数で取得する */
String pagepath = "pages[" + (pageno - 1) + "]";
int annotscount = (int)p.pcos_get_number(doc, "length:" + pagepath + "/annots");
if (annotscount == 0) {
return;
}
/* 既存 PDF 文書内の「注釈」分だけ繰り返す */
for (int i = 0; i < annotscount; i++) {
String path = pagepath + "/annots[" + i + "]";
/* 注釈のうち、Subtype が "Link" のもののみを対象にする */
if (!(p.pcos_get_string(doc, path + "/Subtype").equals("Link"))) {
continue;
}
String optlist;
/* リンク先の取得
* リンク注釈は Action (/A) または Dest (/Dest) のいずれかを持っており、
* /A エントリがある場合は Action を実行し、ない場合は /Dest エントリの内容に移動する
*/
double pcosid = p.pcos_get_number(doc, "pcosid:" + path + "/A");
if (pcosid != -1) {
/* Action を実行
* 本サンプルでは GoTo, URI のみをサポートする
*/
int action;
switch (p.pcos_get_string(doc, path + "/A/S")) {
case "GoTo":
/* 同文書内に移動するアクションを実行する
* Dest パスは "/A/D"
*/
optlist = create_dest(p, doc, path, "/A/D");
action = p.create_action("GoTo", optlist);
optlist = "action={activate={" + action + "}}";
break;
case "URI":
// ウェブページへの移動
String url = p.pcos_get_string(doc, path + "/A/URI");
optlist = "url={" + url + "}";
action = p.create_action("URI", optlist);
optlist = "action={activate={" + action + "}}";
break;
default:
// 本サンプルでは扱わない
continue;
}
} else {
/* Dest 先に移動する
* Dest パスは "/Dest"
*/
optlist = create_dest(p, doc, path, "/Dest");
}
/* リンクの座標を取得 */
double llx = p.pcos_get_number(doc, path + "/Rect[0]");
double lly = p.pcos_get_number(doc, path + "/Rect[1]");
double urx = p.pcos_get_number(doc, path + "/Rect[2]");
double ury = p.pcos_get_number(doc, path + "/Rect[3]");
/* Link 注釈を作成する */
optlist += " linewidth={0}";
p.create_annotation(llx, lly, urx, ury, "Link", optlist);
}
}
public static String create_dest(pdflib p, int doc, String path, String subpath) throws PDFlibException {
// 注釈の移動先ページを取得
int destpage = (int) p.pcos_get_number(doc, path + "/destpage");
String optlist, destoptlist = "";
/* 移動先の型ごとに処理を分ける
* 配列("array") の場合、指定された移動先に移動する
* 文字列("string") の場合、指定された名前付き移動先を作成して移動する
*/
switch (p.pcos_get_string(doc, "type:" + path + subpath)) {
/* 指定された移動先に移動する場合 */
case "array":
// 指定された移動先を optlist で再現
destoptlist = parse_dest(p, doc, path + subpath);
destoptlist += " page={" + destpage + "}";
optlist = "destination={" + destoptlist + "}";
break;
/* 指定された名前付き移動先を作成して移動する場合 */
case "string":
/* 移動先名と、該当の移動先情報を取得 */
String destpath = "";
String destname = p.pcos_get_string(doc, path + subpath);
int destcount = (int) p.pcos_get_number(doc, "length:names/Dests");
for (int i = 0; i < destcount; i++) {
String names_key = p.pcos_get_string(doc, "names/Dests[" + i + "].key");
if (destname.equals(names_key)) {
destpath = "names/Dests[" + i + "]/D";
break;
}
}
/* 名前付き移動先を、新規 PDF 文書のページ上に作成する */
destoptlist = parse_dest(p, doc, destpath);
destoptlist += " page={" + destpage + "}";
p.add_nameddest(destname, destoptlist);
// 作成した名前付き移動先に移動するよう設定
optlist = "destname={" + destname + "}";
break;
default:
optlist = "destination={page={" + destpage + "}}";
}
return optlist;
}
public static String parse_dest(pdflib p, int doc, String path) throws PDFlibException {
String optlist, type;
double top, bottom, right, left, zoom;
/* 移動先配列を解析し、PDFlib で必要なオプションに変換する
*
* [0] はページオブジェクトへの参照ですが、今回は使用しない
* [1] はページの表示方法を表す文字列で、これにより配列のサイズが異なる
* ・XYZ: left, top, zoom に従った固定されたページ表示 ([page /XYZ left top zoom])
* ・Fit: ページ全体が入るように表示 ([page /Fit])
* ・FitH: ページ幅に合わせて表示 ([page /FitH top])
* ・FitV: ページ高に合わせて表示 ([page /FitV left])
* ・FitR: 指定された四角形に合わせて表示 ([page /FitR left bottom right top])
* ・FitB: ページ領域 (ArtBox) に合わせて表示 ([page /FitB])
* ・FitBH: ページ領域 (ArtBox) の高さに合わせて表示 ([page /FitBH top])
* ・FitBW: ページ領域 (ArtBox) の幅に合わせて表示 ([page /FitBW left])
*
* 画面の表示方法によって、画面表示の y 座標を格納しているプロパティが異なり、
* また、destinationに設定する移動先オプションが異なるので、処理を分岐する
*/
/* 画面の表示方法を取得・分岐 */
switch (p.pcos_get_string(doc, path + "[1]")) {
case "XYZ":
optlist = "type={fixed}";
type = p.pcos_get_string(doc, "type:" + path + "[2]");
if (type.equals("number")) {
left = p.pcos_get_number(doc, path + "[2]");
if (left < 0) left = 0.0;
optlist += " left={" + left + "}";
}
type = p.pcos_get_string(doc, "type:" + path + "[3]");
if (type.equals("number")) {
top = p.pcos_get_number(doc, path + "[3]");
if (top < 0) top = 0.0;
optlist += " top={" + top + "}";
}
type = p.pcos_get_string(doc, "type:" + path + "[4]");
if (type.equals("number")) {
zoom = p.pcos_get_number(doc, path + "[4]");
optlist += " zoom={" + zoom + "}";
}
break;
case "Fit":
optlist = "type={fitwindow}";
break;
case "FitH":
optlist = "type={fitwidth}";
type = p.pcos_get_string(doc, "type:" + path + "[2]");
if (type.equals("number")) {
top = p.pcos_get_number(doc, path + "[2]");
if (top < 0) top = 0.0;
optlist += " top={" + top + "}";
}
break;
case "FitV":
optlist = "type={fitheight}";
type = p.pcos_get_string(doc, "type:" + path + "[2]");
if (type.equals("number")) {
left = p.pcos_get_number(doc, path + "[2]");
if (left < 0) left = 0.0;
optlist += " top={" + left + "}";
}
break;
case "FitR":
optlist = "type={fitrect}";
type = p.pcos_get_string(doc, "type:" + path + "[2]");
if (type.equals("number")) {
left = p.pcos_get_number(doc, path + "[2]");
if (left < 0) left = 0.0;
optlist += " left={" + left + "}";
}
type = p.pcos_get_string(doc, "type:" + path + "[3]");
if (type.equals("number")) {
bottom = p.pcos_get_number(doc, path + "[3]");
if (bottom < 0) bottom = 0.0;
optlist += " bottom={" + bottom + "}";
}
type = p.pcos_get_string(doc, "type:" + path + "[4]");
if (type.equals("number")) {
right = p.pcos_get_number(doc, path + "[4]");
if (right < 0) right = 0.0;
optlist += " right={" + right + "}";
}
type = p.pcos_get_string(doc, "type:" + path + "[5]");
if (type.equals("number")) {
top = p.pcos_get_number(doc, path + "[5]");
if (top < 0) top = 0.0;
optlist += " top={" + top + "}";
}
break;
case "FitB":
optlist = "type={fitvisible}";
break;
case "FitBH":
optlist = "type={fitvisiblewidth}";
type = p.pcos_get_string(doc, "type:" + path + "[2]");
if (type.equals("number")) {
top = p.pcos_get_number(doc, path + "[2]");
if (top < 0) top = 0.0;
optlist += " top={" + top + "}";
}
break;
case "FitBW":
optlist = "type={fitvisibleheight}";
type = p.pcos_get_string(doc, "type:" + path + "[2]");
if (type.equals("number")) {
left = p.pcos_get_number(doc, path + "[2]");
if (left < 0) left = 0.0;
optlist += " left={" + left + "}";
}
break;
default:
optlist = "";
}
return optlist;
}
}