package com.dl.flowable.flow; import org.flowable.bpmn.model.AssociationDirection; import org.flowable.bpmn.model.GraphicInfo; import org.flowable.image.impl.DefaultProcessDiagramCanvas; import org.flowable.image.util.ReflectUtil; import javax.imageio.ImageIO; import java.awt.*; import java.awt.font.FontRenderContext; import java.awt.font.LineBreakMeasurer; import java.awt.font.TextAttribute; import java.awt.font.TextLayout; import java.awt.geom.Ellipse2D; import java.awt.geom.Rectangle2D; import java.awt.geom.RoundRectangle2D; import java.awt.image.BufferedImage; import java.io.IOException; import java.text.AttributedCharacterIterator; import java.text.AttributedString; /** * @author XuanXuan * @date 2021/4/4 23:58 */ public class CustomProcessDiagramCanvas extends DefaultProcessDiagramCanvas { //定义走过流程连线颜色为绿色 protected static Color HIGHLIGHT_SequenceFlow_COLOR = Color.GREEN; //设置未走过流程的连接线颜色 protected static Color CONNECTION_COLOR = Color.BLACK; //设置flows连接线字体颜色red protected static Color LABEL_COLOR = new Color(0, 0, 0); //高亮显示task框颜色 protected static Color HIGHLIGHT_COLOR = Color.GREEN; protected static Color HIGHLIGHT_COLOR1 = Color.RED; public CustomProcessDiagramCanvas(int width, int height, int minX, int minY, String imageType, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) { super(width, height, minX, minY, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader); this.initialize(imageType); } /** * 重写绘制连线的方式,设置绘制颜色 * @param xPoints * @param yPoints * @param conditional * @param isDefault * @param connectionType * @param associationDirection * @param highLighted * @param scaleFactor */ @Override public void drawConnection(int[] xPoints, int[] yPoints, boolean conditional, boolean isDefault, String connectionType, AssociationDirection associationDirection, boolean highLighted, double scaleFactor) { Paint originalPaint = this.g.getPaint(); Stroke originalStroke = this.g.getStroke(); this.g.setPaint(CONNECTION_COLOR); if (connectionType.equals("association")) { this.g.setStroke(ASSOCIATION_STROKE); } else if (highLighted) { this.g.setPaint(HIGHLIGHT_SequenceFlow_COLOR); this.g.setStroke(HIGHLIGHT_FLOW_STROKE); } for (int i = 1; i < xPoints.length; ++i) { int sourceX = xPoints[i - 1]; int sourceY = yPoints[i - 1]; int targetX = xPoints[i]; int targetY = yPoints[i]; java.awt.geom.Line2D.Double line = new java.awt.geom.Line2D.Double((double) sourceX, (double) sourceY, (double) targetX, (double) targetY); this.g.draw(line); } java.awt.geom.Line2D.Double line; if (isDefault) { line = new java.awt.geom.Line2D.Double((double) xPoints[0], (double) yPoints[0], (double) xPoints[1], (double) yPoints[1]); this.drawDefaultSequenceFlowIndicator(line, scaleFactor); } if (conditional) { line = new java.awt.geom.Line2D.Double((double) xPoints[0], (double) yPoints[0], (double) xPoints[1], (double) yPoints[1]); this.drawConditionalSequenceFlowIndicator(line, scaleFactor); } if (associationDirection.equals(AssociationDirection.ONE) || associationDirection.equals(AssociationDirection.BOTH)) { line = new java.awt.geom.Line2D.Double((double) xPoints[xPoints.length - 2], (double) yPoints[xPoints.length - 2], (double) xPoints[xPoints.length - 1], (double) yPoints[xPoints.length - 1]); this.drawArrowHead(line, scaleFactor); } if (associationDirection.equals(AssociationDirection.BOTH)) { line = new java.awt.geom.Line2D.Double((double) xPoints[1], (double) yPoints[1], (double) xPoints[0], (double) yPoints[0]); this.drawArrowHead(line, scaleFactor); } this.g.setPaint(originalPaint); this.g.setStroke(originalStroke); } /** * 设置字体大小图标颜色 * @param imageType */ @Override public void initialize(String imageType) { if ("png".equalsIgnoreCase(imageType)) { this.processDiagram = new BufferedImage(this.canvasWidth, this.canvasHeight, 2); } else { this.processDiagram = new BufferedImage(this.canvasWidth, this.canvasHeight, 1); } this.g = this.processDiagram.createGraphics(); if (!"png".equalsIgnoreCase(imageType)) { this.g.setBackground(new Color(255, 255, 255, 0)); this.g.clearRect(0, 0, this.canvasWidth, this.canvasHeight); } this.g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); //修改图标颜色,修改图标字体大小 this.g.setPaint(Color.black); Font font = new Font(this.activityFontName, 10, 14); this.g.setFont(font); this.fontMetrics = this.g.getFontMetrics(); //修改连接线字体大小 LABEL_FONT = new Font(this.labelFontName, 10, 15); ANNOTATION_FONT = new Font(this.annotationFontName, 0, 11); try { USERTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/userTask.png", this.customClassLoader)); SCRIPTTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/scriptTask.png", this.customClassLoader)); SERVICETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/serviceTask.png", this.customClassLoader)); RECEIVETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/receiveTask.png", this.customClassLoader)); SENDTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/sendTask.png", this.customClassLoader)); MANUALTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/manualTask.png", this.customClassLoader)); BUSINESS_RULE_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/businessRuleTask.png", this.customClassLoader)); SHELL_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/shellTask.png", this.customClassLoader)); DMN_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/dmnTask.png", this.customClassLoader)); CAMEL_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/camelTask.png", this.customClassLoader)); MULE_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/muleTask.png", this.customClassLoader)); HTTP_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/httpTask.png", this.customClassLoader)); TIMER_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/timer.png", this.customClassLoader)); COMPENSATE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/compensate-throw.png", this.customClassLoader)); COMPENSATE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/compensate.png", this.customClassLoader)); ERROR_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/error-throw.png", this.customClassLoader)); ERROR_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/error.png", this.customClassLoader)); MESSAGE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/message-throw.png", this.customClassLoader)); MESSAGE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/message.png", this.customClassLoader)); SIGNAL_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/signal-throw.png", this.customClassLoader)); SIGNAL_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/signal.png", this.customClassLoader)); } catch (IOException var4) { LOGGER.warn("Could not load image for process diagram creation: {}", var4.getMessage()); } } /** * 设置连接线字体 * @param text * @param graphicInfo * @param centered */ @Override public void drawLabel(String text, GraphicInfo graphicInfo, boolean centered) { float interline = 1.0f; // text if (text != null && text.length() > 0) { Paint originalPaint = g.getPaint(); Font originalFont = g.getFont(); g.setPaint(LABEL_COLOR); g.setFont(LABEL_FONT); int wrapWidth = 100; int textY = (int) graphicInfo.getY(); // TODO: use drawMultilineText() AttributedString as = new AttributedString(text); as.addAttribute(TextAttribute.FOREGROUND, g.getPaint()); as.addAttribute(TextAttribute.FONT, g.getFont()); AttributedCharacterIterator aci = as.getIterator(); FontRenderContext frc = new FontRenderContext(null, true, false); LineBreakMeasurer lbm = new LineBreakMeasurer(aci, frc); while (lbm.getPosition() < text.length()) { TextLayout tl = lbm.nextLayout(wrapWidth); textY += tl.getAscent(); Rectangle2D bb = tl.getBounds(); double tX = graphicInfo.getX(); if (centered) { tX += (int) (graphicInfo.getWidth() / 2 - bb.getWidth() / 2); } tl.draw(g, (float) tX, textY); textY += tl.getDescent() + tl.getLeading() + (interline - 1.0f) * tl.getAscent(); } // restore originals g.setFont(originalFont); g.setPaint(originalPaint); } } /** * 高亮显示task框完成的 * @param x * @param y * @param width * @param height */ @Override public void drawHighLight(int x, int y, int width, int height) { Paint originalPaint = g.getPaint(); Stroke originalStroke = g.getStroke(); g.setPaint(HIGHLIGHT_COLOR); g.setStroke(THICK_TASK_BORDER_STROKE); RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20); g.draw(rect); g.setPaint(originalPaint); g.setStroke(originalStroke); } /** * 自定义task框当前的位置 * @param x * @param y * @param width * @param height */ public void drawHighLightNow(int x, int y, int width, int height) { Paint originalPaint = g.getPaint(); Stroke originalStroke = g.getStroke(); g.setPaint(HIGHLIGHT_COLOR1); g.setStroke(THICK_TASK_BORDER_STROKE); RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20); g.draw(rect); g.setPaint(originalPaint); g.setStroke(originalStroke); } /** * 自定义结束节点 * @param x * @param y * @param width * @param height */ public void drawHighLightEnd(int x, int y, int width, int height) { Paint originalPaint = g.getPaint(); Stroke originalStroke = g.getStroke(); g.setPaint(HIGHLIGHT_COLOR); g.setStroke(THICK_TASK_BORDER_STROKE); RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20); g.draw(rect); g.setPaint(originalPaint); g.setStroke(originalStroke); } /** * task框自定义文字 * @param name * @param graphicInfo * @param thickBorder * @param scaleFactor */ @Override protected void drawTask(String name, GraphicInfo graphicInfo, boolean thickBorder, double scaleFactor) { Paint originalPaint = g.getPaint(); int x = (int) graphicInfo.getX(); int y = (int) graphicInfo.getY(); int width = (int) graphicInfo.getWidth(); int height = (int) graphicInfo.getHeight(); // Create a new gradient paint for every task box, gradient depends on x and y and is not relative g.setPaint(TASK_BOX_COLOR); int arcR = 6; if (thickBorder) { arcR = 3; } // shape RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, arcR, arcR); g.fill(rect); g.setPaint(TASK_BORDER_COLOR); if (thickBorder) { Stroke originalStroke = g.getStroke(); g.setStroke(THICK_TASK_BORDER_STROKE); g.draw(rect); g.setStroke(originalStroke); } else { g.draw(rect); } g.setPaint(originalPaint); // text if (scaleFactor == 1.0 && name != null && name.length() > 0) { int boxWidth = width - (2 * TEXT_PADDING); int boxHeight = height - 16 - ICON_PADDING - ICON_PADDING - MARKER_WIDTH - 2 - 2; int boxX = x + width / 2 - boxWidth / 2; int boxY = y + height / 2 - boxHeight / 2 + ICON_PADDING + ICON_PADDING - 2 - 2; drawMultilineCentredText(name, boxX, boxY, boxWidth, boxHeight); } } protected static Color EVENT_COLOR = new Color(255, 255, 255); /** * 重写开始事件 * @param graphicInfo * @param image * @param scaleFactor */ @Override public void drawStartEvent(GraphicInfo graphicInfo, BufferedImage image, double scaleFactor) { Paint originalPaint = g.getPaint(); g.setPaint(EVENT_COLOR); Ellipse2D circle = new Ellipse2D.Double(graphicInfo.getX(), graphicInfo.getY(), graphicInfo.getWidth(), graphicInfo.getHeight()); g.fill(circle); g.setPaint(EVENT_BORDER_COLOR); g.draw(circle); g.setPaint(originalPaint); if (image != null) { // calculate coordinates to center image int imageX = (int) Math.round(graphicInfo.getX() + (graphicInfo.getWidth() / 2) - (image.getWidth() / (2 * scaleFactor))); int imageY = (int) Math.round(graphicInfo.getY() + (graphicInfo.getHeight() / 2) - (image.getHeight() / (2 * scaleFactor))); g.drawImage(image, imageX, imageY, (int) (image.getWidth() / scaleFactor), (int) (image.getHeight() / scaleFactor), null); } } /** * 重写结束事件 * @param graphicInfo * @param scaleFactor */ @Override public void drawNoneEndEvent(GraphicInfo graphicInfo, double scaleFactor) { Paint originalPaint = g.getPaint(); Stroke originalStroke = g.getStroke(); g.setPaint(EVENT_COLOR); Ellipse2D circle = new Ellipse2D.Double(graphicInfo.getX(), graphicInfo.getY(), graphicInfo.getWidth(), graphicInfo.getHeight()); g.fill(circle); g.setPaint(EVENT_BORDER_COLOR); // g.setPaint(HIGHLIGHT_COLOR); if (scaleFactor == 1.0) { g.setStroke(END_EVENT_STROKE); } else { g.setStroke(new BasicStroke(2.0f)); } g.draw(circle); g.setStroke(originalStroke); g.setPaint(originalPaint); } }