All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ofdrw.layout.element.canvas.CellContentDrawer Maven / Gradle / Ivy

The newest version!
package org.ofdrw.layout.element.canvas;

import org.ofdrw.core.basicType.STBase;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.LinkedList;

/**
 * 类单元格特殊Canvas 绘制器
 * 

* 可以实现类似于单元个效果,用于简化区域占位区块绘制。 * * @author 权观宇 * @since 2023-11-13 18:48:53 */ public class CellContentDrawer implements Drawer { /** * 调试开关,在开启后会绘制辅助线 */ public static Boolean DEBUG = false; /** * Canvas对象 */ private final Canvas canvas; /** * 单元格文字内容 */ private String value; /** * 文字水平浮动方式 *

* 默认:左浮动 */ private TextAlign textAlign = TextAlign.left; /** * 文字垂直方向浮动方式 *

* 默认:居中 */ private VerticalAlign verticalAlign = VerticalAlign.center; /** * 文字颜色 *

* 支持16进制颜色值:#000000 *

* RGB:rgb(0,0,0) *

* RGBA:rgba(0,0,0,1) *

* 默认:#000000 (黑色) */ private String color = "#000000"; /** * 字体名称 *

* 默认:宋体 */ private String fontName = "宋体"; /** * 字号 *

* 默认:3 (单位:毫米) */ private double fontSize = 3; /** * 行间距 */ private Double lineSpace = 0.6; /** * 是否加粗 */ private Boolean bold = false; /** * 获取字体宽度 * * @return String 字体宽度,应遵循 CSS3标准,可选值为 normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 */ private String fontWeight = "normal"; /** * 是否斜体 */ private Boolean italic = false; /** * 文字之间的间距 */ private Double letterSpacing = 0d; /** * 图片 */ private Img img = null; /** * 是否有下划线 */ private boolean underline = false; /** * 是否删除线 */ private boolean deleteLine = false; /** * 外部字体路径 */ private Path extFontPath = null; /** * 通过已有Canvas构造单元格 *

* 注意该方法将会替换Canvas的绘制器为单元格的绘制器。 * * @param canvas Canvas */ public CellContentDrawer(Canvas canvas) { if (canvas == null) { throw new IllegalArgumentException("Canvas 不能为空"); } this.canvas = canvas; canvas.setDrawer(this); } /** * 创建单元格 * * @param x 左下角x坐标 * @param y 左下角y坐标 * @param width 宽度 * @param height 高度 */ public CellContentDrawer(double x, double y, double width, double height) { this.canvas = new Canvas(x, y, width, height); this.canvas.setDrawer(this); } /** * 文字行 */ private static class TextLine { /** * 文本内容 */ public String text; /** * 文本宽度 */ public double width; public TextLine(String text, double width) { this.text = text; this.width = width; if (DEBUG) { System.out.println(">> text:" + text + " width:" + width); } } } /** * 图片 */ private static class Img { /** * 图片路径 */ public Path path; /** * 图片宽度 */ public double width; /** * 图片高度 */ public double height; public Img(Path path, double width, double height) { this.path = path; this.width = width; this.height = height; } } /** * 单元格内部绘制 * * @param ctx 绘制上下文 * @throws IOException 图形绘制异常 */ @Override public void draw(DrawContext ctx) throws IOException { ctx.save(); try { if (this.img != null) { // 绘制图片 drawImg(ctx); } else { if (extFontPath != null) { // 添加外部字体 ctx.addFont(fontName, extFontPath); } // 绘制文字 drawText(ctx); } } finally { ctx.restore(); } } /** * 单元格图片绘制 * * @param ctx 绘制上下文 * @throws IOException 图片绘制异常 */ private void drawImg(DrawContext ctx) throws IOException { if (this.img == null) { return; } if (this.img.width <= 0 || this.img.height <= 0) { // 若未对图片进行宽高设置,则从图片中获取宽高 BufferedImage gImg = ImageIO.read(img.path.toFile()); this.img.width = ctx.mm(gImg.getWidth()); this.img.height = ctx.mm(gImg.getHeight()); if (DEBUG) { System.out.printf(">> 从图片中获取宽高 img.width:%.2f img.height:%.2f\n", img.width, img.height); } } double x = 0; switch (this.textAlign) { case right: case end: // 右浮动 x = canvas.getWidth() - img.width; break; case center: // 居中 x = (canvas.getWidth() - img.width) / 2d; break; case start: case left: default: // 左浮动 x = 0d; break; } double y = 0; switch (this.verticalAlign) { case bottom: y = canvas.getHeight() - img.height; break; case center: y = (canvas.getHeight() - img.height) / 2d; break; case top: default: y = 0d; break; } ctx.drawImage(img.path, x, y, img.width, img.height); if (DEBUG) { debugBorder(ctx); } } /** * 单元格文字内容绘制 * * @param ctx 绘制上下文 * @throws IOException 文字绘制异常 */ private void drawText(DrawContext ctx) throws IOException { if (this.value == null || this.value.isEmpty()) { return; } // 设置字体样式 String fontStr = ""; if (italic) { fontStr += "italic "; } if (bold) { fontStr += "bold "; } else if (fontWeight != null && !fontWeight.isEmpty()) { fontStr += fontWeight + " "; } ctx.font = fontStr + STBase.fmt(fontSize) + "mm " + fontName; if (this.letterSpacing != 0) { // 设置字间距 ctx.getFont().setLetterSpacing(this.letterSpacing); } // 设置字体颜色 if (this.color != null && !this.color.isEmpty()) { ctx.fillStyle = this.color; } double width = canvas.getWidth(); double height = canvas.getHeight(); TextMetrics textMetrics = ctx.measureText(this.value); LinkedList lines = new LinkedList<>(); double textLineWidth = 0; int offset = 0; // 分段 for (int i = 0; i < this.value.length(); i++) { char c = this.value.charAt(i); // 换行符提前结束 if (c == '\n') { lines.add(new TextLine(this.value.substring(offset, i), textLineWidth)); offset = i + 1; textLineWidth = 0; continue; } double cWidth = 0; if (i == this.value.length() - 1) { cWidth = ctx.measureText(String.valueOf(c)).width; } else { cWidth = textMetrics.offset[i]; } if (textLineWidth + cWidth > width) { // 超出宽度,换行 lines.add(new TextLine(this.value.substring(offset, i), textLineWidth)); offset = i; textLineWidth = cWidth; } else { textLineWidth += cWidth; } } // 最后一行 if (offset < this.value.length()) { lines.add(new TextLine(this.value.substring(offset), textLineWidth)); } // 内容高度:(字号 + 行间距) *行数 double contentHeight = (this.fontSize + this.lineSpace) * lines.size(); double offsetY = 0; // 第一行 Y 偏移量计算:字体坐标位于字体的左下角 if (this.verticalAlign == VerticalAlign.top) { offsetY = fontSize; } else if (this.verticalAlign == VerticalAlign.center) { offsetY = height / 2 - contentHeight / 2 + fontSize; } else { // 底部对齐 偏移量为:总高度 - 内容高度 + 字号 offsetY = height - contentHeight + fontSize; } for (TextLine line : lines) { double offsetX = 0; // 按照左右浮动方式 依次计算出每行的X 偏移量 if (this.textAlign == TextAlign.left || this.textAlign == TextAlign.start || this.textAlign == null) { // 左浮动 offsetX = 0; } else if (this.textAlign == TextAlign.center) { // 居中 offsetX = (width - line.width) / 2d; } else { // 右浮动 offsetX = width - line.width; } if (!"".equals(line.text)) { ctx.fillText(line.text, offsetX, offsetY); } // 文字装饰线条宽度 // 比例系数由经验指定无特定规则 double fontLineWidth = fontSize / 30; double underlineOffset = fontLineWidth * 3.2; // 下划线 if (this.underline) { ctx.save(); ctx.setLineWidth(fontLineWidth); ctx.beginPath(); ctx.moveTo(offsetX, offsetY + underlineOffset); ctx.lineTo(offsetX + line.width, offsetY + underlineOffset); ctx.stroke(); ctx.restore(); } // 删除线 if (this.deleteLine) { ctx.save(); ctx.setLineWidth(fontLineWidth); ctx.beginPath(); // 由于文字定位在基线位置,此处以 5/18 比例计算出删除线位置,5/18为经验值,无特殊意义。 ctx.moveTo(offsetX, offsetY - fontSize * 5 / 18); ctx.lineTo(offsetX + line.width, offsetY - fontSize * 5 / 18); ctx.stroke(); ctx.restore(); } if (DEBUG) { ctx.save(); ctx.setLineDash(1.5d, 1.5d); ctx.setLineWidth(0.1); ctx.setGlobalAlpha(0.53); ctx.strokeStyle = "rgb(255,0,0)"; ctx.strokeRect(offsetX, offsetY - fontSize, line.width, fontSize + lineSpace); ctx.stroke(); ctx.restore(); } // 绘制完上一行后将offsetY移动到下一行 offsetY += this.fontSize + this.lineSpace; } if (DEBUG) { debugBorder(ctx); } } /** * 绘制辅助线 * * @param ctx 绘制上下文 */ private void debugBorder(DrawContext ctx) { double width = canvas.getWidth(); double height = canvas.getHeight(); ctx.save(); double lineWidth = 0.353d; ctx.setLineDash(1.5d, 1.5d); ctx.setLineWidth(lineWidth); ctx.setGlobalAlpha(0.53); ctx.strokeStyle = "rgb(255,0,0)"; ctx.moveTo(0, 0); ctx.lineTo(width, height); ctx.moveTo(width, 0); ctx.lineTo(0, height); ctx.rect(0, 0, width, height); ctx.stroke(); ctx.restore(); } /** * 获取单元格文字内容 * * @return 单元格文字内容 */ public String getValue() { return value; } /** * 设置单元格文字内容 * * @param value 单元格文字内容 * @return this */ public CellContentDrawer setValue(String value) { this.value = value; return this; } /** * 设置图片 * * @param imgPath 图片路径,仅支持png、jpg、jpeg、gif、bmp格式 * @param w 图片宽度,单位:毫米 * @param h 图片高度,单位:毫米 * @return this */ public CellContentDrawer setValue(Path imgPath, double w, double h) { this.img = new Img(imgPath, w, h); return this; } /** * 设置图片 *

* 图片宽度与高度通过 {@link DrawContext#mm(int)} } 方法转换为毫米 * * @param imgPath 图片路径,仅支持png、jpg、jpeg、gif、bmp格式 * @return this * @throws IOException 图片加载异常 */ public CellContentDrawer setValue(Path imgPath) throws IOException { this.img = new Img(imgPath, 0, 0); return this; } /** * 获取单元格颜色 * * @return 单元格颜色,格式:#000000、rgb(0,0,0)、rgba(0,0,0,1) */ public String getColor() { return color; } /** * 设置单元格颜色 * * @param color 单元格颜色,格式:#000000、rgb(0,0,0)、rgba(0,0,0,1) * @return this */ public CellContentDrawer setColor(String color) { if (color == null || color.isEmpty()) { throw new IllegalArgumentException("颜色(color)不能为空"); } this.color = color; return this; } /** * 获取字体名称 * * @return 字体名称 */ public String getFontName() { return fontName; } /** * 设置字体名称 * * @param fontName 字体名称,仅支持系统安装字体,且不会嵌入到OFD中。 * @return this */ public CellContentDrawer setFontName(String fontName) { this.fontName = fontName; return this; } /** * 获取字号 * * @return 字号,默认:0.353 (单位:毫米) */ public double getFontSize() { return fontSize; } /** * 设置字号 * * @param fontSize 字号,默认:3(单位:毫米) * @return this */ public CellContentDrawer setFontSize(double fontSize) { if (fontSize <= 0) { throw new IllegalArgumentException("字号(fontSize)必须大于0"); } this.fontSize = fontSize; return this; } /** * 获取单元格的Canvas对象 * * @return 单元格的Canvas对象 */ public Canvas getCanvas() { return canvas; } /** * 获取 文字对齐方式 * * @return 文字对齐方式,默认:左对齐 */ public TextAlign getTextAlign() { return textAlign; } /** * 设置 文字对齐方式 * * @param textAlign 文字对齐方式 * @return this */ public CellContentDrawer setTextAlign(TextAlign textAlign) { this.textAlign = textAlign; return this; } /** * 获取 文字垂直方向浮动方式 * * @return 文字垂直方向浮动方式,默认:居中 {@link VerticalAlign#center} */ public VerticalAlign getVerticalAlign() { return verticalAlign; } /** * 设置 文字垂直方向浮动方式 * * @param verticalAlign 文字垂直方向浮动方式 * @return this */ public CellContentDrawer setVerticalAlign(VerticalAlign verticalAlign) { this.verticalAlign = verticalAlign; return this; } /** * 获取 行间距 * * @return 行间距,默认值 0.6mm */ public Double getLineSpace() { return lineSpace; } /** * 设置 行间距 * * @param lineSpace 行间距 * @return this */ public CellContentDrawer setLineSpace(Double lineSpace) { this.lineSpace = lineSpace; return this; } /** * @return 是否加粗,默认:不加粗 * @deprecated 单词错误 {@link #getBold()} * 是否加粗 */ @Deprecated public Boolean getBlob() { return bold; } /** * @param bolb 是否加粗 * @return this * @deprecated 单词错误 {@link #setBold(Boolean)} *

* 设置 是否加粗 */ @Deprecated public CellContentDrawer setBlob(Boolean bolb) { this.bold = bolb; return this; } /** * 是否加粗 * * @return 是否加粗,默认:不加粗 */ public Boolean getBold() { return bold; } /** * 设置 是否加粗 * * @param bold 是否加粗 * @return this */ public CellContentDrawer setBold(Boolean bold) { this.bold = bold; return this; } /** * 是否斜体 * * @return true - 斜体、false - 正常 */ public Boolean getItalic() { return italic; } /** * 设置 是否斜体 * * @param italic 是否斜体,true - 斜体、false - 正常 * @return this */ public CellContentDrawer setItalic(Boolean italic) { this.italic = italic; return this; } /** * 获取 文字之间的间距 * * @return 文字之间的间距,默认为:0 */ public Double getLetterSpacing() { return letterSpacing; } /** * 设置 文字之间的间距 * * @param letterSpacing 文字之间的间距,可以为负数,默认为:0。 * @return this */ public CellContentDrawer setLetterSpacing(Double letterSpacing) { this.letterSpacing = letterSpacing; return this; } /** * 获取图片路径 * * @return 图片路径,可能为空。 */ public Path getImgPath() { if (img == null) { return null; } return img.path; } /** * 获取图片高度 * * @return 图片高度,可能为0。 */ public double getImgWidth() { if (img == null) { return 0; } return img.width; } /** * 获取图片宽度 * * @return 图片宽度,可能为0。 */ public double getImgHeight() { if (img == null) { return 0; } return img.height; } /** * 设置是否开启下划线 * * @param underline true - 启下划线,false - 禁用下划线 * @return this */ public CellContentDrawer setUnderline(boolean underline) { this.underline = underline; return this; } /** * 获取是否开启下划线 * * @return true - 启下划线,false - 不启用下划线 */ public boolean getUnderline() { return this.underline; } /** * 设置是否开启删除线 * * @param deleteLine true - 启删除线,false - 禁用删除线 * @return this */ public CellContentDrawer setDeleteLine(boolean deleteLine) { this.deleteLine = deleteLine; return this; } /** * 获取是否开启删除线 * * @return true - 启删除线,false - 禁用删除线 */ public boolean getDeleteLine() { return this.deleteLine; } /** * 获取字体宽度 * * @return 字体宽度,遵循 CSS3标准,可选值为 normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 */ public String getFontWeight() { return fontWeight; } /** * 设置字体宽度 * * @param fontWeight 字体宽度,应遵循 CSS3标准,可选值为 normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 */ public void setFontWeight(String fontWeight) { this.fontWeight = fontWeight; } /** * 设置单元格绘制器使用的外部字体 *

* 注意OFDRW不会提供任何字体裁剪功能,您的字体文件将直接加入OFD文件中,这可能造成文件体积剧增。 * * @param fontName 字体名称,如“思源宋体” * @param fontPath 字体文件所在路径 * @return this */ public CellContentDrawer setFont(String fontName, Path fontPath) { if (fontName == null || fontName.isEmpty()) { throw new IllegalArgumentException("字体名称(fontName)不能为空"); } if (fontPath == null || Files.exists(fontPath) == false) { throw new IllegalArgumentException("字体文件(fontPath)不存在"); } this.setFontName(fontName); this.extFontPath = fontPath; return null; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy