pdfbox工具类1.0

一、优化

此次修改优化了pdf工具类已有功能,同时可以用更简单的方式实现更复杂的样式,整体使用方式与前版并无明显区别

1、背景文字生成

修改背景颜色生成位置,起始位置从当前行文字中间改到当前行中间,解决了同一表格下不同行高不同字号表格背景,边框不能完全对齐的问题

Y初始值100

行高

背景位置y+t/4

忽略相同值t/4

100

20

90-110

90

10

85-95

70

20

60-80

该方法在不同行高时存在重叠和空缺问题,不符合主观逻辑,所以对方法进行修改


Y初始值100

行高

背景位置y+h/2

100

20

100-120

90

10

90-100

70

20

70-90

修改后方法可适配各种行高,不存在重叠或空缺问题,同时更符合主观逻辑,但同时需要修改文字展示位置,使文字处于背景中央

文字位置:

文字位置计算公式为y+h/2-t/2    y轴位置+行高h/2-文字高度t/2

2、自动文字大小控制

表格列增加autoSize字段,控制文字在自动宽度下换行是否更改文字大小

3、图片相对定位

为适应图表自动排列分页,修改图表定位方式为相对定位,将图片和文字看做是同一种表格,便于文档内容排版

二、工具类

边框样式

public enum BorderStyle {

    SOLID(new float[]{}, 0),
    DOTTED(new float[]{1}, 1),
    DASHED(new float[]{5,2}, 1);

    private final float[] pattern;
    private final int phase;

    BorderStyle(float[] pattern, int phase) {
        this.pattern = pattern;
        this.phase = phase;
    }

    public float[] getPattern() {
        return pattern;
    }

    public int getPhase() {
        return phase;
    }
}

 对齐样式

public enum HorizontalAlignment {

    LEFT, CENTER, RIGHT

}

 表 

@Data
public class PdfTable {
  /**
 * 行
 */
  private List rows;
  /**
 * 字体
 */
  private String font;
  /**
 * 语言
 */
  private String language;
  /**
 * 页眉
 */
  private PdfRow pageHead;
  public void addRow(PdfRow row){
    rows.add(row);
  }
  public void addTitleRow(PdfRow row){
    pageHead=row;
  }
}

 行

@Data
@Builder
public class PdfRow {
  /**
 * 列
 */
  private List columns;
  /**
 * 行高
 */
  private float height;
  /**
 * 边框样式
 */
  private BorderStyle borderStyle;
  /**
 * 边框颜色
 */
  @Builder.Default
   private Color boderColor=Color.BLACK;
  /**
 * 下边框是否生效
 */
  private Boolean downBorder;
  /**
 * 上边框是否生效
 */
  private Boolean upBorder;
  public void addColumn(PdfColumn pdfColumn){
    columns.add(pdfColumn);
  }
}

 文字列

@Data
@Builder
public class PdfColumn {
  /**
 * 背景颜色
 */
  @Builder.Default
   private Color backGround = Color.WHITE;
  /**
 * 边框颜色
 */
  @Builder.Default
   private Color borderColor = Color.BLACK;
  /**
 * 文字颜色
 */
  @Builder.Default
   private Color textColor= Color.BLACK;
  /**
 * 加粗
 */
  @Builder.Default
   private Boolean block=false;
  /**
 * 文字大小
 */
  @Builder.Default
   private float fontSize=10F;
  /**
 * 偏移
 */
  @Builder.Default
   private float offset=0F;
  /**
 * 文字位置
 */
  @Builder.Default
   private HorizontalAlignment align =HorizontalAlignment.CENTER;
  /**
 * 自动宽度
 */
  @Builder.Default
   private Boolean autoWidth=false;
  /**
 * 自动换行
 */
  @Builder.Default
   private Boolean autoLine = false;
  /**
 * 自动大小
 */
  @Builder.Default
   private Boolean autoSize=false;
  /**
 * 左边框
 */
  private Boolean leftBorder;
  /**
 * 右边框
 */
  private Boolean rightBorder;
  /**
 * 名称
 */
  private String name;
  /**
 * 宽度,仅在自动宽度未生效时启用
 */
  private float width;
  /**
 * 图片,图片生效时其他属性均不生效
 */
  private PdfColumnImage columnImage;
}

