首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

Java 使用 iText 為 PDF 新增書籤:從文字目錄自動產生完整實作

     編輯:Java 程式設計入門

需求背景

許多電子書 PDF 缺乏書籤(大綱),閱讀體驗不佳。本工具讀取純文字目錄(每行「標題 頁碼」,縮排代表層級),解析後注入既有 PDF,輸出含書籤的新檔案。

輸入文字格式範例

第1章 HTTP 概述 3
1.1 HTTP——因特網的多媒體信使 4
1.2 Web 客戶端和服務器 4
1.3 資源 5
1.3.1 媒體類型 6
1.3.2 URI 7
...
  • 行尾數字為目標頁碼(PDF 實際頁碼,通常比書面頁碼偏移)。
  • 標題前的數字編號(1.、1.1.、1.3.1.)決定書籤層級(點號數量 = 層級)。

核心類別 AddPdfOutLineFromTxt

主要流程

  1. createPdf(dest, source, reader, pattern):讀取源 PDF,依 pattern 決定保留舊書籤、捨棄或保留第一個。
  2. addBookmarks(bufRead, outlines, preOutline, preLevel):遞迴讀取每行,解析層級與頁碼,建立 HashMap 結構(iText SimpleBookmark 格式),維護父書籤堆疊處理層級跳轉。
  3. parseFormmattedText(line):切分標題與頁碼,計算點號數量為層級。
  4. parseBookmarkToHashMap(bookmark):產出 {Title, Action:"GoTo", Page:"N Fit"}。
  5. 最後 PdfStamper.setOutlines(outlines) 寫入新 PDF。

關鍵程式碼片段

// 遞迴建立書籤樹
private void addBookmarks(BufferedReader in, List<Map<String,Object>> outlines,
                          Map<String,Object> parent, int parentLevel) throws IOException {
    in.mark(1);
    String line = in.readLine();
    if (line == null) return;

    FormattedBookmark bm = parseFormmattedText(line);
    Map<String,Object> node = parseBookmarkToHashMap(bm);
    int level = bm.getLevel();

    if (level == parentLevel) {                 // 同層
        outlines.add(node);
        addBookmarks(in, outlines, node, level);
    } else if (level > parentLevel) {           // 子層
        List<Map<String,Object>> kids = new ArrayList<>();
        kids.add(node);
        parent.put("Kids", kids);
        parentStack.push(new OutlineInfo(parent, outlines, parentLevel));
        addBookmarks(in, kids, node, level);
    } else {                                    // 回到上層
        in.reset();
        OutlineInfo oi = parentStack.pop();
        addBookmarks(in, oi.getOutlines(), oi.getPreOutline(), oi.getPreLevel());
    }
}

// 解析單行
private FormattedBookmark parseFormmattedText(String line) {
    FormattedBookmark bm = new FormattedBookmark();
    int lastSpace = line.lastIndexOf(' ');
    String title = (lastSpace == -1) ? line : line.substring(0, lastSpace);
    String page = (lastSpace == -1) ? "1" : line.substring(lastSpace + 1);
    int level = title.split(" ")[0].split("\\.").length - 1; // 點號數 = 層級
    bm.setTitle(title); bm.setPage(page); bm.setLevel(level);
    return bm;
}

保留既有書籤策略

  • RESERVED_OLD_OUTLINE:合併舊書籤與新解析書籤。
  • RESERVED_FIRST_OUTLINE:僅保留第一個舊書籤(通常為書名)。
  • RESERVED_NONE:完全取代。

依賴 (Maven)

<dependency>
  <groupId>com.itextpdf</groupId>
  <artifactId>itextpdf</artifactId>
  <version>5.5.13.3</version>
</dependency>

使用範例

public class PdfBookmarkDemo {
    public static void main(String[] args) throws Exception {
        try (BufferedReader br = new BufferedReader(new FileReader("toc.txt"));
             FileOutputStream fos = new FileOutputStream("output_with_bookmarks.pdf")) {
            PdfReader reader = new PdfReader("input.pdf");
            PdfStamper stamper = new PdfStamper(reader, fos);
            new AddPdfOutLineFromTxt().createPdf("output_with_bookmarks.pdf",
                "input.pdf", br, AddBookmarkConstants.RESERVED_OLD_OUTLINE);
            stamper.close();
            reader.close();
        }
    }
}

注意事項

  • 頁碼對應:文字目錄頁碼需為 PDF 實際頁碼(封面、目錄頁常導致偏移),可先用 PDF 閱讀器確認。
  • 編碼:確保 BufferedReader 使用正確字元集(UTF-8/Big5)。
  • iText 7 版 API 不同,若使用新版請參考 PdfOutline 相關類別。
  • 加密 PDF 需先解密或提供密碼給 PdfReader。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved