pdf渲染实现右到左从上到下,中文正常显示,英文或者符号顺时针旋转90度

问题描述

在使用XEasyPdf(XEasyPdf是优良的封装Apache PDFBox的工具)时需要将文本实现如图所示的效果。
文字效果图
我没有找到相关的方法,如果官方支持请看到@我,我的方法开销很大,容易gc,每次使用都要手动gc 不是一个好方法,但总归可以实现文字效果。
特此记下文字旋转的方法

    public static InputStream backY(XEasyPdfPage xEasyPdfPage, Map<String, Object> params) {
        // 创建 PDF 文档
        XEasyPdfDocument xDocument = XEasyPdfHandler.Document.build();
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        xDocument.addPage(xEasyPdfPage);
        xDocument.save(out);
        try (InputStream inputStream = new ByteArrayInputStream(out.toByteArray())) {
            try (PDDocument document = PDDocument.load(inputStream)) {
                // 加载自定义中文字体文件
                try (InputStream inputStreamFont = Thread.currentThread().getContextClassLoader().getResourceAsStream("static/font/fs_GB2312.ttf")) {
                    PDType0Font customFont = PDType0Font.load(document, inputStreamFont);

                    float startX = (float) params.get("startX"); // 右侧开始绘制,留边距
                    float yStart = (float) params.get("startY"); // 顶部开始绘制,留边距
                    int fontSize = (int) params.getOrDefault("fontSize", 22); // 字体大小,默认为 22
                    float fontWeight = (int) params.getOrDefault("fontWeight", 16); // 字体宽度,默认为 16
                    int eWeight = (int) params.getOrDefault("eWeight", 2); // 字体宽度,默认为 2
                    int specialWeight = (int) params.getOrDefault("specialWeight", 4); // 字体宽度,默认为 16
                    float charHeight = customFont.getFontDescriptor().getFontBoundingBox().getHeight() / 1000 * fontSize; // 计算字符高度

                    // 统计偏移量
                    int offsetCount = 0;


                    try (PDPageContentStream contentStream = new PDPageContentStream(document, document.getPage(0), true, true, true)) {
                        contentStream.beginText(); // 开始文本块
                        contentStream.setFont(customFont, fontSize);
                        customFont.getFontDescriptor().setForceBold(true);//加粗

                        String text = params.get("text").toString(); // 包含中英符号混合的文本
                        for (int i = 0; i < text.length(); i++) {
                            char ch = text.charAt(i);
                            boolean isChinese = isChinese(ch);
                            boolean isCLastE = (i > 0) && isChinese && !isChinese(text.charAt(i - 1));
                            // 回补偏移量
                            float offsetRe = 0;
                            float offset = isChinese ? fontSize : customFont.getStringWidth(String.valueOf(ch)) / 1000 * fontWeight;
                            //恰好此时是数字和文字的分割处  取消回补的偏移量
                            if (isChinese && isCLastE && yStart != (float) params.get("startY")) {
                                //英文下面的汉字回补偏移量  除二是回补偏移量,这里取的是偏移量的双倍,为了明显的文字间隔作用
                                offsetRe = (fontSize - fontWeight);
                                offset += (fontSize - fontWeight);
                            }

                            offsetCount += offset;

                            if (isChinese) {
                                //中文保持不变
                                contentStream.setTextMatrix(1, 0, 0, 1, startX, yStart - offsetRe);
                            } else {

                                //英文或者符号旋转
                                //中置偏移量
                                float offsetX = (charHeight - fontWeight) / 2;
                                //计算文本宽度
                                float offsetDiff = customFont.getStringWidth(String.valueOf(ch)) / 1000 * fontWeight;
                                //上面的回补操作和这里对于,解决了文字旋转脱离原本的位置的问题 这个 reWeight 是增加英文或者符号的间隔距离
                                float reWeight = eWeight;
                                if (ch == '—') {
                                    reWeight = eWeight + specialWeight;
                                }
                                if (ch == '、' || ch == '。' || ch == ',' || ch == ':' || ch == ';') {
                                    reWeight = 0;
                                }
                                float offsetY = offsetDiff + (fontSize - offsetDiff) / 2 + reWeight;
                                contentStream.setTextMatrix(0, -1, 1, 0, startX + offsetX, yStart + offsetY);
                                yStart -= reWeight;
                                offsetCount += reWeight;
                            }

                            yStart -= offset;
                            contentStream.showText(Character.toString(ch));

                            //这里发现超过440也就是会超过边框一点点 430-22-1=407  所以是407 
                            if (offsetCount > 407) {
                                startX -= charHeight + 2;
                                yStart = (float) params.get("startY");
                                System.out.println("重置偏移量成功!" + offsetCount + ": " + ch);
                                //重置
                                offsetCount = 0;
                            }
                        }

                        contentStream.endText(); // 结束文本块
                    }

                    ByteArrayOutputStream outDoc = new ByteArrayOutputStream();
                    document.save(outDoc);
                    return new ByteArrayInputStream(outDoc.toByteArray());
                } catch (IOException e) {
                    e.printStackTrace();
                    
                }
            } catch (IOException e) {
                e.printStackTrace();
                
            }
        } catch (IOException e) {
            e.printStackTrace();
           
        }

        return null;
    }

    // 判断是否为中文字符
    private static boolean isChinese(char c) {
        return c >= '\u4e00' && c <= '\u9fff';
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值