使用java为pdf添加书签的方法(pdf书签制作)
发布时间:2022-03-07 14:59:11
由于我经常下载一些pdf格式的电子书,有的时候一些好书下载下来没有书签,读起来感觉没有整体的感觉,所以决定自己写一个小工具,将特定格式的文本解析成为书签,然后保存到pdf格式中。
整体思路是从豆瓣啊、京东啊、当当啊、亚马逊下面的介绍中可以copy出目录信息,拿《HTTP权威指南》为例:
目录的结构如:
第1章 HTTP 概述 3
1.1 HTTP——因特网的多媒体信使 4
1.2 Web 客户端和服务器 4
1.3 资源 5
1.3.1 媒体类型 6
1.3.2 URI 7
1.3.3 URL 7
1.3.4 URN 8
1.4 事务 9
1.4.1 方法 9
1.4.2 状态码 10
1.4.3 Web 页面中可以包含多个对象 10
1.5 报文 11
1.6 连接 13
主要的逻辑为:
package org.fra.pdf.bussiness;
import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Stack;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.IntHashtable;
import com.itextpdf.text.pdf.PdfArray;
import com.itextpdf.text.pdf.PdfDictionary;
import com.itextpdf.text.pdf.PdfIndirectReference;
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.PdfString;
import com.itextpdf.text.pdf.SimpleBookmark;
public class AddPdfOutLineFromTxt {
private Stack<OutlineInfo> parentOutlineStack = new Stack<OutlineInfo>();
public void createPdf(String destPdf, String sourcePdf,
BufferedReader bufRead, int pattern) throws IOException,
DocumentException {
if (pattern != AddBookmarkConstants.RESERVED_OLD_OUTLINE
&& pattern != AddBookmarkConstants.RESERVED_NONE
&& pattern != AddBookmarkConstants.RESERVED_FIRST_OUTLINE)
return;
// 读入pdf文件
PdfReader reader = new PdfReader(sourcePdf);
List<HashMap<String, Object>> outlines = new ArrayList<HashMap<String, Object>>();
if (pattern == AddBookmarkConstants.RESERVED_OLD_OUTLINE) {
outlines.addAll(SimpleBookmark.getBookmark(reader));
} else if (pattern == AddBookmarkConstants.RESERVED_FIRST_OUTLINE) {
addFirstOutlineReservedPdf(outlines, reader);
}
addBookmarks(bufRead, outlines, null, 0);
// 新建stamper
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(
destPdf));
stamper.setOutlines(outlines);
stamper.close();
}
private void addBookmarks(BufferedReader bufRead,
List<HashMap<String, Object>> outlines,
HashMap<String, Object> preOutline, int preLevel)
throws IOException {
String contentFormatLine = null;
bufRead.mark(1);
if ((contentFormatLine = bufRead.readLine()) != null) {
FormattedBookmark bookmark = parseFormmattedText(contentFormatLine);
HashMap<String, Object> map = parseBookmarkToHashMap(bookmark);
int level = bookmark.getLevel();
// 如果n==m, 那么是同一层的,这个时候,就加到ArrayList中,继续往下面读取
if (level == preLevel) {
outlines.add(map);
addBookmarks(bufRead, outlines, map, level);
}
// 如果n>m,那么可以肯定,该行是上一行的孩子,, new 一个kids的arraylist,并且加入到这个arraylist中
else if (level > preLevel) {
List<HashMap<String, Object>> kids = new ArrayList<HashMap<String, Object>>();
kids.add(map);
preOutline.put("Kids", kids);
// 记录有孩子的outline信息
parentOutlineStack.push(new OutlineInfo(preOutline, outlines,
preLevel));
addBookmarks(bufRead, kids, map, level);
}
// 如果n<m , 那么就是说孩子增加完了,退回到上层,bufRead倒退一行
else if (level < preLevel) {
bufRead.reset();
OutlineInfo obj = parentOutlineStack.pop();
addBookmarks(bufRead, obj.getOutlines(), obj.getPreOutline(),
obj.getPreLevel());
}
}
}
private HashMap<String, Object> parseBookmarkToHashMap(
FormattedBookmark bookmark) {
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("Title", bookmark.getTitle());
map.put("Action", "GoTo");
map.put("Page", bookmark.getPage() + " Fit");
return map;
}
private FormattedBookmark parseFormmattedText(String contentFormatLine) {
FormattedBookmark bookmark = new FormattedBookmark();
String title = "";
String destPage = "";
// 当没有页码在字符串结尾的时候,一般就是书的名字,如果格式正确的话。
int lastSpaceIndex = contentFormatLine.lastIndexOf(" ");
if (lastSpaceIndex == -1) {
title = contentFormatLine;
destPage = "1";
} else {
title = contentFormatLine.substring(0, lastSpaceIndex);
destPage = contentFormatLine.substring(lastSpaceIndex + 1);
}
String[] titleSplit = title.split(" ");
int dotCount = titleSplit[0].split("\\.").length - 1;
bookmark.setLevel(dotCount);
bookmark.setPage(destPage);
bookmark.setTitle(title);
return bookmark;
}
private void addFirstOutlineReservedPdf(
List<HashMap<String, Object>> outlines, PdfReader reader) {
PdfDictionary catalog = reader.getCatalog();
PdfObject obj = PdfReader.getPdfObjectRelease(catalog
.get(PdfName.OUTLINES));
// 没有书签
if (obj == null || !obj.isDictionary())
return;
PdfDictionary outlinesDictionary = (PdfDictionary) obj;
// 得到第一个书签
PdfDictionary firstOutline = (PdfDictionary) PdfReader
.getPdfObjectRelease(outlinesDictionary.get(PdfName.FIRST));
PdfString titleObj = firstOutline.getAsString((PdfName.TITLE));
String title = titleObj.toUnicodeString();
PdfArray dest = firstOutline.getAsArray(PdfName.DEST);
if (dest == null) {
PdfDictionary action = (PdfDictionary) PdfReader
.getPdfObjectRelease(firstOutline.get(PdfName.A));
if (action != null) {
if (PdfName.GOTO.equals(PdfReader.getPdfObjectRelease(action
.get(PdfName.S)))) {
dest = (PdfArray) PdfReader.getPdfObjectRelease(action
.get(PdfName.D));
}
}
}
String destStr = parseDestString(dest, reader);
String[] decodeStr = destStr.split(" ");
int num = Integer.valueOf(decodeStr[0]);
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("Title", title);
map.put("Action", "GoTo");
map.put("Page", num + " Fit");
outlines.add(map);
}
private String parseDestString(PdfArray dest, PdfReader reader) {
String destStr = "";
if (dest.isString()) {
destStr = dest.toString();
} else if (dest.isName()) {
destStr = PdfName.decodeName(dest.toString());
} else if (dest.isArray()) {
IntHashtable pages = new IntHashtable();
int numPages = reader.getNumberOfPages();
for (int k = 1; k <= numPages; ++k) {
pages.put(reader.getPageOrigRef(k).getNumber(), k);
reader.releasePage(k);
}
destStr = makeBookmarkParam((PdfArray) dest, pages);
}
return destStr;
}
private String makeBookmarkParam(PdfArray dest, IntHashtable pages) {
StringBuffer s = new StringBuffer();
PdfObject obj = dest.getPdfObject(0);
if (obj.isNumber()) {
s.append(((PdfNumber) obj).intValue() + 1);
} else {
s.append(pages.get(getNumber((PdfIndirectReference) obj)));
}
s.append(' ').append(dest.getPdfObject(1).toString().substring(1));
for (int k = 2; k < dest.size(); ++k) {
s.append(' ').append(dest.getPdfObject(k).toString());
}
return s.toString();
}
private int getNumber(PdfIndirectReference indirect) {
PdfDictionary pdfObj = (PdfDictionary) PdfReader
.getPdfObjectRelease(indirect);
if (pdfObj.contains(PdfName.TYPE)
&& pdfObj.get(PdfName.TYPE).equals(PdfName.PAGES)
&& pdfObj.contains(PdfName.KIDS)) {
PdfArray kids = (PdfArray) pdfObj.get(PdfName.KIDS);
indirect = (PdfIndirectReference) kids.getPdfObject(0);
}
return indirect.getNumber();
}
}


