侧边栏壁纸
博主头像
拾荒的小海螺博主等级

只有想不到的,没有做不到的

  • 累计撰写 140 篇文章
  • 累计创建 15 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

JAVA:实现 Excel 和 PDF 中的上下标的技术指南

拾荒的小海螺
2024-06-26 / 0 评论 / 0 点赞 / 13 阅读 / 10562 字

1、简介

在文档处理和数据报告中,上下标(Subscript 和 Superscript)是非常常见的格式需求,特别是在涉及数学公式、化学公式和其他需要特殊标记的领域。本文将介绍如何在 Excel 和 PDF 文档中实现上下标功能,分别使用 Apache POI 和 iText 库。

2、在 Excel 中实现上下标

我们将使用 Apache POI 库来处理 Excel 文件。Apache POI 是一个功能强大的 Java API,可以读取和写入微软的 OLE2 和 OOXML 文件格式,如 Excel、Word 和 PowerPoint。

首先,在你的 Maven 项目中添加 Apache POI 的依赖::

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>5.2.2</version>
</dependency>

实现上下标代码:

XSSFWorkbook workbook = new XSSFWorkbook();
XSSFSheet sheet = workbook.createSheet("Sheet1");
XSSFRow row = sheet.createRow(0);
XSSFCell cell = row.createCell(0);

XSSFFont font = workbook.createFont();
font.setTypeOffset(XSSFFont.SS_SUB); // 上标
font.setTypeOffset(XSSFFont.SS_SUPER);//下标

XSSFRichTextString richTextString = new XSSFRichTextString("Hcu");
richTextString.applyFont(1, 2, font); // 设置第二个字符为上标 "c"
richTextString.applyFont(2, 3, font); // 设置第三个字符为下标 "u"

cell.setCellValue(richTextString);
Path tempFile =  Paths.get("E:\\dist\\pdf0.xlsx");

try(OutputStream os = Files.newOutputStream(tempFile)){
	workbook.write(os);
	logger.error("xssfWorkbook-end:" + tempFile.toAbsolutePath());
}

3、造字

因某些字母没有对应的上下标字形,所以通过FontCreate软件来造上下标,至于软件可以去网上下载破解版,还有就是Unicode指定的数量就那么多,所以我们可以通过改变已有Unicode编码字符来作为我们上下标的编码。

可以通过找到当前分支少的Unicode字符做插入:比如选中西里尔字母这个分类点击 插入->字符:

1719403541557.jpg

然后我们对已有的字符做修改和做删除自己造:

1719403525428.jpg

最后形成我们自己所需要的字符:

1719403511981.jpg

4、在 PDF 中实现上下标

生成的PDF,采用开源的是开放源码的站点sourceforge一个项目itextpdf,是用于生成PDF文档的一个java类库。通过iText不仅可以生成PDF或rtf的文档,而且可以将XML、Html文件转化为PDF文件。因项目通过Excel来转PDF,但是因itextpdf无法识别Excel上下标,并且缺少了关键上下标。

首先,在你的 Maven 项目中添加 iText 的依赖:

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

输出中文,还要引入下面itext-asian.jar包:

<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itext-asian</artifactId>
    <version>5.2.0</version>
</dependency>

通过以上造好的字母上下标,直接通过加载指定的Unicode来实现实现加载:

String value = "设计强度ƒ\u0460\u0461";

File pdfFile = new File("E:\\dist\\pdf1.pdf");