图片列

@Data
@Builder
public class PdfColumnImage {
  /**
 * 图片
 */
  private final byte[] image;
  /**
 * x轴偏移位置
 */
  private float x;
  /**
 * y轴偏移位置
 */
  private float y;
  /**
 * 图片宽度
 */
  private float width;
  /**
 * 图片高度
 */
  private float height;
}

pdf工具类

public class DrawTableUtils {
    /**
 * 图片路径
 */
    private static final String IMAGE_PATH = "image/";
    private static final String TARGET_FOLDER = "target";
    /**
 * 正常字体
 */
    private static final String NORMAL_FONT = "AlibabaSans-Regular.otf";
    /**
 * 加粗字体
 */
    private static final String BOLD_FONT = "AlibabaSans-Bold.otf";
    /**
 * 水平页边距
 */
    public static final float LEVEL_PADDING = 35;
    /**
 * 垂直页边距
 */
    public static final float VERTICAL_PADDING = 20;
    /**
 * 下表格线偏移
 */
    private static final float DOWN_LINE_PADDING = 0.5f;
    private static final float SIDE_BORDER_PADDING = 1;
    /**
 * 边框宽度
 */
    private static final float BORDER_WIDTH = 1;
    /**
 * 生成PDF
 *
 * @param table pdf数据
 * @param type PDF类型
 * @throws IOException
 */
    public static ByteArrayOutputStream createDocument(PdfTable table, ReportType type, String filepath) throws IOException {
        ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
        // 初始化文档
        PDDocument document = new PDDocument();
        PDPage page = new PDPage(PDRectangle.A4);
        document.addPage(page);
        // 字体
        String fontName = table.getFont();
        InputStream inFont = DrawTableUtils.class.getClassLoader().getResourceAsStream(fontName);
        PDType0Font normalFont = PDType0Font.load(document, inFont);
        PDType0Font boldFont = null;
        // InputStream normalStream = DrawTableUtils.class.getClassLoader().getResourceAsStream(NORMAL_FONT);
        // assert normalStream != null;
        // OpenTypeFont normalOtfFont = new OTFParser(false, true).parse(normalStream);
        // PDType0Font normalFont = PDType0Font.load(document, normalOtfFont, false);
        //
        // // 加粗字体
        // InputStream boldStream = DrawTableUtils.class.getClassLoader().getResourceAsStream(BOLD_FONT);
        // assert boldStream != null;
        // OpenTypeFont boldOtfFont = new OTFParser(false, true).parse(boldStream);
        // PDType0Font boldFont = PDType0Font.load(document, boldOtfFont, false);
        // 页
        int pageNum = 1;
        // 初始化文档位置
        Position position = new Position(LEVEL_PADDING, page.getMediaBox().getHeight() - VERTICAL_PADDING, 0);
        PDPageContentStream contentStream = new PDPageContentStream(document, page);
        for (PdfRow row : table.getRows()) {
            // 分页
            if (position.startY - row.getHeight() <= VERTICAL_PADDING * 2) {
                pageNum++;
                PDPage pdPage = new PDPage(PDRectangle.A4);
                document.addPage(pdPage);
                position.startY = page.getMediaBox().getHeight() - LEVEL_PADDING;
                contentStream.close();
                contentStream = new PDPageContentStream(document, pdPage);
            }
            drawLine(row, document, contentStream, position, normalFont, boldFont);
            if (row.isDownBorder()) {
                drawDownBorder(contentStream, page, row, position);
            }
            if (row.isUpBorder()) {
                drawUpBorder(contentStream, page, row, position);
            }
        }
        contentStream.close();
        if (type == ReportType.SINGLE) {
            drawPageFootPage(document, pageNum, normalFont);
            drawPageHeadPage(document, table.getPageHead(), pageNum, normalFont);
        } else if (type == ReportType.PATIENT) {
            drawPageFootPage(document, pageNum, normalFont);
            drawPageHeadPage(document, table.getPageHead(), pageNum, normalFont);
        } else if (type == ReportType.dOUBLE) {
            drawPageFootPage(document, pageNum, normalFont);
            drawPageHeadPage(document, table.getPageHead(), pageNum, normalFont);
        } else if (type == ReportType.HEALTH) {
            drawPageFootPage(document, pageNum, normalFont);
        }
        if (type == ReportType.SINGLE) {
            document.save(arrayOutputStream);
        } else if (type == ReportType.PATIENT) {
            document.save(arrayOutputStream);
        } else if (type == ReportType.dOUBLE) {
            document.save(arrayOutputStream);
        } else if (type == ReportType.HEALTH) {
            document.save(arrayOutputStream);
        }
        document.close();
        return arrayOutputStream;
    }
    /**
 * 页眉
 *
 * @param document 文档
 * @param pageHead 页眉
 * @param pageNum 文档页数
 * @param font 字体
 * @throws IOException
 */
    private static void drawPageHeadPage(PDDocument document, PdfRow pageHead, int pageNum, PDType0Font font) throws IOException {
        for (int i = 1; i < pageNum; i++) {
            Position position = new Position(LEVEL_PADDING, PDRectangle.A4.getHeight() - VERTICAL_PADDING, 0);
            PDPage page = document.getPage(i);
            PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true);
            drawLine(pageHead, document, contentStream, position, font, null);
            contentStream.close();
        }
    }
    /**
 * 生成表格下框,以行为单位
 *
 * @param contentStream 流
 * @param page 页
 * @param row 行
 * @param position 位置
 * @throws IOException
 */
    private static void drawDownBorder(PDPageContentStream contentStream, PDPage page, PdfRow row, Position position) throws IOException {
        contentStream.setStrokingColor(row.getBoderColor());
        contentStream.setLineWidth(BORDER_WIDTH);
        contentStream.setLineDashPattern(row.getBorderStyle().getPattern(), row.getBorderStyle().getPhase());
        contentStream.moveTo(LEVEL_PADDING, position.startY + DOWN_LINE_PADDING);
        contentStream.lineTo(page.getMediaBox().getWidth() - LEVEL_PADDING, position.startY + DOWN_LINE_PADDING);
        contentStream.stroke();
    }
    /**
 * 生成表格上边框,以行为单位
 * @param contentStream
 * @param page 页
 * @param row 行
 * @param position 位置
 * @throws IOException
 */
    private static void drawUpBorder(PDPageContentStream contentStream, PDPage page, PdfRow row, Position position) throws IOException {
        contentStream.setStrokingColor(row.getBoderColor());
        contentStream.setLineWidth(BORDER_WIDTH);
        contentStream.setLineDashPattern(row.getBorderStyle().getPattern(), row.getBorderStyle().getPhase());
        contentStream.moveTo(LEVEL_PADDING, position.startY + row.getHeight() - DOWN_LINE_PADDING);
        contentStream.lineTo(page.getMediaBox().getWidth() - LEVEL_PADDING, position.startY + row.getHeight() - DOWN_LINE_PADDING);
        contentStream.stroke();
    }
    /**
 * 生成一行中每一列数据
 *
 * @param row 行
 * @param document 文档
 * @param contentStream 流
 * @param position 位置
 * @param normalFont 正常字体
 * @param boldFont 加粗字体
 * @throws IOException
 */
    private static void drawLine(PdfRow row, PDDocument document, PDPageContentStream contentStream, Position position, PDType0Font normalFont, PDType0Font boldFont) throws IOException {
        // 更新Y轴位置
        position.startY -= row.getHeight();
        for (PdfColumn column : row.getColumns()) {
            // 添加图片
            if (column.getColumnImage() != null) {
                PdfColumnImage columnImage = column.getColumnImage();
                // final byte[] imageByte = IOUtils.toByteArray(Objects.requireNonNull(DrawTableUtils.class.getClassLoader().getResourceAsStream(IMAGE_PATH + columnImage.getImage())));
                final PDImageXObject image = PDImageXObject.createFromByteArray(document, columnImage.getImage(), "picture");
                contentStream.drawImage(image, position.startX + columnImage.getX(), position.startY + columnImage.getY(), columnImage.getWidth(), columnImage.getHeight());
            } else {
                // 背景
                if (column.getBackGround() != Color.WHITE) {
                    // 文字高度
                    position.titleHeight = normalFont.getFontDescriptor().getFontBoundingBox().getHeight() / 1000 * column.getFontSize();
                    // 背景高度,取行高
                    contentStream.setLineWidth(row.getHeight());
                    contentStream.setStrokingColor(column.getBackGround());
                    // 实线无间隔
                    contentStream.setLineDashPattern(BorderStyle.SOLID.getPattern(), BorderStyle.SOLID.getPhase());
                    // 设置Y轴起始位置,背景占位为当前位置向上高度
                    contentStream.moveTo(position.startX + column.getOffset(), position.startY + row.getHeight() / 2);
                    contentStream.lineTo(position.startX + column.getOffset() + column.getWidth(), position.startY + row.getHeight() / 2);
                    contentStream.stroke();
                }
                // 左边框
                if (column.isLeftBorder()) {
                    position.titleHeight = normalFont.getFontDescriptor().getFontBoundingBox().getHeight() / 1000 * column.getFontSize();
                    // 设置宽度
                    contentStream.setLineWidth(BORDER_WIDTH);
                    contentStream.setStrokingColor(column.getBorderColor());
                    // 实线无间隔
                    contentStream.setLineDashPattern(BorderStyle.SOLID.getPattern(), BorderStyle.SOLID.getPhase());
                    // 设置左边框位置,与背景色对齐
                    contentStream.moveTo(position.startX + column.getOffset(), position.startY);
                    contentStream.lineTo(position.startX + column.getOffset(), position.startY + row.getHeight());
                    contentStream.stroke();
                }
                // 右边框
                if (column.isRightBorder()) {
                    position.titleHeight = normalFont.getFontDescriptor().getFontBoundingBox().getHeight() / 1000 * column.getFontSize();
                    // 设置宽度
                    contentStream.setLineWidth(BORDER_WIDTH);
                    contentStream.setStrokingColor(column.getBorderColor());
                    // 实线无间隔
                    contentStream.setLineDashPattern(BorderStyle.SOLID.getPattern(), BorderStyle.SOLID.getPhase());
                    // 设置左边框位置,与背景色对齐
                    contentStream.moveTo(position.startX + column.getOffset() + column.getWidth(), position.startY);
                    contentStream.lineTo(position.startX + column.getOffset() + column.getWidth(), position.startY + row.getHeight());
                    contentStream.stroke();
                }
                float titleWidth = normalFont.getStringWidth(column.getName() == null ? "" : column.getName()) / 1000 * column.getFontSize();
                // 自动换行
                if (column.isAutoLine()) {
                    if (titleWidth > column.getWidth()) {
                        // 切分行数
                        int ceil = (int) Math.ceil(titleWidth / column.getWidth());
                        int currentCel = ceil;
                        float hi = row.getHeight() / ceil;
                        String[] split = column.getName().split(" ");
                        StringBuilder builder = new StringBuilder();
                        float len = 0;
                        for (int i = 0; i < split.length; i++) {
                            len += normalFont.getStringWidth(split[i] + " ") / 1000 * column.getFontSize();
                            builder.append(split[i]).append(" ");
                            if ((i == split.length - 1 && len > 0) || len + normalFont.getStringWidth(split[i + 1]) / 1000 * column.getFontSize() >= column.getWidth()) {
                                // 写入分行数据
                                Position aPosition = new Position(position.startX, position.startY + hi * (currentCel - 1), position.getTitleHeight());
                                PdfColumn aColumn = PdfColumn.builder().align(column.getAlign()).fontSize(column.isAutoSize() ? column.getFontSize() / ceil * 1.8f : column.getFontSize()).name(builder.toString()).width(column.getWidth()).offset(column.getOffset()).block(column.isBlock()).build();
                                float pWidth = normalFont.getStringWidth(aColumn.getName() == null ? "" : aColumn.getName()) / 1000 * aColumn.getFontSize();
                                drawMuLine(contentStream, aPosition, aColumn, pWidth, hi, normalFont);
                                currentCel--;
                                builder.setLength(0);
                                len = 0;
                            }
                        }
                    } else {
                        drawMuLine(contentStream, position, column, titleWidth, row.getHeight(), normalFont);
                    }
                } else {
                    // 自动宽度
                    if (column.isAutoWidth()) {
                        column.setWidth(titleWidth);
                    }
                    drawMuLine(contentStream, position, column, titleWidth, row.getHeight(), normalFont);
                }
            }
            // 更新X轴位置
            position.startX = position.startX + column.getWidth() + column.getOffset();
        }
        position.startX = LEVEL_PADDING;
    }
    /**
 * 页脚页数编码
 *
 * @param document 文档
 * @param pageNum 页面数
 * @param font 字体
 * @throws IOException
 */
    private static void drawPageFootPage(PDDocument document, int pageNum, PDType0Font font) throws IOException {
        for (int i = 0; i < pageNum; i++) {
            String title = "第" + (i + 1) + "页" + "/共" + pageNum + "页";
            float titleWidth = font.getStringWidth(title) / 1000 * 10;
            PDPage page = document.getPage(i);
            PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true);
            contentStream.beginText();
            contentStream.setFont(font, 10);
            contentStream.setNonStrokingColor(Color.BLACK);
            contentStream.newLineAtOffset((PDRectangle.A4.getWidth() - titleWidth) / 2, 25);
            contentStream.showText(title);
            contentStream.endText();
            contentStream.close();
        }
    }
    private static void drawMuLine(PDPageContentStream contentStream, Position position, PdfColumn column, float titleWidth, float rowHeight, PDType0Font normalFont) throws IOException {
        // 文字
        contentStream.beginText();
        contentStream.setFont(normalFont, column.getFontSize());
        contentStream.setNonStrokingColor(column.getTextColor());
        HorizontalAlignment align = column.getAlign();
        if (align == HorizontalAlignment.CENTER) {
            // 居中
            contentStream.newLineAtOffset((column.getWidth() - titleWidth) / 2 + position.startX + column.getOffset(), position.startY - position.titleHeight / 2 + rowHeight / 2);
        } else if (align == HorizontalAlignment.LEFT) {
            // 居左
            contentStream.newLineAtOffset(position.startX + column.getOffset(), position.startY - position.titleHeight / 2 + rowHeight / 2);
        } else if (align == HorizontalAlignment.RIGHT) {
            // 居右
            contentStream.newLineAtOffset(position.startX + column.getOffset() + column.getWidth() - titleWidth, position.startY - position.titleHeight / 2 + rowHeight / 2);
        }
        contentStream.showText(column.getName() == null ? "" : column.getName());
        contentStream.endText();
        // 加粗使用偏移方法
        if (column.isBlock()) {
            drawBlockText(contentStream, position, column, titleWidth, rowHeight);
        }
    }
    /**
 * 加粗字体,偏移实现
 *
 * @param contentStream
 * @param position
 * @param pdfColumn
 * @param titleWidth
 * @param rowHeight
 * @throws IOException
 */
    private static void drawBlockText(PDPageContentStream contentStream, Position position, PdfColumn pdfColumn, float titleWidth, float rowHeight) throws IOException {
        float offset = pdfColumn.getFontSize() / 100;
        float x = 0;
        float y = 0;
        if (pdfColumn.getAlign() == HorizontalAlignment.CENTER) {
            // 居中
            x = (pdfColumn.getWidth() - titleWidth) / 2 + position.startX + pdfColumn.getOffset();
            y = position.startY - position.titleHeight / 2 + rowHeight / 2;
        } else if (pdfColumn.getAlign() == HorizontalAlignment.LEFT) {
            // 居左
            x = position.startX + pdfColumn.getOffset();
            y = position.startY - position.titleHeight / 2 + rowHeight / 2;
        } else if (pdfColumn.getAlign() == HorizontalAlignment.RIGHT) {
            // 居右
            x = position.startX + pdfColumn.getOffset() + pdfColumn.getWidth() - titleWidth;
            y = position.startY - position.titleHeight / 2 + rowHeight / 2;
            // offset = -offset;
        }
        // 左上
        contentStream.beginText();
        contentStream.newLineAtOffset(x - offset, y + offset);
        contentStream.showText(pdfColumn.getName() == null ? "" : pdfColumn.getName());
        contentStream.endText();
        // 左
        contentStream.beginText();
        contentStream.newLineAtOffset(x - offset, y);
        contentStream.showText(pdfColumn.getName() == null ? "" : pdfColumn.getName());
        contentStream.endText();
        // 左下
        contentStream.beginText();
        contentStream.newLineAtOffset(x - offset, y - offset);
        contentStream.showText(pdfColumn.getName() == null ? "" : pdfColumn.getName());
        contentStream.endText();
        // 上
        contentStream.beginText();
        contentStream.newLineAtOffset(x, y + offset);
        contentStream.showText(pdfColumn.getName() == null ? "" : pdfColumn.getName());
        contentStream.endText();
        // 下
        contentStream.beginText();
        contentStream.newLineAtOffset(x, y - offset);
        contentStream.showText(pdfColumn.getName() == null ? "" : pdfColumn.getName());
        contentStream.endText();
        // 右上
        contentStream.beginText();
        contentStream.newLineAtOffset(x + offset, y + offset);
        contentStream.showText(pdfColumn.getName() == null ? "" : pdfColumn.getName());
        contentStream.endText();
        // 右
        contentStream.beginText();
        contentStream.newLineAtOffset(x + offset, y);
        contentStream.showText(pdfColumn.getName() == null ? "" : pdfColumn.getName());
        contentStream.endText();
        // 右下
        contentStream.beginText();
        contentStream.newLineAtOffset(x + offset, y - offset);
        contentStream.showText(pdfColumn.getName() == null ? "" : pdfColumn.getName());
        contentStream.endText();
        // // 水平
        // contentStream.beginText();
        // contentStream.newLineAtOffset(x + offset, y);
        // contentStream.showText(pdfColumn.getName() == null ? "" : pdfColumn.getName());
        // contentStream.endText();
        // // 水平上
        // contentStream.beginText();
        // contentStream.newLineAtOffset(x + offset, y + Math.abs(offset));
        // contentStream.showText(pdfColumn.getName() == null ? "" : pdfColumn.getName());
        // contentStream.endText();
        // // 垂直
        // contentStream.beginText();
        // contentStream.newLineAtOffset(x, y + Math.abs(offset));
        // contentStream.showText(pdfColumn.getName() == null ? "" : pdfColumn.getName());
        // contentStream.endText();
    }
    @Data
     @AllArgsConstructor
     static class Position {
        float startX;
        float startY;
        float titleHeight;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值