猜你喜欢
- 当一个产品或者项目由大量独立模块组成时,想要从 Git 挨个下载下来导入 IDE 查看并不容易,此时可以结合使用 Git 和 Maven 的
- public interface ICacheStrategy { &
- 通过zookeeper实现分布式锁1、创建zookeeper的client首先通过CuratorFrameworkFactory创建一个连接
- 以前看到过个:Java开发手册(阿里巴巴-公开版),这是个pdf文档,里面描述了一些Java开发的规约,里面确实有很多好用的规约,要是在学校
- 本文实例讲述了Java Web实现session过期后自动跳转到登陆页功能。分享给大家供大家参考,具体如下:通过过滤器的方式实现 sessi
- 简介我们在前面的Android教程中已经提到过这么一件事:Android在启动后会有一个主线程。它不允许任何子线程去改变主UI线程里的内容。
- 本文以实例讲述了android中DatePicker和TimePicker的使用方法,具体步骤如下:下面是实现具体功能的代码,其中main.
- 一、在 AndroidManifest.xml文件中配置Widgets:<manifest xmlns:android="h
- 一个非侵入的api编译、收集、Rest文档生成工具。工具通过分析代码和注释,获取文档信息,生成RestDoc文档前言程序员一直以来都有一个烦
- 本文介绍了idea 创建 maven web 工程流程(图文教程),分享给大家,具体如下:1创建一个project工作区间;2.创建一个ma
- 前言本文给你提供在Spring Boot 应用程序中编写好的单元测试的机制,并且深入技术细节。我们将带你学习如何以可测试的方式创建Sprin
- ManualResetEvent表示线程同步事件,可以对所有进行等待的线程进行统一管理(收到信号时必须手动重置该事件)其构造函数为:publ
- 访问修饰符都知道是什么,但是在这之前没有深入的去研究和探索,每天都接触的东西应该清楚才可以。最基础的三个访问修饰符:public 、priv
- 一、概述UDP和TCP是网络通讯常用的两个传输协议,C#一般可以通过Socket来实现UDP和TCP通讯,由于.NET框架通过UdpClie
- 前言继续动画探索,今天用Flutter制作一个心碎的感觉,灵感来源于今天的股市,哎,心哇凉哇凉的。废话不多说,开始。效果图先上:实现步骤1、
- Android在自定义控件时,经常需要获得屏幕的宽高,每次都要写,不妨直接把他封装成工具类,直接拿来用,废话不说,直接上代码/** * &n
- 前言项目中检测人脸图片是否合法的功能,之前用的是百度的人脸识别接口,由于成本高昂不得不寻求替代方案。什么是opencv?OpenCV是一个基
- 本文告诉大家如何使用相同权限调用cmd并且传入命令。如果想要用相同的权限运行一个程序,可以使用 ProcessStartInfo 的方法&n
- Spring中有很多继承于aware中的接口,这些接口到底是做什么用到的。aware,翻译过来是知道的,已感知的,意识到的,所以这些接口从字
- Java NIO读取大文件已经不是什么新鲜事了,但根据网上示例写出的代码来处理具体的业务总会出现一些奇怪的Bug。针对这种情况,我总结了一些