//字符和Unicode组合格式化
value = StringEscapeUtils.unescapeJava(value);

Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(pdfFile));
document.open();
// 将 Excel 单元格内容写入 PDF 文档
PdfPTable table = new PdfPTable(1);
BaseFont bf = BaseFont.createFont("E:\\cell\\Merge.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
com.itextpdf.text.Font  f = new com.itextpdf.text.Font(bf, 10);
Paragraph p = new Paragraph(value, f);

table.addCell(p);
document.add(table);
document.close();
writer.close();

5、字符映射

因设计可以通过造字来实现上下标字母,现在我们可以通过指定字符来实现当前上下标的标签替换,设计上下标通过标签来包裹,类似:下标:<sub> </sub> 上标:<sup><sup>来标签指定上下标。
通过解析当前字符串上下标标签来实现字符替换:

private static final String SUB_START = "<sub>";	//下标
private static final String SUB_END = "</sub>";
private static final String SUP_START = "<sup>";	//上标
private static final String SUP_END = "</sup>";

/**
 * 获取下一对标签的index,不存在这些标签就返回null
 * @param s
 * @param tag SUB_START或者SUP_START
 * @return int[]中有两个元素,第一个是开始标签的index,第二个元素是结束标签的index
 */
public static int[] getNextTagsIndex(String s, String tag) {
    int firstStart = s.indexOf(tag);
    if (firstStart > -1) {
        int firstEnd = 0;
        if (tag.equals(SUB_START)) {
            firstEnd = s.indexOf(SUB_END);
            String ssString = s.substring(firstStart, firstEnd+SUB_END.length());
            if (ssString.contains(SUP_START)) {
                ssString = ssString.replace(SUP_START, "").replaceAll(SUP_END, "");
                firstEnd = firstStart + ssString.indexOf(SUB_END);
            }
        }else if (tag.equals(SUP_START)) {
            firstEnd = s.indexOf(SUP_END);
            String ssString = s.substring(firstStart, firstEnd+SUP_END.length());
            if (ssString.contains(SUB_START)) {
                ssString = ssString.replace(SUP_START, "").replaceAll(SUP_END, "");
                firstEnd = firstStart + ssString.indexOf(SUP_END);
            }
        }
        if (firstEnd > firstStart) {
            return new int[] { firstStart, firstEnd };
        }
    }
    return null;
}

/**移除下一对sub或者sup标签,返回移除后的字符串
 * @param s
 * @param tag SUB_START或者SUP_START
 * @return
 */
public static String removeNextTags(String s, String tag) {
    s = s.replaceFirst(tag, "");
    if (tag.equals(SUB_START)) {
        s = s.replaceFirst(SUB_END, "");
    }else if (tag.equals(SUP_START)) {
        s = s.replaceFirst(SUP_END, "");
    }
    return s;
}

/**
 * 判断是不是包含sub、sup标签
 * @param s
 * @return
 */
public static boolean containTag(String s) {
    return (s.contains(SUB_START) && s.contains(SUB_END)) || (s.contains(SUP_START) && s.contains(SUP_END));
}

/**
 * 处理字符串,得到每个sub、sup标签的开始和对应的结束的标签的index
 * @param s
 * @param tagIndexList 传一个新建的空list进来,方法结束的时候会存储好标签位置信息。
 * <br>tagIndexList.get(0)存放的sub
 * <br>tagIndexList.get(1)存放的是sup
 *
 * @return 返回sub、sup处理完之后的字符串
 */
public static String getIndexs(String s, List<List<int[]>> tagIndexList) {
    List<int[]> subs = Lists.newArrayList();
    List<int[]> sups =  Lists.newArrayList();
    while (true) {
        int[] sub_pair = getNextTagsIndex(s, SUB_START);
        if (Objects.nonNull(sub_pair)) {
            int firstStart = sub_pair[0];
            if (firstStart > -1) {
                int firstEnd = s.indexOf(SUB_END);
                String startString = s.substring(0, firstStart);
                String centreString = s.substring(firstStart, firstEnd);
                String endString = s.substring(firstEnd, s.length());
                if (centreString.contains(SUP_START)) {
                    centreString = centreString.replaceAll(SUP_START, "").replaceAll(SUP_END, "");
                    s = startString + centreString + endString;
                }
            }
        }

        int[] sup_pair = getNextTagsIndex(s, SUP_START);

        if (Objects.nonNull(sup_pair)) {
            int firstStart = sup_pair[0];
            if (firstStart > -1) {
                int firstEnd = s.indexOf(SUP_END);
                String startString = s.substring(0, firstStart);
                String centreString = s.substring(firstStart, firstEnd);
                String endString = s.substring(firstEnd, s.length());
                if (centreString.contains(SUB_START)) {
                    centreString = centreString.replaceAll(SUB_START, "").replaceAll(SUB_END, "");
                    s = startString + centreString + endString;
                }
            }
        }

        boolean subFirst = false;
        boolean supFirst = false;

        List a = new ArrayList();
        if (Objects.nonNull(sub_pair)) {
            a.add(sub_pair[0]);
        }
        if (Objects.nonNull(sup_pair)) {
            a.add(sup_pair[0]);
        }


        Collections.sort(a);
        if (Objects.nonNull(sub_pair)) {
            if (sub_pair[0] == Integer.parseInt(a.get(0).toString())) {
                subFirst = true;
            }
        }
        if (Objects.nonNull(sup_pair)) {
            if (sup_pair[0] == Integer.parseInt(a.get(0).toString())) {
                supFirst = true;
            }
        }

        if (sub_pair != null && subFirst) {
            s = removeNextTags(s, SUB_START);
            //<sub>标签被去掉之后,结束标签需要相应往前移动
            sub_pair[1] = sub_pair[1] - SUB_START.length();
            subs.add(sub_pair);
            continue;
        }
        if (sup_pair != null && supFirst) {
            s = removeNextTags(s, SUP_START);
            //<sup>标签被去掉之后,结束标签需要相应往前移动
            sup_pair[1] = sup_pair[1] - SUP_START.length();
            sups.add(sup_pair);
            continue;
        }

        if (sub_pair == null && sup_pair == null ) {
            break;
        }
    }

    tagIndexList.add(subs);
    tagIndexList.add(sups);
    return s;
}

然后我们通过获取上下标标签的下标,来完成字符的替换,至于要替换的Unicode是存在哪里,这个自己设计。
首先我们要把指定的字符转成Unicode的:

unicode = StringEscapeUtils.unescapeJava(unicode);

然后通过全局的字符builder来重新构造字符串:

StringBuilder sb  = new StringBuilder(value);
unicode = StringEscapeUtils.unescapeJava(unicode);
sb.replace(pair[0], pair[1], unicode);

1719403492590.jpg

6、结论

本文介绍了如何使用 Apache POI 和 iText 库在 Excel 和 PDF 文件中实现上下标功能。通过这些库,你可以灵活地处理各种文档格式,满足不同的业务需求。希望本文对你有所帮助!

0

评论区