使用itext为已有的pdf文档生成书签

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

有些时候我们下载的电子书没有书签,虽然不影响阅读,但是使用体验很差,尤其是需要跳转时。因为之前在项目中也是用过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 1
        Document document = new Document();
        // step 2 输出文件
        PdfCopy writer = new PdfCopy(document, new FileOutputStream(filename)); // step 3
        writer.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); //一级标题的outline

                    for (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文件添加书签的小工具

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在Java中使用ItextPdf填充已有pdf并且分页,可以采用以下步骤: 1.首先使用ItextPdf读取已有pdf文件,可以使用PdfReader类。 2.使用PdfStamper类创建新的pdf文件,并将原始pdf文件的内容复制到新的pdf文件中。 3.使用PdfContentByte类在新的pdf文件中添加新的内容,可以使用addTemplate方法将模板添加到新的pdf文件中。 4.使用ColumnText类将文本添加到新的pdf文件中,并进行分页处理。 下面是一个示例代码: ```java PdfReader reader = new PdfReader("original.pdf"); PdfStamper stamper = new PdfStamper(reader, new FileOutputStream("new.pdf")); PdfContentByte canvas = stamper.getOverContent(1); // 添加模板 PdfImportedPage importedPage = stamper.getImportedPage(reader, 1); canvas.addTemplate(importedPage, 0, 0); // 添加新的内容 ColumnText ct = new ColumnText(canvas); ct.setSimpleColumn(36, 36, 559, 806); Paragraph p = new Paragraph("Hello World!"); ct.addElement(p); ct.go(); // 分页处理 int pageNumber = 1; while (ColumnText.hasMoreText(ct)) { stamper.insertPage(++pageNumber, reader.getPageSize(1)); canvas = stamper.getOverContent(pageNumber); canvas.addTemplate(importedPage, 0, 0); ct.setCanvas(canvas); ct.setSimpleColumn(36, 36, 559, 806); ct.go(); } stamper.close(); reader.close(); ``` 在上面的示例中,我们首先使用PdfReader类读取原始的pdf文件,然后使用PdfStamper类创建新的pdf文件,并将原始pdf文件的第一页内容复制到新的pdf文件中。 接着,我们使用ColumnText类将新的文本添加到新的pdf文件中,并进行分页处理。在while循环中,我们使用insertPage方法添加新的页面,并将原始pdf文件的第一页内容复制到新的页面中。然后,我们使用setCanvas方法将ColumnText绑定到新的页面上,并调用go方法将文本添加到新的页面中。 最后,我们使用PdfStamper类关闭新的pdf文件,并使用PdfReader类关闭原始pdf文件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值