代码具有很强的局限性,并不适合所有的电子书目录。代码生成目录主要是针对于 《啊哈!算法》,其他的目录结构可能生成结果不符合预期。

有些时候我们下载的电子书没有书签,虽然不影响阅读,但是使用体验很差,尤其是需要跳转时。因为之前在项目中也是用过itext用于生成企业的信用报告(图片,目录,水印,锚点),所以相对来说比较熟悉些,看过官网的文档也知道可以实现。然后就在网上找了些demo试着使用itext生成书签,解放双手。

准备

该版本比较简单,局限性特别大,只能识别特定的简单目录结构。

引入依赖
<dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.11</version>
</dependency>
获取PDF文档的目录

在当当上或者京东等网站上找到对应的书籍,在商品介绍里面一般都会提供书籍的目录信息。复制目录,并对目录做一些小的处理。

  1. 复制的目录中的空格是中文空格,需要将所有的中文空格修改为英文空格。当然也可以使用中文空格(代码需要修改)
  2. 为了操作方便,删除了“目录”文本部分。当然也可以对目录页也添加对应的页码关系
  3. 需要手动的找出文档正文的起始页码。 比如啊哈!算法的正文是第13页。
  4. 如果想要把目录也放在文件中,需要进行文本转码(utf-8)。Windows记事本的转码的utf8文件实际上是utf-8-bom ,这种格式是不对的。

啊哈!算法。 文本中带目录对应页码信息的的文本。注意需要手动的找出目录对应的实际页数

目录 11
第1章 一大波数正在靠近——排序 1第1节 最快最简单的排序——桶排序 2第2节 邻居好说话——冒泡排序 7第3节 最常用的排序——快速排序 12第4节 小哼买书 20..............................

spring源码解析的目录

第一部分 核心实现第1章 Spring整体架构和环境搭建 2
1.1 Spring的整体架构 2
1.2 环境搭建 4
1.2.1 安装GitHub 4
1.2.2 安装Gradle 5
1.2.3 下载Spring 6第2章 容器的基本实现 10
2.1 容器基本用法 10
2.2 功能分析 11
2.3 工程搭建 12
。。。。。。。。。。.。。。。

代码针对了这两种格式的目录文件(嗯,第二种没有测试,可能有问题)。

生成书签

下面以啊哈!算法的书签生成为例

手动的找到正文的实际页码,即“第1章 一大波数正在靠近——排序 1 ”是第13页。

代码

注意需要对目录文件进行utf8转码(utf8-no-bom),utf8转码,utf8转码,utf8转码,utf8转码,可以使用editplus等文本编辑工具来修改编码。Windows记事本的转码是utf-8-with-bom。

不进行转码的话,书签会生成失败。因为下面代码永不执行。因为使用IDE工具会发现,
sectionNumberStr实际上等于"\ufeff目录"。这是因为Microsoft 建议所有的 Unicode 文件应该以 \uFEFF 字符开头,作为标记字节顺序存储的标记。标准的utf8编码是不需要设置 这个字节顺序标记。

else if("目录".equals(sectionNumberStr)){}

该方式需要传入目录对应的实际页码。

目录 11
第1章 一大波数正在靠近——排序 1第1节 最快最简单的排序——桶排序 2第2节 邻居好说话——冒泡排序 7第3节 最常用的排序——快速排序 12第4节 小哼买书 20
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.*;
import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;public class GenerateBookmarkUtil {private Pattern spacePattern = Pattern.compile("\\s*");public static void main(String[] args) {GenerateBookmarkUtil zcc = new GenerateBookmarkUtil();String sourceDocPath = "E:\\BaiduNetdiskDownload\\啊哈!算法.pdf"; //电子书路径String sourceTextPath = "C:\\Users\\Administrator\\Desktop\\aha.txt";//生成的目录文本路径String desFilename = "D:\\testhaha1.pdf"; // 生成的带有目录的文件的路径(问价是复制,不是覆盖)try {zcc.createPdf(sourceTextPath, sourceDocPath, desFilename,13);} catch (DocumentException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}/***@param sTxtPath 书签文件的路径*@param sPdfPath 需要生成书签的文件*@param filename  生成书签后的文件路径*@param startPage  正文的实际页数-1*/public void createPdf(String sTxtPath, String sPdfPath, String filename,int startPage) throws DocumentException, IOException {if(startPage>0)startpage--;//可以创建新的文件,也可以直接操作源文件,添加书签// step 1Document document = new Document();// step 2 输出文件PdfCopy writer = new PdfCopy(document, new FileOutputStream(filename)); // step 3writer.setViewerPreferences(PdfWriter.PageModeUseOutlines);//设置打开pdf文件时显示书签document.open();// step 4 逐页读入pdf文件并写入输出文件PdfReader reader = new PdfReader(sPdfPath);int n = reader.getNumberOfPages();for (int page = 1; page <= n; page++) {writer.addPage(writer.getImportedPage(reader, page));}writer.freeReader(reader);// step 5 添加书签PdfOutline root = writer.getRootOutline();PdfAction action;//标识书签点击后的跳转动作,通过它设置跳转的页码try {//读入保存书签的TXT文件,分拆为书签名及跳转页码;BufferedReader bufRead = new BufferedReader(new FileReader(sTxtPath));String str;String[] ss = null;int skipPage = startPage; //正文书签跳过的页数/*数据的存储结构** page 页码*title 标题* kids 子书签   page,title ,kids*/List<Map<String, Object>> outlines = new ArrayList<>();//存放解析的数据String pageNumStr = null;int pageNum = 0;//遍历读取目录文件,每次读取一行,并解析出该行的标题和页码值 while ((str = bufRead.readLine()) != null) {String title = "";int parentPage = 0;String childTitle = "";int childPage = 0;str = str.trim();//过滤空行Matcher matcher = spacePattern.matcher(str);if (matcher.matches()) {continue;}//获取页码ss = str.split(" ");pageNumStr = ss[ss.length - 1].trim();//不包含页码的标题for (int j = 0; j < ss.length - 1; j++) {title += ss[j] + "  ";}if (pageNumStr.matches("\\d+")) {//存在页码try {pageNum = Integer.valueOf(pageNumStr);pageNum += skipPage;} catch (Exception e) {}String category="目录";String sectionNumberStr = ss[0]; //获取标题的序号if (sectionNumberStr.contains("章")) {//章节List<Map<String, Object>> kids = null;kids = new ArrayList();Map<String, Object> chapterMap = new HashMap<>();chapterMap.put("title", title);chapterMap.put("page", pageNum);chapterMap.put("kids", kids);outlines.add(chapterMap);}else if("目录".equals(sectionNumberStr)){//目录书签new PdfOutline(root,PdfAction.gotoLocalPage(pageNum-skipPage, new PdfDestination(PdfDestination.FIT), writer), "目   录");}else {//小节List<Map<String, Object>> kids = (List<Map<String, Object>>) outlines.get(outlines.size() - 1).get("kids");//将小节添追加到自书签列表Map<String, Object> littleChapter = new HashMap<>();littleChapter.put("title", title);littleChapter.put("page", pageNum);littleChapter.put("kids", new ArrayList<>());kids.add(littleChapter);}} else {//不存在页码,可能是大标题 或者目录   例如:第一部分 核心实现Map<String, Object> bigSection = new HashMap<>();bigSection.put("title", title);bigSection.put("page", -1);bigSection.put("kids", new ArrayList<>());outlines.add(bigSection);}}//数据遍历,添加书签outlines.forEach(map -> {String sectionTitle = (String) map.get("title");int page = (int) map.get("page");List<Map<String, Object>> kids = (List<Map<String, Object>>) map.get("kids");PdfOutline sectionOutline = null;if (page < 0) {//该部分是大类别int parentPage = (int) kids.get(0).get("page") - 1; //获取大分类的页数,一般就是子目录页数减一PdfAction action1 = PdfAction.gotoLocalPage(parentPage,new PdfDestination(PdfDestination.FIT), writer);//设置书签动作sectionOutline = new PdfOutline(root, action1, sectionTitle, false); //大分类的} else {PdfAction action1 = PdfAction.gotoLocalPage(page,new PdfDestination(PdfDestination.FIT), writer);//设置书签动作sectionOutline = new PdfOutline(root, action1, sectionTitle, false); //一级章节标题;}for (Map<String, Object> kid : kids) { //一级章节String firstTitle = (String) kid.get("title");int firstPage = (int) kid.get("page");List<Map<String, Object>> firstKids = (List<Map<String, Object>>) kid.get("kids");PdfAction action2 = PdfAction.gotoLocalPage(firstPage,new PdfDestination(PdfDestination.FIT), writer);//设置书签动作PdfOutline firstOutline = new PdfOutline(sectionOutline, action2, firstTitle, false); //一级标题的outlinefor (Map<String, Object> secondKid : firstKids) {//二级章节String secondTitle = (String) kid.get("title");int secondPage = (int) kid.get("page");List<Map<String, Object>> secondKids = (List<Map<String, Object>>) kid.get("kids");PdfAction action3 = PdfAction.gotoLocalPage(secondPage,new PdfDestination(PdfDestination.FIT), writer);//设置书签动作PdfOutline secondOutline = new PdfOutline(firstOutline, action2, secondTitle, false); //二级标题的outline}}});} catch (IOException ioe) {}document.close();}}

生成好的书签。代码的局限性比较高,可以自定义自己的书签类

参考文章:使用itext写的pdf文件添加书签的小工具

使用itext为已有的pdf文档生成书签相关推荐

  1. itext html 转换 pdf文件,利用itext实现html转pdf文档

    Link: http://keyknight.blog.163.com/blog/static/366378402009431104941637/ 利用itext实现html转pdf文档的代码实在是太 ...

  2. pdf文档添加书签的三种方式

    本文总结了三种pdf文档添加书签的方式. 1 搜出书签用PdgCntEditor创建书签方式 方法原地址:https://zhuanlan.zhihu.com/p/170590863 1.1 在全国图 ...

  3. 一键生成PDF文档的书签和目录(书签,目录页带页码 都行)

    转自:https://blog.csdn.net/u010391342/article/details/86681359 一键生成PDF文档的书签目录 原创codeing_doc 发布于2019-01 ...

  4. 快速为PDF文档添加书签的3种方法

    在PDF文档中添加书签是PDF阅读或PDF编辑的基本操作,有时候PDF文档的页数是相当多的,为PDF文档添加书签可以大大提高PDF阅读效率和PDF编辑速率,那么今天就为大家介绍快速为PDF文档添加书签 ...

  5. PDF文档添加书签,不仅仅是种仪式感

    PDF文档添加书签,不仅仅是种仪式感 回家已经一个多月了,突然兴起,于某个蝉鸣的午后翻看起了初高中的旧物.随手拿出被反反复复翻看了好几遍的那本<小王子>,书的某一页中夹着的干花书签令我回忆 ...

  6. 【福听阅读器】为PDF文档添加书签和子书签

    转自:http://blog.sina.com.cn/s/blog_4ee13c2c0100ukdm.html 打开福昕阅读器,选择要操作的pdf文件,1.  打开您想要书签链接的页面,并调整视图设置 ...

  7. 利用福昕阅读器为PDF文档添加书签和子书签

    引用自:http://blog.sina.com.cn/s/blog_4ee13c2c0100ukdm.html  (谢谢作者) 打开福昕阅读器,选择要操作的pdf文件,1. 打开您想要书签链接的页面 ...

  8. PDF文档一键自动生成目录和书签

      在工作中经常会遇到编写文档的时候,当我们在word编写完文档后,一般可以自动生成一个目录.为了方便阅读和保护文档不被破坏,一般发送给别人的时候,需要把word文档转换成PDF格式.但是word文档 ...

  9. 【PDF】java使用Itext生成pdf文档--详解

    [API接口] 一.Itext简介 API地址:javadoc/index.html:如 D:/MyJAR/原JAR包/PDF/itext-5.5.3/itextpdf-5.5.3-javadoc/i ...

最新文章

  1. easyui中的tree数据使用说明
  2. mysql数据库事务日志已满_服务器事务日志已满解决方法
  3. 判断是否在数组中,若在输入其下标,否则输入-1
  4. PHP mail()可能导致的问题
  5. java outlook 发送邮件_基于java使用JavaMail发送邮件
  6. 深度学习各场景评估指标总结
  7. Pytorch专题实战——逻辑回归(Logistic Regression)
  8. MOSS中集成各个子网站的数据到一个页面,做决策支持页面的首选: Web Capture
  9. 程序包androidx.support.annotation不存在/import android.support.annotation.NonNull;报错
  10. MinIO 快速入门
  11. 富盛企业经营分析软件操作手册下载
  12. CF 1715 D. 2+ doors 位运算 1900
  13. 天啦噜,竟然用AI来点名!你还敢逃课吗
  14. windows使用命令行修改用户密码
  15. Tomcat - 深度学习 - 类加器详解
  16. NVIDIA CUDA初级教程(P2-P3)CPU体系架构概述、并行程序设计概述
  17. 2022.04.14【读书笔记】|WGCNA分析原理和数据挖掘技巧
  18. 信息学竞赛考什么内容
  19. 1号店两年即被资本俘获 创始人离开仅是时间问题
  20. 物联网项目(五)下单渠道

热门文章

  1. [并查集]leetcode765:情侣牵手(hard)
  2. Android简单物流查询
  3. C语言数据结构课程设计-校园导游系统
  4. 设计模式前置知识类图---设计原则----man看了会沉默,woman看了会流泪系列第二篇
  5. hx711称重模块调试
  6. 微信小程序调用腾讯地图,点击选择并返回选择位置数据!
  7. 服务器数据监控、业务数据监控调研
  8. 算法设计与分析之众数和重数
  9. article-五自由度机械臂运动学分析(三转动+两移动自由度)
  10. 在/mnt/看不到共享文件夹的解决