Activiti6.0查看流程图 流程追踪图,双色高亮显示

2023-12-23 06:40

本文主要是介绍Activiti6.0查看流程图 流程追踪图,双色高亮显示,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Contorllor

/*** 根据流程定义id查看流程图** @param processDefinitionId* @return*/@GetMapping(value = "/image/{processDefinitionId}")@ApiOperation(value = "根据流程定义id查看流程图", notes = "根据流程定义id,查询返回不带节点流转信息的图片")public void image(@PathVariable("processDefinitionId") String processDefinitionId, HttpServletResponse response) {try (OutputStream out = response.getOutputStream();InputStream is = imageService.getFlowImgByProcInstId(processDefinitionId, null, false)) {if (null != is) {BufferedImage image = ImageIO.read(is);response.setContentType("image/png");ImageIO.write(image, "png", out);}} catch (Exception ex) {log.error("查看流程图失败", ex);}}

Service

package com.ruifu.act.service;import java.io.InputStream;/*** @author Administrator*/
public interface ImageService {/*** 获取指定流程对应的流程图image** @param procDefId   流程模版定义id* @param procInstId  流程实例id* @param showHistory 是否显示流程图历史节点渲染.<br/>true:显示历史节点,使用procInstId。<br/>false:不显示历史节点,直接使用procDefId。* @return* @throws Exception*/InputStream getFlowImgByProcInstId(String procDefId, String procInstId, boolean showHistory) throws Exception;}

根据流程实例Id,获取实时流程图片 实现类

package com.ruifu.act.service.impl;import com.ruifu.act.service.ImageService;
import com.ruifu.act.utils.CustomProcessDiagramGenerator;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.runtime.Execution;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;/*** 根据流程实例Id,获取实时流程图片实现类** @author liuxz*/
@Service
@Transactional(rollbackFor = Exception.class)
@Slf4j
public class ImageServiceImpl implements ImageService {@Autowiredprivate RepositoryService repositoryService;@Autowiredprivate HistoryService historyService;@Autowiredprivate RuntimeService runtimeService;/*** 根据流程实例Id,获取实时流程图片** @param procDefId   流程模版定义id* @param procInstId  流程实例id* @param showHistory 是否显示流程图历史节点渲染.<br/>true:显示历史节点,使用procInstId。<br/>false:不显示历史节点,直接使用procDefId。* @return* @throws Exception*/@Overridepublic InputStream getFlowImgByProcInstId(String procDefId, String procInstId, boolean showHistory) throws Exception {InputStream imageStream = null;BpmnModel bpmnModel = null;List<String> highLightedActivitiIdList = new ArrayList<>();List<String> runningActivitiIdList = new ArrayList<>();List<String> highLightedFlowIds = new ArrayList<>();if (showHistory && StringUtils.isNotEmpty(procInstId)) {// 通过流程实例ID获取历史流程实例HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(procInstId).singleResult();if (null != historicProcessInstance) {// 获取流程定义Model对象bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());//如果需要展示历史节点记录,则需要查询出对应的历史节点信息if (showHistory) {// 通过流程实例ID获取流程中已经执行的节点,按照执行先后顺序排序List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery().processInstanceId(procInstId).orderByHistoricActivityInstanceId().asc().list();// 将已经执行的节点ID放入高亮显示节点集合for (HistoricActivityInstance historicActivityInstance : historicActivityInstanceList) {highLightedActivitiIdList.add(historicActivityInstance.getActivityId());}// 通过流程实例ID获取流程中正在执行的节点List<Execution> runningActivityInstanceList = runtimeService.createExecutionQuery().processInstanceId(procInstId).list();for (Execution execution : runningActivityInstanceList) {if (StringUtils.isNotEmpty(execution.getActivityId())) {runningActivitiIdList.add(execution.getActivityId());}}// 获取已流经的流程线,需要高亮显示高亮流程已发生流转的线id集合highLightedFlowIds.addAll(getHighLightedFlows(bpmnModel, historicActivityInstanceList));}}} else if (!showHistory && StringUtils.isNotEmpty(procDefId)) {bpmnModel = repositoryService.getBpmnModel(procDefId);}try {if (null != bpmnModel) {// 定义流程画布生成器CustomProcessDiagramGenerator processDiagramGenerator = new CustomProcessDiagramGenerator();// 使用默认配置获得流程图表生成器,并生成追踪图片字符流imageStream = processDiagramGenerator.generateDiagramCustom(bpmnModel, "png",highLightedActivitiIdList, runningActivitiIdList, highLightedFlowIds,"宋体", "黑体", "黑体",null, 2.0);}} catch (Exception e) {log.error("通过流程实例ID[{}]获取流程图时出现异常!", procInstId, e);throw new Exception("通过流程实例ID" + procInstId + "获取流程图时出现异常!", e);}return imageStream;}/*** 获取已流经的流程线,需要高亮显示高亮流程已发生流转的线id集合** @param bpmnModel* @param historicActivityInstanceList* @return*/private List<String> getHighLightedFlows(BpmnModel bpmnModel,List<HistoricActivityInstance> historicActivityInstanceList) {// 已流经的流程线,需要高亮显示List<String> highLightedFlowIdList = new ArrayList<>();// 全部活动节点List<FlowNode> allHistoricActivityNodeList = new ArrayList<>();// 已完成的历史活动节点List<HistoricActivityInstance> finishedActivityInstanceList = new ArrayList<>();for (HistoricActivityInstance historicActivityInstance : historicActivityInstanceList) {// 获取流程节点FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstance.getActivityId(), true);allHistoricActivityNodeList.add(flowNode);// 结束时间不为空,当前节点则已经完成if (historicActivityInstance.getEndTime() != null) {finishedActivityInstanceList.add(historicActivityInstance);}}FlowNode currentFlowNode;FlowNode targetFlowNode;HistoricActivityInstance currentActivityInstance;// 遍历已完成的活动实例,从每个实例的outgoingFlows中找到已执行的for (int k = 0; k < finishedActivityInstanceList.size(); k++) {currentActivityInstance = finishedActivityInstanceList.get(k);// 获得当前活动对应的节点信息及outgoingFlows信息currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivityInstance.getActivityId(), true);// 当前节点的所有流出线List<SequenceFlow> outgoingFlowList = currentFlowNode.getOutgoingFlows();/*** 遍历outgoingFlows并找到已流转的 满足如下条件认为已流转:* 1.当前节点是并行网关或兼容网关,则通过outgoingFlows能够在历史活动中找到的全部节点均为已流转* 2.当前节点是以上两种类型之外的,通过outgoingFlows查找到的时间最早的流转节点视为有效流转* (第2点有问题,有过驳回的,会只绘制驳回的流程线,通过走向下一级的流程线没有高亮显示)*/if ("parallelGateway".equals(currentActivityInstance.getActivityType()) || "inclusiveGateway".equals(currentActivityInstance.getActivityType())) {// 遍历历史活动节点,找到匹配流程目标节点的for (SequenceFlow outgoingFlow : outgoingFlowList) {// 获取当前节点流程线对应的下级节点targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(outgoingFlow.getTargetRef(),true);// 如果下级节点包含在所有历史节点中,则将当前节点的流出线高亮显示if (allHistoricActivityNodeList.contains(targetFlowNode)) {highLightedFlowIdList.add(outgoingFlow.getId());}}} else {/*** 2、当前节点不是并行网关或兼容网关* 【已解决-问题】如果当前节点有驳回功能,驳回到申请节点,* 则因为申请节点在历史节点中,导致当前节点驳回到申请节点的流程线被高亮显示,但实际并没有进行驳回操作*/// 当前节点IDString currentActivityId = currentActivityInstance.getActivityId();int size = historicActivityInstanceList.size();boolean ifStartFind = false;boolean ifFinded = false;HistoricActivityInstance historicActivityInstance;// 循环当前节点的所有流出线// 循环所有历史节点//log.info("【开始】-匹配当前节点-ActivityId=【{}】需要高亮显示的流出线", currentActivityId);//log.info("循环历史节点");for (int i = 0; i < historicActivityInstanceList.size(); i++) {// // 如果当前节点流程线对应的下级节点在历史节点中,则该条流程线进行高亮显示(【问题】有驳回流程线时,即使没有进行驳回操作,因为申请节点在历史节点中,也会将驳回流程线高亮显示-_-||)// if (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef())) {// Map<String, Object> map = new HashMap<>();// map.put("highLightedFlowId", sequenceFlow.getId());// map.put("highLightedFlowStartTime", historicActivityInstance.getStartTime().getTime());// tempMapList.add(map);// // highLightedFlowIdList.add(sequenceFlow.getId());// }// 历史节点historicActivityInstance = historicActivityInstanceList.get(i);//log.info("第【{}/{}】个历史节点-ActivityId=[{}]", i + 1, size, historicActivityInstance.getActivityId());// 如果循环历史节点中的id等于当前节点id,从当前历史节点继续先后查找是否有当前节点流程线等于的节点// 历史节点的序号需要大于等于已完成历史节点的序号,防止驳回重审一个节点经过两次是只取第一次的流出线高亮显示,第二次的不显示if (i >= k && historicActivityInstance.getActivityId().equals(currentActivityId)) {//log.info("第[{}]个历史节点和当前节点一致-ActivityId=[{}]", i + 1, historicActivityInstance.getActivityId());ifStartFind = true;// 跳过当前节点继续查找下一个节点continue;}if (ifStartFind) {//log.info("[开始]-循环当前节点-ActivityId=【{}】的所有流出线", currentActivityId);ifFinded = false;for (SequenceFlow sequenceFlow : outgoingFlowList) {// 如果当前节点流程线对应的下级节点在其后面的历史节点中,则该条流程线进行高亮显示// 【问题】//log.info("当前流出线的下级节点=[{}]", sequenceFlow.getTargetRef());if (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef())) {//log.info("当前节点[{}]需高亮显示的流出线=[{}]", currentActivityId, sequenceFlow.getId());highLightedFlowIdList.add(sequenceFlow.getId());// 暂时默认找到离当前节点最近的下一级节点即退出循环,否则有多条流出线时将全部被高亮显示ifFinded = true;break;}}//log.info("[完成]-循环当前节点-ActivityId=【{}】的所有流出线", currentActivityId);}if (ifFinded) {// 暂时默认找到离当前节点最近的下一级节点即退出历史节点循环,否则有多条流出线时将全部被高亮显示break;}}//log.info("【完成】-匹配当前节点-ActivityId=【{}】需要高亮显示的流出线", currentActivityId);}}return highLightedFlowIdList;}
}

定义流程画布生成器

package com.ruifu.act.utils;import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.*;
import org.activiti.image.ProcessDiagramGenerator;import java.awt.image.BufferedImage;
import java.io.InputStream;
import java.util.*;/*** Class to generate an image based the diagram interchange information in a BPMN 2.0 process.** @author Joram Barrez* @author Tijs Rademakers*/
@Slf4j
public class CustomProcessDiagramGenerator implements ProcessDiagramGenerator {protected Map<Class<? extends BaseElement>, ActivityDrawInstruction> activityDrawInstructions = new HashMap<Class<? extends BaseElement>, ActivityDrawInstruction>();protected Map<Class<? extends BaseElement>, ArtifactDrawInstruction> artifactDrawInstructions = new HashMap<Class<? extends BaseElement>, ArtifactDrawInstruction>();public CustomProcessDiagramGenerator() {this(1.0);}/*** The instructions on how to draw a certain construct is* created statically and stored in a map for performance.*/public CustomProcessDiagramGenerator(final double scaleFactor) {// start eventactivityDrawInstructions.put(StartEvent.class, new ActivityDrawInstruction() {@Overridepublic void draw(CustomProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());StartEvent startEvent = (StartEvent) flowNode;if (startEvent.getEventDefinitions() != null && !startEvent.getEventDefinitions().isEmpty()) {EventDefinition eventDefinition = startEvent.getEventDefinitions().get(0);if (eventDefinition instanceof TimerEventDefinition) {processDiagramCanvas.drawTimerStartEvent(graphicInfo, scaleFactor);} else if (eventDefinition instanceof ErrorEventDefinition) {processDiagramCanvas.drawErrorStartEvent(graphicInfo, scaleFactor);} else if (eventDefinition instanceof SignalEventDefinition) {processDiagramCanvas.drawSignalStartEvent(graphicInfo, scaleFactor);} else if (eventDefinition instanceof MessageEventDefinition) {processDiagramCanvas.drawMessageStartEvent(graphicInfo, scaleFactor);} else {processDiagramCanvas.drawNoneStartEvent(graphicInfo);}} else {processDiagramCanvas.drawNoneStartEvent(graphicInfo);}}});// signal catchactivityDrawInstructions.put(IntermediateCatchEvent.class, new ActivityDrawInstruction() {@Overridepublic void draw(CustomProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());IntermediateCatchEvent intermediateCatchEvent = (IntermediateCatchEvent) flowNode;if (intermediateCatchEvent.getEventDefinitions() != null && !intermediateCatchEvent.getEventDefinitions().isEmpty()) {if (intermediateCatchEvent.getEventDefinitions().get(0) instanceof SignalEventDefinition) {processDiagramCanvas.drawCatchingSignalEvent(flowNode.getName(), graphicInfo, true,scaleFactor);} else if (intermediateCatchEvent.getEventDefinitions().get(0) instanceof TimerEventDefinition) {processDiagramCanvas.drawCatchingTimerEvent(flowNode.getName(), graphicInfo, true, scaleFactor);} else if (intermediateCatchEvent.getEventDefinitions().get(0) instanceof MessageEventDefinition) {processDiagramCanvas.drawCatchingMessageEvent(flowNode.getName(), graphicInfo, true,scaleFactor);}}}});// signal throwactivityDrawInstructions.put(ThrowEvent.class, new ActivityDrawInstruction() {@Overridepublic void draw(CustomProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());ThrowEvent throwEvent = (ThrowEvent) flowNode;if (throwEvent.getEventDefinitions() != null && !throwEvent.getEventDefinitions().isEmpty()) {if (throwEvent.getEventDefinitions().get(0) instanceof SignalEventDefinition) {processDiagramCanvas.drawThrowingSignalEvent(graphicInfo, scaleFactor);} else if (throwEvent.getEventDefinitions().get(0) instanceof CompensateEventDefinition) {processDiagramCanvas.drawThrowingCompensateEvent(graphicInfo, scaleFactor);} else {processDiagramCanvas.drawThrowingNoneEvent(graphicInfo, scaleFactor);}} else {processDiagramCanvas.drawThrowingNoneEvent(graphicInfo, scaleFactor);}}});// end eventactivityDrawInstructions.put(EndEvent.class, new ActivityDrawInstruction() {@Overridepublic void draw(CustomProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());EndEvent endEvent = (EndEvent) flowNode;if (endEvent.getEventDefinitions() != null && !endEvent.getEventDefinitions().isEmpty()) {if (endEvent.getEventDefinitions().get(0) instanceof ErrorEventDefinition) {processDiagramCanvas.drawErrorEndEvent(flowNode.getName(), graphicInfo, scaleFactor);} else {processDiagramCanvas.drawNoneEndEvent(graphicInfo, scaleFactor);}} else {processDiagramCanvas.drawNoneEndEvent(graphicInfo, scaleFactor);}}});// taskactivityDrawInstructions.put(Task.class, new ActivityDrawInstruction() {@Overridepublic void draw(CustomProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());processDiagramCanvas.drawTask(flowNode.getName(), graphicInfo);}});// user taskactivityDrawInstructions.put(UserTask.class, new ActivityDrawInstruction() {@Overridepublic void draw(CustomProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());processDiagramCanvas.drawUserTask(flowNode.getName(), graphicInfo, scaleFactor);}});// script taskactivityDrawInstructions.put(ScriptTask.class, new ActivityDrawInstruction() {@Overridepublic void draw(CustomProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());processDiagramCanvas.drawScriptTask(flowNode.getName(), graphicInfo, scaleFactor);}});// service taskactivityDrawInstructions.put(ServiceTask.class, new ActivityDrawInstruction() {@Overridepublic void draw(CustomProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());ServiceTask serviceTask = (ServiceTask) flowNode;if ("camel".equalsIgnoreCase(serviceTask.getType())) {processDiagramCanvas.drawCamelTask(serviceTask.getName(), graphicInfo, scaleFactor);} else if ("mule".equalsIgnoreCase(serviceTask.getType())) {processDiagramCanvas.drawMuleTask(serviceTask.getName(), graphicInfo, scaleFactor);} else {processDiagramCanvas.drawServiceTask(serviceTask.getName(), graphicInfo, scaleFactor);}}});// receive taskactivityDrawInstructions.put(ReceiveTask.class, new ActivityDrawInstruction() {@Overridepublic void draw(CustomProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());processDiagramCanvas.drawReceiveTask(flowNode.getName(), graphicInfo, scaleFactor);}});// send taskactivityDrawInstructions.put(SendTask.class, new ActivityDrawInstruction() {@Overridepublic void draw(CustomProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());processDiagramCanvas.drawSendTask(flowNode.getName(), graphicInfo, scaleFactor);}});// manual taskactivityDrawInstructions.put(ManualTask.class, new ActivityDrawInstruction() {@Overridepublic void draw(CustomProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());processDiagramCanvas.drawManualTask(flowNode.getName(), graphicInfo, scaleFactor);}});// businessRuleTask taskactivityDrawInstructions.put(BusinessRuleTask.class, new ActivityDrawInstruction() {@Overridepublic void draw(CustomProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());processDiagramCanvas.drawBusinessRuleTask(flowNode.getName(), graphicInfo, scaleFactor);}});// exclusive gatewayactivityDrawInstructions.put(ExclusiveGateway.class, new ActivityDrawInstruction() {@Overridepublic void draw(CustomProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());processDiagramCanvas.drawExclusiveGateway(graphicInfo, scaleFactor);}});// inclusive gatewayactivityDrawInstructions.put(InclusiveGateway.class, new ActivityDrawInstruction() {@Overridepublic void draw(CustomProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());processDiagramCanvas.drawInclusiveGateway(graphicInfo, scaleFactor);}});// parallel gatewayactivityDrawInstructions.put(ParallelGateway.class, new ActivityDrawInstruction() {@Overridepublic void draw(CustomProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());processDiagramCanvas.drawParallelGateway(graphicInfo, scaleFactor);}});// event based gatewayactivityDrawInstructions.put(EventGateway.class, new ActivityDrawInstruction() {@Overridepublic void draw(CustomProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());processDiagramCanvas.drawEventBasedGateway(graphicInfo, scaleFactor);}});// Boundary timeractivityDrawInstructions.put(BoundaryEvent.class, new ActivityDrawInstruction() {@Overridepublic void draw(CustomProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());BoundaryEvent boundaryEvent = (BoundaryEvent) flowNode;if (boundaryEvent.getEventDefinitions() != null && !boundaryEvent.getEventDefinitions().isEmpty()) {if (boundaryEvent.getEventDefinitions().get(0) instanceof TimerEventDefinition) {processDiagramCanvas.drawCatchingTimerEvent(flowNode.getName(), graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);} else if (boundaryEvent.getEventDefinitions().get(0) instanceof ErrorEventDefinition) {processDiagramCanvas.drawCatchingErrorEvent(graphicInfo, boundaryEvent.isCancelActivity(),scaleFactor);} else if (boundaryEvent.getEventDefinitions().get(0) instanceof SignalEventDefinition) {processDiagramCanvas.drawCatchingSignalEvent(flowNode.getName(), graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);} else if (boundaryEvent.getEventDefinitions().get(0) instanceof MessageEventDefinition) {processDiagramCanvas.drawCatchingMessageEvent(flowNode.getName(), graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);} else if (boundaryEvent.getEventDefinitions().get(0) instanceof CompensateEventDefinition) {processDiagramCanvas.drawCatchingCompensateEvent(graphicInfo, boundaryEvent.isCancelActivity(),scaleFactor);}}}});// subprocessactivityDrawInstructions.put(SubProcess.class, new ActivityDrawInstruction() {@Overridepublic void draw(CustomProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());if (graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) {processDiagramCanvas.drawCollapsedSubProcess(flowNode.getName(), graphicInfo, false);} else {processDiagramCanvas.drawExpandedSubProcess(flowNode.getName(), graphicInfo, false, scaleFactor);}}});// Event subprocessactivityDrawInstructions.put(EventSubProcess.class, new ActivityDrawInstruction() {@Overridepublic void draw(CustomProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());if (graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) {processDiagramCanvas.drawCollapsedSubProcess(flowNode.getName(), graphicInfo, true);} else {processDiagramCanvas.drawExpandedSubProcess(flowNode.getName(), graphicInfo, true, scaleFactor);}}});// call activityactivityDrawInstructions.put(CallActivity.class, new ActivityDrawInstruction() {@Overridepublic void draw(CustomProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());processDiagramCanvas.drawCollapsedCallActivity(flowNode.getName(), graphicInfo);}});// text annotationartifactDrawInstructions.put(TextAnnotation.class, new ArtifactDrawInstruction() {@Overridepublic void draw(CustomProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, Artifact artifact) {GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(artifact.getId());TextAnnotation textAnnotation = (TextAnnotation) artifact;processDiagramCanvas.drawTextAnnotation(textAnnotation.getText(), graphicInfo);}});// associationartifactDrawInstructions.put(Association.class, new ArtifactDrawInstruction() {@Overridepublic void draw(CustomProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, Artifact artifact) {Association association = (Association) artifact;String sourceRef = association.getSourceRef();String targetRef = association.getTargetRef();// source and target can be instance of FlowElement or ArtifactBaseElement sourceElement = bpmnModel.getFlowElement(sourceRef);BaseElement targetElement = bpmnModel.getFlowElement(targetRef);if (sourceElement == null) {sourceElement = bpmnModel.getArtifact(sourceRef);}if (targetElement == null) {targetElement = bpmnModel.getArtifact(targetRef);}List<GraphicInfo> graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(artifact.getId());graphicInfoList = connectionPerfectionizer(processDiagramCanvas, bpmnModel, sourceElement,targetElement, graphicInfoList);int[] xPoints = new int[graphicInfoList.size()];int[] yPoints = new int[graphicInfoList.size()];for (int i = 1; i < graphicInfoList.size(); i++) {GraphicInfo graphicInfo = graphicInfoList.get(i);GraphicInfo previousGraphicInfo = graphicInfoList.get(i - 1);if (i == 1) {xPoints[0] = (int) previousGraphicInfo.getX();yPoints[0] = (int) previousGraphicInfo.getY();}xPoints[i] = (int) graphicInfo.getX();yPoints[i] = (int) graphicInfo.getY();}AssociationDirection associationDirection = association.getAssociationDirection();processDiagramCanvas.drawAssociation(xPoints, yPoints, associationDirection, false, scaleFactor);}});}public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities,List<String> highLightedFlows, String activityFontName, String labelFontName, ClassLoader customClassLoader,double scaleFactor) {return generateProcessDiagram(bpmnModel, imageType, highLightedActivities, new ArrayList<String>(),highLightedFlows, activityFontName, labelFontName, customClassLoader, scaleFactor).generateImage(imageType);}@Overridepublic InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities,List<String> highLightedFlows) {return generateDiagram(bpmnModel, imageType, highLightedActivities, highLightedFlows, null, null, null, 1.0);}@Overridepublic InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities,List<String> highLightedFlows, double scaleFactor) {return generateDiagram(bpmnModel, imageType, highLightedActivities, highLightedFlows, null, null, null,scaleFactor);}@Overridepublic InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities) {return generateDiagram(bpmnModel, imageType, highLightedActivities, Collections.<String>emptyList());}@Overridepublic InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities,double scaleFactor) {return generateDiagram(bpmnModel, imageType, highLightedActivities, Collections.<String>emptyList(),scaleFactor);}public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, String activityFontName,String labelFontName, ClassLoader customClassLoader) {return generateDiagram(bpmnModel, imageType, Collections.<String>emptyList(), Collections.<String>emptyList(),activityFontName, labelFontName, customClassLoader, 1.0);}public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, String activityFontName,String labelFontName, ClassLoader customClassLoader, double scaleFactor) {return generateDiagram(bpmnModel, imageType, Collections.<String>emptyList(), Collections.<String>emptyList(),activityFontName, labelFontName, customClassLoader, scaleFactor);}@Overridepublic InputStream generatePngDiagram(BpmnModel bpmnModel) {return generatePngDiagram(bpmnModel, 1.0);}@Overridepublic InputStream generatePngDiagram(BpmnModel bpmnModel, double scaleFactor) {return generateDiagram(bpmnModel, "png", Collections.<String>emptyList(), Collections.<String>emptyList(),scaleFactor);}@Overridepublic InputStream generateJpgDiagram(BpmnModel bpmnModel) {return generateJpgDiagram(bpmnModel, 1.0);}@Overridepublic InputStream generateJpgDiagram(BpmnModel bpmnModel, double scaleFactor) {return generateDiagram(bpmnModel, "jpg", Collections.<String>emptyList(), Collections.<String>emptyList());}public BufferedImage generateImage(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities,List<String> highLightedFlows, String activityFontName, String labelFontName, ClassLoader customClassLoader,double scaleFactor) {return generateProcessDiagram(bpmnModel, imageType, highLightedActivities, new ArrayList<String>(),highLightedFlows, activityFontName, labelFontName, customClassLoader, scaleFactor).generateBufferedImage(imageType);}public BufferedImage generateImage(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities,List<String> highLightedFlows, double scaleFactor) {return generateImage(bpmnModel, imageType, highLightedActivities, highLightedFlows, null, null, null,scaleFactor);}@Overridepublic BufferedImage generatePngImage(BpmnModel bpmnModel, double scaleFactor) {return generateImage(bpmnModel, "png", Collections.<String>emptyList(), Collections.<String>emptyList(),scaleFactor);}protected CustomProcessDiagramCanvas generateProcessDiagram(BpmnModel bpmnModel, String imageType,List<String> highLightedActivities, List<String> runningActivitiIdList, List<String> highLightedFlows,String activityFontName, String labelFontName, ClassLoader customClassLoader, double scaleFactor) {CustomProcessDiagramCanvas processDiagramCanvas = initProcessDiagramCanvas(bpmnModel, imageType,activityFontName, labelFontName, customClassLoader);// Draw pool shape, if process is participant in collaborationfor (Pool pool : bpmnModel.getPools()) {GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(pool.getId());processDiagramCanvas.drawPoolOrLane(pool.getName(), graphicInfo);}// Draw lanesfor (Process process : bpmnModel.getProcesses()) {for (Lane lane : process.getLanes()) {GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(lane.getId());processDiagramCanvas.drawPoolOrLane(lane.getName(), graphicInfo);}}// Draw activities and their sequence-flows/*** 绘制流程图上的所有节点和流程线,对高亮显示的节点和流程线进行特殊处理*/for (FlowNode flowNode : bpmnModel.getProcesses().get(0).findFlowElementsOfType(FlowNode.class)) {drawActivity(processDiagramCanvas, bpmnModel, flowNode, highLightedActivities, runningActivitiIdList,highLightedFlows, scaleFactor);}// Draw artifactsfor (Process process : bpmnModel.getProcesses()) {for (Artifact artifact : process.getArtifacts()) {drawArtifact(processDiagramCanvas, bpmnModel, artifact);}}return processDiagramCanvas;}/*** Desc: 绘制流程图上的所有节点和流程线,对高亮显示的节点和流程线进行特殊处理** @param processDiagramCanvas* @param bpmnModel* @param flowNode* @param highLightedActivities* @param highLightedFlows* @param scaleFactor* @author Fuxs*/protected void drawActivity(CustomProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode,List<String> highLightedActivities, List<String> runningActivitiIdList, List<String> highLightedFlows,double scaleFactor) {ActivityDrawInstruction drawInstruction = activityDrawInstructions.get(flowNode.getClass());if (drawInstruction != null) {drawInstruction.draw(processDiagramCanvas, bpmnModel, flowNode);// Gather info on the multi instance markerboolean multiInstanceSequential = false, multiInstanceParallel = false, collapsed = false;if (flowNode instanceof Activity) {Activity activity = (Activity) flowNode;MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = activity.getLoopCharacteristics();if (multiInstanceLoopCharacteristics != null) {multiInstanceSequential = multiInstanceLoopCharacteristics.isSequential();multiInstanceParallel = !multiInstanceSequential;}}// Gather info on the collapsed markerGraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());if (flowNode instanceof SubProcess) {collapsed = graphicInfo.getExpanded() != null && !graphicInfo.getExpanded();} else if (flowNode instanceof CallActivity) {collapsed = true;}if (scaleFactor == 1.0) {// Actually draw the markersprocessDiagramCanvas.drawActivityMarkers((int) graphicInfo.getX(), (int) graphicInfo.getY(),(int) graphicInfo.getWidth(), (int) graphicInfo.getHeight(), multiInstanceSequential,multiInstanceParallel, collapsed);}// Draw highlighted activitiesif (highLightedActivities.contains(flowNode.getId())) {/*** 如果节点为当前正在处理中的节点,则红色高亮显示*/if (runningActivitiIdList.contains(flowNode.getId())) {log.debug("[绘制]-当前正在处理中的节点-红色高亮显示节点[{}-{}]", flowNode.getId(), flowNode.getName());drawRunningActivitiHighLight(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId()));} else {log.debug("[绘制]-高亮显示节点[{}-{}]", flowNode.getId(), flowNode.getName());drawHighLight(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId()));}}}// Outgoing transitions of activity/*** 绘制当前节点的流程线*/for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) {boolean highLighted = (highLightedFlows.contains(sequenceFlow.getId()));String defaultFlow = null;if (flowNode instanceof Activity) {defaultFlow = ((Activity) flowNode).getDefaultFlow();} else if (flowNode instanceof Gateway) {defaultFlow = ((Gateway) flowNode).getDefaultFlow();}boolean isDefault = false;if (defaultFlow != null && defaultFlow.equalsIgnoreCase(sequenceFlow.getId())) {isDefault = true;}boolean drawConditionalIndicator = sequenceFlow.getConditionExpression() != null&& !(flowNode instanceof Gateway);String sourceRef = sequenceFlow.getSourceRef();String targetRef = sequenceFlow.getTargetRef();FlowElement sourceElement = bpmnModel.getFlowElement(sourceRef);FlowElement targetElement = bpmnModel.getFlowElement(targetRef);List<GraphicInfo> graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(sequenceFlow.getId());if (graphicInfoList != null && graphicInfoList.size() > 0) {graphicInfoList = connectionPerfectionizer(processDiagramCanvas, bpmnModel, sourceElement,targetElement, graphicInfoList);int[] xPoints = new int[graphicInfoList.size()];int[] yPoints = new int[graphicInfoList.size()];for (int i = 1; i < graphicInfoList.size(); i++) {GraphicInfo graphicInfo = graphicInfoList.get(i);GraphicInfo previousGraphicInfo = graphicInfoList.get(i - 1);if (i == 1) {xPoints[0] = (int) previousGraphicInfo.getX();yPoints[0] = (int) previousGraphicInfo.getY();}xPoints[i] = (int) graphicInfo.getX();yPoints[i] = (int) graphicInfo.getY();}processDiagramCanvas.drawSequenceflow(xPoints, yPoints, drawConditionalIndicator, isDefault,highLighted, scaleFactor);// Draw sequenceflow label/*** 绘制流程线名称*/GraphicInfo labelGraphicInfo = bpmnModel.getLabelGraphicInfo(sequenceFlow.getId());
//				if (labelGraphicInfo != null) {GraphicInfo lineCenter = getLineCenter(graphicInfoList);processDiagramCanvas.drawLabel(sequenceFlow.getName(), lineCenter, true);
//				}}}// Nested elementsif (flowNode instanceof FlowElementsContainer) {for (FlowElement nestedFlowElement : ((FlowElementsContainer) flowNode).getFlowElements()) {if (nestedFlowElement instanceof FlowNode) {drawActivity(processDiagramCanvas, bpmnModel, (FlowNode) nestedFlowElement, highLightedActivities,runningActivitiIdList, highLightedFlows, scaleFactor);}}}}/*** This method makes coordinates of connection flow better.** @param processDiagramCanvas* @param bpmnModel* @param sourceElement* @param targetElement* @param graphicInfoList* @return*/protected static List<GraphicInfo> connectionPerfectionizer(CustomProcessDiagramCanvas processDiagramCanvas,BpmnModel bpmnModel, BaseElement sourceElement, BaseElement targetElement,List<GraphicInfo> graphicInfoList) {GraphicInfo sourceGraphicInfo = bpmnModel.getGraphicInfo(sourceElement.getId());GraphicInfo targetGraphicInfo = bpmnModel.getGraphicInfo(targetElement.getId());CustomProcessDiagramCanvas.SHAPE_TYPE sourceShapeType = getShapeType(sourceElement);CustomProcessDiagramCanvas.SHAPE_TYPE targetShapeType = getShapeType(targetElement);return processDiagramCanvas.connectionPerfectionizer(sourceShapeType, targetShapeType, sourceGraphicInfo,targetGraphicInfo, graphicInfoList);}/*** This method returns shape type of base element.<br>* Each element can be presented as rectangle, rhombus, or ellipse.** @param baseElement* @return CustomProcessDiagramCanvas.SHAPE_TYPE*/protected static CustomProcessDiagramCanvas.SHAPE_TYPE getShapeType(BaseElement baseElement) {if (baseElement instanceof Task || baseElement instanceof Activity || baseElement instanceof TextAnnotation) {return CustomProcessDiagramCanvas.SHAPE_TYPE.Rectangle;} else if (baseElement instanceof Gateway) {return CustomProcessDiagramCanvas.SHAPE_TYPE.Rhombus;} else if (baseElement instanceof Event) {return CustomProcessDiagramCanvas.SHAPE_TYPE.Ellipse;} else {// unknown source element, just do not correct coordinates}return null;}protected static GraphicInfo getLineCenter(List<GraphicInfo> graphicInfoList) {GraphicInfo gi = new GraphicInfo();int[] xPoints = new int[graphicInfoList.size()];int[] yPoints = new int[graphicInfoList.size()];double length = 0;double[] lengths = new double[graphicInfoList.size()];lengths[0] = 0;double m;for (int i = 1; i < graphicInfoList.size(); i++) {GraphicInfo graphicInfo = graphicInfoList.get(i);GraphicInfo previousGraphicInfo = graphicInfoList.get(i - 1);if (i == 1) {xPoints[0] = (int) previousGraphicInfo.getX();yPoints[0] = (int) previousGraphicInfo.getY();}xPoints[i] = (int) graphicInfo.getX();yPoints[i] = (int) graphicInfo.getY();length += Math.sqrt(Math.pow((int) graphicInfo.getX() - (int) previousGraphicInfo.getX(), 2) + Math.pow((int) graphicInfo.getY() - (int) previousGraphicInfo.getY(), 2));lengths[i] = length;}m = length / 2;int p1 = 0, p2 = 1;for (int i = 1; i < lengths.length; i++) {double len = lengths[i];p1 = i - 1;p2 = i;if (len > m) {break;}}GraphicInfo graphicInfo1 = graphicInfoList.get(p1);GraphicInfo graphicInfo2 = graphicInfoList.get(p2);double AB = (int) graphicInfo2.getX() - (int) graphicInfo1.getX();double OA = (int) graphicInfo2.getY() - (int) graphicInfo1.getY();double OB = lengths[p2] - lengths[p1];double ob = m - lengths[p1];double ab = AB * ob / OB;double oa = OA * ob / OB;double mx = graphicInfo1.getX() + ab;double my = graphicInfo1.getY() + oa;gi.setX(mx);gi.setY(my);return gi;}protected void drawArtifact(CustomProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel,Artifact artifact) {ArtifactDrawInstruction drawInstruction = artifactDrawInstructions.get(artifact.getClass());if (drawInstruction != null) {drawInstruction.draw(processDiagramCanvas, bpmnModel, artifact);}}private static void drawHighLight(CustomProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) {processDiagramCanvas.drawHighLight((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight());}/*** Desc:绘制正在执行中的节点红色高亮显示** @param processDiagramCanvas* @param graphicInfo* @author Fuxs*/private static void drawRunningActivitiHighLight(CustomProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) {processDiagramCanvas.drawRunningActivitiHighLight((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight());}protected static CustomProcessDiagramCanvas initProcessDiagramCanvas(BpmnModel bpmnModel, String imageType,String activityFontName, String labelFontName, ClassLoader customClassLoader) {// We need to calculate maximum values to know how big the image will be in its entiretydouble minX = Double.MAX_VALUE;double maxX = 0;double minY = Double.MAX_VALUE;double maxY = 0;for (Pool pool : bpmnModel.getPools()) {GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(pool.getId());minX = graphicInfo.getX();maxX = graphicInfo.getX() + graphicInfo.getWidth();minY = graphicInfo.getY();maxY = graphicInfo.getY() + graphicInfo.getHeight();}List<FlowNode> flowNodes = gatherAllFlowNodes(bpmnModel);for (FlowNode flowNode : flowNodes) {GraphicInfo flowNodeGraphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());// widthif (flowNodeGraphicInfo.getX() + flowNodeGraphicInfo.getWidth() > maxX) {maxX = flowNodeGraphicInfo.getX() + flowNodeGraphicInfo.getWidth();}if (flowNodeGraphicInfo.getX() < minX) {minX = flowNodeGraphicInfo.getX();}// heightif (flowNodeGraphicInfo.getY() + flowNodeGraphicInfo.getHeight() > maxY) {maxY = flowNodeGraphicInfo.getY() + flowNodeGraphicInfo.getHeight();}if (flowNodeGraphicInfo.getY() < minY) {minY = flowNodeGraphicInfo.getY();}for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) {List<GraphicInfo> graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(sequenceFlow.getId());if (graphicInfoList != null) {for (GraphicInfo graphicInfo : graphicInfoList) {// widthif (graphicInfo.getX() > maxX) {maxX = graphicInfo.getX();}if (graphicInfo.getX() < minX) {minX = graphicInfo.getX();}// heightif (graphicInfo.getY() > maxY) {maxY = graphicInfo.getY();}if (graphicInfo.getY() < minY) {minY = graphicInfo.getY();}}}}}List<Artifact> artifacts = gatherAllArtifacts(bpmnModel);for (Artifact artifact : artifacts) {GraphicInfo artifactGraphicInfo = bpmnModel.getGraphicInfo(artifact.getId());if (artifactGraphicInfo != null) {// widthif (artifactGraphicInfo.getX() + artifactGraphicInfo.getWidth() > maxX) {maxX = artifactGraphicInfo.getX() + artifactGraphicInfo.getWidth();}if (artifactGraphicInfo.getX() < minX) {minX = artifactGraphicInfo.getX();}// heightif (artifactGraphicInfo.getY() + artifactGraphicInfo.getHeight() > maxY) {maxY = artifactGraphicInfo.getY() + artifactGraphicInfo.getHeight();}if (artifactGraphicInfo.getY() < minY) {minY = artifactGraphicInfo.getY();}}List<GraphicInfo> graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(artifact.getId());if (graphicInfoList != null) {for (GraphicInfo graphicInfo : graphicInfoList) {// widthif (graphicInfo.getX() > maxX) {maxX = graphicInfo.getX();}if (graphicInfo.getX() < minX) {minX = graphicInfo.getX();}// heightif (graphicInfo.getY() > maxY) {maxY = graphicInfo.getY();}if (graphicInfo.getY() < minY) {minY = graphicInfo.getY();}}}}int nrOfLanes = 0;for (Process process : bpmnModel.getProcesses()) {for (Lane l : process.getLanes()) {nrOfLanes++;GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(l.getId());// // widthif (graphicInfo.getX() + graphicInfo.getWidth() > maxX) {maxX = graphicInfo.getX() + graphicInfo.getWidth();}if (graphicInfo.getX() < minX) {minX = graphicInfo.getX();}// heightif (graphicInfo.getY() + graphicInfo.getHeight() > maxY) {maxY = graphicInfo.getY() + graphicInfo.getHeight();}if (graphicInfo.getY() < minY) {minY = graphicInfo.getY();}}}// Special case, see http://jira.codehaus.org/browse/ACT-1431if (flowNodes.isEmpty() && bpmnModel.getPools().isEmpty() && nrOfLanes == 0) {// Nothing to showminX = 0;minY = 0;}return new CustomProcessDiagramCanvas((int) maxX + 10, (int) maxY + 10, (int) minX, (int) minY, imageType,activityFontName, labelFontName, customClassLoader);}protected static List<Artifact> gatherAllArtifacts(BpmnModel bpmnModel) {List<Artifact> artifacts = new ArrayList<Artifact>();for (Process process : bpmnModel.getProcesses()) {artifacts.addAll(process.getArtifacts());}return artifacts;}protected static List<FlowNode> gatherAllFlowNodes(BpmnModel bpmnModel) {List<FlowNode> flowNodes = new ArrayList<FlowNode>();for (Process process : bpmnModel.getProcesses()) {flowNodes.addAll(gatherAllFlowNodes(process));}return flowNodes;}protected static List<FlowNode> gatherAllFlowNodes(FlowElementsContainer flowElementsContainer) {List<FlowNode> flowNodes = new ArrayList<FlowNode>();for (FlowElement flowElement : flowElementsContainer.getFlowElements()) {if (flowElement instanceof FlowNode) {flowNodes.add((FlowNode) flowElement);}if (flowElement instanceof FlowElementsContainer) {flowNodes.addAll(gatherAllFlowNodes((FlowElementsContainer) flowElement));}}return flowNodes;}public Map<Class<? extends BaseElement>, ActivityDrawInstruction> getActivityDrawInstructions() {return activityDrawInstructions;}public void setActivityDrawInstructions(Map<Class<? extends BaseElement>, ActivityDrawInstruction> activityDrawInstructions) {this.activityDrawInstructions = activityDrawInstructions;}public Map<Class<? extends BaseElement>, ArtifactDrawInstruction> getArtifactDrawInstructions() {return artifactDrawInstructions;}public void setArtifactDrawInstructions(Map<Class<? extends BaseElement>, ArtifactDrawInstruction> artifactDrawInstructions) {this.artifactDrawInstructions = artifactDrawInstructions;}protected interface ActivityDrawInstruction {void draw(CustomProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode);}protected interface ArtifactDrawInstruction {void draw(CustomProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, Artifact artifact);}/*** Desc: 自定义生成Diagram方法,添加对正在执行中的节点红色高亮显示** @param bpmnModel* @param imageType* @param highLightedActivities* @param runningActivitiIdList* @param highLightedFlows* @param activityFontName* @param labelFontName* @param annotationFontName* @param customClassLoader* @param scaleFactor* @return* @author Fuxs*/public InputStream generateDiagramCustom(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities,List<String> runningActivitiIdList, List<String> highLightedFlows, String activityFontName,String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor) {// TODO Auto-generated method stubreturn generateProcessDiagram(bpmnModel, imageType, highLightedActivities, runningActivitiIdList,highLightedFlows, activityFontName, labelFontName, customClassLoader, scaleFactor).generateImage(imageType);}@Overridepublic InputStream generateDiagram(BpmnModel bpmnModel, String imageType, String activityFontName,String labelFontName, String annotationFontName, ClassLoader customClassLoader) {return generateDiagram(bpmnModel, imageType, Collections.<String>emptyList(), Collections.<String>emptyList(),activityFontName, labelFontName, customClassLoader, 1.0);}@Overridepublic InputStream generateDiagram(BpmnModel bpmnModel, String imageType, String activityFontName,String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor) {// TODO Auto-generated method stubreturn generateDiagram(bpmnModel, imageType, Collections.<String>emptyList(), Collections.<String>emptyList(),activityFontName, labelFontName, customClassLoader, scaleFactor);}@Overridepublic InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities,List<String> highLightedFlows, String activityFontName, String labelFontName, String annotationFontName,ClassLoader customClassLoader, double scaleFactor) {// TODO Auto-generated method stubreturn generateProcessDiagram(bpmnModel, imageType, highLightedActivities, new ArrayList<String>(),highLightedFlows, activityFontName, labelFontName, customClassLoader, scaleFactor).generateImage(imageType);}}

自定义流程图画布

package com.ruifu.act.utils;import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.AssociationDirection;
import org.activiti.bpmn.model.GraphicInfo;
import org.activiti.engine.ActivitiException;
import org.activiti.image.exception.ActivitiImageException;
import org.activiti.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.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import java.util.ArrayList;
import java.util.List;/*** activiti6流程图绘制** @author liuxz*/
@Slf4j
public class CustomProcessDiagramCanvas {public enum SHAPE_TYPE {Rectangle, Rhombus, Ellipse}protected static final int ARROW_WIDTH = 5;protected static final int CONDITIONAL_INDICATOR_WIDTH = 16;protected static final int DEFAULT_INDICATOR_WIDTH = 10;protected static final int MARKER_WIDTH = 12;protected static final int FONT_SIZE = 12;protected static final int FONT_SPACING = 2;protected static final int TEXT_PADDING = 3;protected static final int ANNOTATION_TEXT_PADDING = 7;protected static final int LINE_HEIGHT = FONT_SIZE + FONT_SPACING;/*** Colors*/protected static Color TASK_BOX_COLOR = new Color(249, 249, 249);protected static Color SUBPROCESS_BOX_COLOR = new Color(255, 255, 255);protected static Color EVENT_COLOR = new Color(255, 255, 255);protected static Color CONNECTION_COLOR = new Color(88, 88, 88);protected static Color CONDITIONAL_INDICATOR_COLOR = new Color(255, 255, 255);protected static Color RUNNING_HIGHLIGHT_COLOR = Color.RED;protected static Color HIGHLIGHT_COLOR = Color.GREEN;protected static Color LABEL_COLOR = new Color(112, 146, 190);//	protected static Color			LABEL_COLOR						= Color.blue;protected static Color TASK_BORDER_COLOR = new Color(187, 187, 187);protected static Color EVENT_BORDER_COLOR = new Color(88, 88, 88);protected static Color SUBPROCESS_BORDER_COLOR = new Color(0, 0, 0);// Fontsprotected static Font LABEL_FONT = new Font("微软雅黑", Font.ITALIC, 11);protected static Font ANNOTATION_FONT = new Font("Arial", Font.PLAIN, FONT_SIZE);protected static Font TASK_FONT = new Font("Arial", Font.PLAIN, FONT_SIZE);// Strokes//TODO 边框宽度修改//protected static Stroke THICK_TASK_BORDER_STROKE = new BasicStroke(3.0f);protected static Stroke THICK_TASK_BORDER_STROKE = new BasicStroke(2.0f);protected static Stroke GATEWAY_TYPE_STROKE = new BasicStroke(3.0f);protected static Stroke END_EVENT_STROKE = new BasicStroke(3.0f);protected static Stroke MULTI_INSTANCE_STROKE = new BasicStroke(1.3f);protected static Stroke EVENT_SUBPROCESS_STROKE = new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1.0f, new float[]{1.0f}, 0.0f);protected static Stroke NON_INTERRUPTING_EVENT_STROKE = new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1.0f, new float[]{4.0f, 3.0f}, 0.0f);protected static Stroke HIGHLIGHT_FLOW_STROKE = new BasicStroke(1.3f);protected static Stroke ANNOTATION_STROKE = new BasicStroke(2.0f);protected static Stroke ASSOCIATION_STROKE = new BasicStroke(2.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1.0f, new float[]{2.0f, 2.0f}, 0.0f);// iconsprotected static int ICON_PADDING = 5;protected static BufferedImage USERTASK_IMAGE;protected static BufferedImage SCRIPTTASK_IMAGE;protected static BufferedImage SERVICETASK_IMAGE;protected static BufferedImage RECEIVETASK_IMAGE;protected static BufferedImage SENDTASK_IMAGE;protected static BufferedImage MANUALTASK_IMAGE;protected static BufferedImage BUSINESS_RULE_TASK_IMAGE;protected static BufferedImage SHELL_TASK_IMAGE;protected static BufferedImage MULE_TASK_IMAGE;protected static BufferedImage CAMEL_TASK_IMAGE;protected static BufferedImage TIMER_IMAGE;protected static BufferedImage COMPENSATE_THROW_IMAGE;protected static BufferedImage COMPENSATE_CATCH_IMAGE;protected static BufferedImage ERROR_THROW_IMAGE;protected static BufferedImage ERROR_CATCH_IMAGE;protected static BufferedImage MESSAGE_THROW_IMAGE;protected static BufferedImage MESSAGE_CATCH_IMAGE;protected static BufferedImage SIGNAL_CATCH_IMAGE;protected static BufferedImage SIGNAL_THROW_IMAGE;protected int canvasWidth = -1;protected int canvasHeight = -1;protected int minX = -1;protected int minY = -1;protected BufferedImage processDiagram;protected Graphics2D g;protected FontMetrics fontMetrics;protected boolean closed;protected ClassLoader customClassLoader;protected String activityFontName = "Arial";protected String labelFontName = "Arial";/*** Creates an empty canvas with given width and height. Allows to specify minimal boundaries on the left and upper side of the canvas. This is useful for diagrams that have* white space there. Everything beneath these minimum values will be cropped. It's also possible to pass a specific font name and a class loader for the icon images.*/public CustomProcessDiagramCanvas(int width, int height, int minX, int minY, String imageType, String activityFontName, String labelFontName, ClassLoader customClassLoader) {this.canvasWidth = width;this.canvasHeight = height;this.minX = minX;this.minY = minY;if (activityFontName != null) {this.activityFontName = activityFontName;}if (labelFontName != null) {this.labelFontName = labelFontName;}this.customClassLoader = customClassLoader;initialize(imageType);}/*** Creates an empty canvas with given width and height. Allows to specify minimal boundaries on the left and upper side of the canvas. This is useful for diagrams that have* white space there (eg Signavio). Everything beneath these minimum values will be cropped.** @param minX Hint that will be used when generating the image. Parts that fall below minX on the horizontal scale will be cropped.* @param minY Hint that will be used when generating the image. Parts that fall below minX on the horizontal scale will be cropped.*/public CustomProcessDiagramCanvas(int width, int height, int minX, int minY, String imageType) {this.canvasWidth = width;this.canvasHeight = height;this.minX = minX;this.minY = minY;initialize(imageType);}public void initialize(String imageType) {if ("png".equalsIgnoreCase(imageType)) {this.processDiagram = new BufferedImage(canvasWidth, canvasHeight, BufferedImage.TYPE_INT_ARGB);} else {this.processDiagram = new BufferedImage(canvasWidth, canvasHeight, BufferedImage.TYPE_INT_RGB);}this.g = processDiagram.createGraphics();if ("png".equalsIgnoreCase(imageType) == false) {this.g.setBackground(new Color(255, 255, 255, 0));this.g.clearRect(0, 0, canvasWidth, canvasHeight);}g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);g.setPaint(Color.black);Font font = new Font(activityFontName, Font.BOLD, FONT_SIZE);g.setFont(font);this.fontMetrics = g.getFontMetrics();//		LABEL_FONT = new Font(labelFontName, Font.ITALIC, 10);try {USERTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/userTask.png", customClassLoader));SCRIPTTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/scriptTask.png", customClassLoader));SERVICETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/serviceTask.png", customClassLoader));RECEIVETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/receiveTask.png", customClassLoader));SENDTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/sendTask.png", customClassLoader));MANUALTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/manualTask.png", customClassLoader));BUSINESS_RULE_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/businessRuleTask.png", customClassLoader));SHELL_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/shellTask.png", customClassLoader));CAMEL_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/camelTask.png", customClassLoader));MULE_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/muleTask.png", customClassLoader));TIMER_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/timer.png", customClassLoader));COMPENSATE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/compensate-throw.png", customClassLoader));COMPENSATE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/compensate.png", customClassLoader));ERROR_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/error-throw.png", customClassLoader));ERROR_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/error.png", customClassLoader));MESSAGE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/message-throw.png", customClassLoader));MESSAGE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/message.png", customClassLoader));SIGNAL_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/signal-throw.png", customClassLoader));SIGNAL_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/signal.png", customClassLoader));} catch (IOException e) {log.warn("Could not load image for process diagram creation: {}", e.getMessage());}}/*** Generates an image of what currently is drawn on the canvas. Throws an {@link ActivitiException} when {@link #close()} is already called.*/public InputStream generateImage(String imageType) {if (closed) {throw new ActivitiImageException("ProcessDiagramGenerator already closed");}ByteArrayOutputStream out = new ByteArrayOutputStream();try {// Try to remove white spaceminX = (minX <= 5) ? 5 : minX;minY = (minY <= 5) ? 5 : minY;BufferedImage imageToSerialize = processDiagram;if (minX >= 0 && minY >= 0) {imageToSerialize = processDiagram.getSubimage(minX - 5, minY - 5, canvasWidth - minX + 5, canvasHeight - minY + 5);}ImageIO.write(imageToSerialize, imageType, out);} catch (IOException e) {throw new ActivitiImageException("Error while generating process image", e);} finally {try {if (out != null) {out.close();}} catch (IOException ignore) {// Exception is silently ignored}}return new ByteArrayInputStream(out.toByteArray());}/*** Generates an image of what currently is drawn on the canvas. Throws an {@link ActivitiException} when {@link #close()} is already called.*/public BufferedImage generateBufferedImage(String imageType) {if (closed) {throw new ActivitiImageException("ProcessDiagramGenerator already closed");}// Try to remove white spaceminX = (minX <= 5) ? 5 : minX;minY = (minY <= 5) ? 5 : minY;BufferedImage imageToSerialize = processDiagram;if (minX >= 0 && minY >= 0) {imageToSerialize = processDiagram.getSubimage(minX - 5, minY - 5, canvasWidth - minX + 5, canvasHeight - minY + 5);}return imageToSerialize;}/*** Closes the canvas which dissallows further drawing and releases graphical resources.*/public void close() {g.dispose();closed = true;}public void drawNoneStartEvent(GraphicInfo graphicInfo) {drawStartEvent(graphicInfo, null, 1.0);}public void drawTimerStartEvent(GraphicInfo graphicInfo, double scaleFactor) {drawStartEvent(graphicInfo, TIMER_IMAGE, scaleFactor);}public void drawSignalStartEvent(GraphicInfo graphicInfo, double scaleFactor) {drawStartEvent(graphicInfo, SIGNAL_CATCH_IMAGE, scaleFactor);}public void drawMessageStartEvent(GraphicInfo graphicInfo, double scaleFactor) {drawStartEvent(graphicInfo, MESSAGE_CATCH_IMAGE, scaleFactor);}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 imageint 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);}}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);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);}public void drawErrorEndEvent(String name, GraphicInfo graphicInfo, double scaleFactor) {drawErrorEndEvent(graphicInfo, scaleFactor);if (scaleFactor == 1.0) {drawLabel(name, graphicInfo);}}public void drawErrorEndEvent(GraphicInfo graphicInfo, double scaleFactor) {drawNoneEndEvent(graphicInfo, scaleFactor);g.drawImage(ERROR_THROW_IMAGE, (int) (graphicInfo.getX() + (graphicInfo.getWidth() / 4)), (int) (graphicInfo.getY() + (graphicInfo.getHeight() / 4)),(int) (ERROR_THROW_IMAGE.getWidth() / scaleFactor), (int) (ERROR_THROW_IMAGE.getHeight() / scaleFactor), null);}public void drawErrorStartEvent(GraphicInfo graphicInfo, double scaleFactor) {drawNoneStartEvent(graphicInfo);g.drawImage(ERROR_CATCH_IMAGE, (int) (graphicInfo.getX() + (graphicInfo.getWidth() / 4)), (int) (graphicInfo.getY() + (graphicInfo.getHeight() / 4)),(int) (ERROR_CATCH_IMAGE.getWidth() / scaleFactor), (int) (ERROR_CATCH_IMAGE.getHeight() / scaleFactor), null);}public void drawCatchingEvent(GraphicInfo graphicInfo, boolean isInterrupting, BufferedImage image, String eventType, double scaleFactor) {// event circlesEllipse2D outerCircle = new Ellipse2D.Double(graphicInfo.getX(), graphicInfo.getY(), graphicInfo.getWidth(), graphicInfo.getHeight());int innerCircleSize = (int) (4 / scaleFactor);if (innerCircleSize == 0) {innerCircleSize = 1;}int innerCircleX = (int) graphicInfo.getX() + innerCircleSize;int innerCircleY = (int) graphicInfo.getY() + innerCircleSize;int innerCircleWidth = (int) graphicInfo.getWidth() - (2 * innerCircleSize);int innerCircleHeight = (int) graphicInfo.getHeight() - (2 * innerCircleSize);Ellipse2D innerCircle = new Ellipse2D.Double(innerCircleX, innerCircleY, innerCircleWidth, innerCircleHeight);Paint originalPaint = g.getPaint();Stroke originalStroke = g.getStroke();g.setPaint(EVENT_COLOR);g.fill(outerCircle);g.setPaint(EVENT_BORDER_COLOR);if (isInterrupting == false) {g.setStroke(NON_INTERRUPTING_EVENT_STROKE);}g.draw(outerCircle);g.setStroke(originalStroke);g.setPaint(originalPaint);g.draw(innerCircle);if (image != null) {// calculate coordinates to center imageint imageX = (int) (graphicInfo.getX() + (graphicInfo.getWidth() / 2) - (image.getWidth() / 2 * scaleFactor));int imageY = (int) (graphicInfo.getY() + (graphicInfo.getHeight() / 2) - (image.getHeight() / 2 * scaleFactor));if (scaleFactor == 1.0 && "timer".equals(eventType)) {// move image one pixel to center timer imageimageX++;imageY++;}g.drawImage(image, imageX, imageY, (int) (image.getWidth() / scaleFactor), (int) (image.getHeight() / scaleFactor), null);}}public void drawCatchingCompensateEvent(String name, GraphicInfo graphicInfo, boolean isInterrupting, double scaleFactor) {drawCatchingCompensateEvent(graphicInfo, isInterrupting, scaleFactor);drawLabel(name, graphicInfo);}public void drawCatchingCompensateEvent(GraphicInfo graphicInfo, boolean isInterrupting, double scaleFactor) {drawCatchingEvent(graphicInfo, isInterrupting, COMPENSATE_CATCH_IMAGE, "compensate", scaleFactor);}public void drawCatchingTimerEvent(String name, GraphicInfo graphicInfo, boolean isInterrupting, double scaleFactor) {drawCatchingTimerEvent(graphicInfo, isInterrupting, scaleFactor);drawLabel(name, graphicInfo);}public void drawCatchingTimerEvent(GraphicInfo graphicInfo, boolean isInterrupting, double scaleFactor) {drawCatchingEvent(graphicInfo, isInterrupting, TIMER_IMAGE, "timer", scaleFactor);}public void drawCatchingErrorEvent(String name, GraphicInfo graphicInfo, boolean isInterrupting, double scaleFactor) {drawCatchingErrorEvent(graphicInfo, isInterrupting, scaleFactor);drawLabel(name, graphicInfo);}public void drawCatchingErrorEvent(GraphicInfo graphicInfo, boolean isInterrupting, double scaleFactor) {drawCatchingEvent(graphicInfo, isInterrupting, ERROR_CATCH_IMAGE, "error", scaleFactor);}public void drawCatchingSignalEvent(String name, GraphicInfo graphicInfo, boolean isInterrupting, double scaleFactor) {drawCatchingSignalEvent(graphicInfo, isInterrupting, scaleFactor);drawLabel(name, graphicInfo);}public void drawCatchingSignalEvent(GraphicInfo graphicInfo, boolean isInterrupting, double scaleFactor) {drawCatchingEvent(graphicInfo, isInterrupting, SIGNAL_CATCH_IMAGE, "signal", scaleFactor);}public void drawCatchingMessageEvent(GraphicInfo graphicInfo, boolean isInterrupting, double scaleFactor) {drawCatchingEvent(graphicInfo, isInterrupting, MESSAGE_CATCH_IMAGE, "message", scaleFactor);}public void drawCatchingMessageEvent(String name, GraphicInfo graphicInfo, boolean isInterrupting, double scaleFactor) {drawCatchingEvent(graphicInfo, isInterrupting, MESSAGE_CATCH_IMAGE, "message", scaleFactor);drawLabel(name, graphicInfo);}public void drawThrowingCompensateEvent(GraphicInfo graphicInfo, double scaleFactor) {drawCatchingEvent(graphicInfo, true, COMPENSATE_THROW_IMAGE, "compensate", scaleFactor);}public void drawThrowingSignalEvent(GraphicInfo graphicInfo, double scaleFactor) {drawCatchingEvent(graphicInfo, true, SIGNAL_THROW_IMAGE, "signal", scaleFactor);}public void drawThrowingNoneEvent(GraphicInfo graphicInfo, double scaleFactor) {drawCatchingEvent(graphicInfo, true, null, "none", scaleFactor);}public void drawSequenceflow(int srcX, int srcY, int targetX, int targetY, boolean conditional, double scaleFactor) {drawSequenceflow(srcX, srcY, targetX, targetY, conditional, false, scaleFactor);}public void drawSequenceflow(int srcX, int srcY, int targetX, int targetY, boolean conditional, boolean highLighted, double scaleFactor) {Paint originalPaint = g.getPaint();if (highLighted) {g.setPaint(HIGHLIGHT_COLOR);}Line2D.Double line = new Line2D.Double(srcX, srcY, targetX, targetY);g.draw(line);drawArrowHead(line, scaleFactor);if (conditional) {drawConditionalSequenceFlowIndicator(line, scaleFactor);}if (highLighted) {g.setPaint(originalPaint);}}public void drawAssociation(int[] xPoints, int[] yPoints, AssociationDirection associationDirection, boolean highLighted, double scaleFactor) {boolean conditional = false, isDefault = false;drawConnection(xPoints, yPoints, conditional, isDefault, "association", associationDirection, highLighted, scaleFactor);}public void drawSequenceflow(int[] xPoints, int[] yPoints, boolean conditional, boolean isDefault, boolean highLighted, double scaleFactor) {drawConnection(xPoints, yPoints, conditional, isDefault, "sequenceFlow", AssociationDirection.ONE, highLighted, scaleFactor);}public void drawConnection(int[] xPoints, int[] yPoints, boolean conditional, boolean isDefault, String connectionType, AssociationDirection associationDirection,boolean highLighted, double scaleFactor) {Paint originalPaint = g.getPaint();Stroke originalStroke = g.getStroke();g.setPaint(CONNECTION_COLOR);if ("association".equals(connectionType)) {g.setStroke(ASSOCIATION_STROKE);} else if (highLighted) {g.setPaint(HIGHLIGHT_COLOR);g.setStroke(HIGHLIGHT_FLOW_STROKE);}for (int i = 1; i < xPoints.length; i++) {Integer sourceX = xPoints[i - 1];Integer sourceY = yPoints[i - 1];Integer targetX = xPoints[i];Integer targetY = yPoints[i];Line2D.Double line = new Line2D.Double(sourceX, sourceY, targetX, targetY);g.draw(line);}if (isDefault) {Line2D.Double line = new Line2D.Double(xPoints[0], yPoints[0], xPoints[1], yPoints[1]);drawDefaultSequenceFlowIndicator(line, scaleFactor);}if (conditional) {Line2D.Double line = new Line2D.Double(xPoints[0], yPoints[0], xPoints[1], yPoints[1]);drawConditionalSequenceFlowIndicator(line, scaleFactor);}if (associationDirection.equals(AssociationDirection.ONE) || associationDirection.equals(AssociationDirection.BOTH)) {Line2D.Double line = new Line2D.Double(xPoints[xPoints.length - 2], yPoints[xPoints.length - 2], xPoints[xPoints.length - 1], yPoints[xPoints.length - 1]);drawArrowHead(line, scaleFactor);}if (associationDirection.equals(AssociationDirection.BOTH)) {Line2D.Double line = new Line2D.Double(xPoints[1], yPoints[1], xPoints[0], yPoints[0]);drawArrowHead(line, scaleFactor);}g.setPaint(originalPaint);g.setStroke(originalStroke);}public void drawSequenceflowWithoutArrow(int srcX, int srcY, int targetX, int targetY, boolean conditional, double scaleFactor) {drawSequenceflowWithoutArrow(srcX, srcY, targetX, targetY, conditional, false, scaleFactor);}public void drawSequenceflowWithoutArrow(int srcX, int srcY, int targetX, int targetY, boolean conditional, boolean highLighted, double scaleFactor) {Paint originalPaint = g.getPaint();if (highLighted) {g.setPaint(HIGHLIGHT_COLOR);}Line2D.Double line = new Line2D.Double(srcX, srcY, targetX, targetY);g.draw(line);if (conditional) {drawConditionalSequenceFlowIndicator(line, scaleFactor);}if (highLighted) {g.setPaint(originalPaint);}}public void drawArrowHead(Line2D.Double line, double scaleFactor) {int doubleArrowWidth = (int) (2 * ARROW_WIDTH / scaleFactor);if (doubleArrowWidth == 0) {doubleArrowWidth = 2;}Polygon arrowHead = new Polygon();arrowHead.addPoint(0, 0);int arrowHeadPoint = (int) (-ARROW_WIDTH / scaleFactor);if (arrowHeadPoint == 0) {arrowHeadPoint = -1;}arrowHead.addPoint(arrowHeadPoint, -doubleArrowWidth);arrowHeadPoint = (int) (ARROW_WIDTH / scaleFactor);if (arrowHeadPoint == 0) {arrowHeadPoint = 1;}arrowHead.addPoint(arrowHeadPoint, -doubleArrowWidth);AffineTransform transformation = new AffineTransform();transformation.setToIdentity();double angle = Math.atan2(line.y2 - line.y1, line.x2 - line.x1);transformation.translate(line.x2, line.y2);transformation.rotate((angle - Math.PI / 2d));AffineTransform originalTransformation = g.getTransform();g.setTransform(transformation);g.fill(arrowHead);g.setTransform(originalTransformation);}public void drawDefaultSequenceFlowIndicator(Line2D.Double line, double scaleFactor) {double length = DEFAULT_INDICATOR_WIDTH / scaleFactor, halfOfLength = length / 2, f = 8;Line2D.Double defaultIndicator = new Line2D.Double(-halfOfLength, 0, halfOfLength, 0);double angle = Math.atan2(line.y2 - line.y1, line.x2 - line.x1);double dx = f * Math.cos(angle), dy = f * Math.sin(angle), x1 = line.x1 + dx, y1 = line.y1 + dy;AffineTransform transformation = new AffineTransform();transformation.setToIdentity();transformation.translate(x1, y1);transformation.rotate((angle - 3 * Math.PI / 4));AffineTransform originalTransformation = g.getTransform();g.setTransform(transformation);g.draw(defaultIndicator);g.setTransform(originalTransformation);}public void drawConditionalSequenceFlowIndicator(Line2D.Double line, double scaleFactor) {if (scaleFactor > 1.0) {return;}int horizontal = (int) (CONDITIONAL_INDICATOR_WIDTH * 0.7);int halfOfHorizontal = horizontal / 2;int halfOfVertical = CONDITIONAL_INDICATOR_WIDTH / 2;Polygon conditionalIndicator = new Polygon();conditionalIndicator.addPoint(0, 0);conditionalIndicator.addPoint(-halfOfHorizontal, halfOfVertical);conditionalIndicator.addPoint(0, CONDITIONAL_INDICATOR_WIDTH);conditionalIndicator.addPoint(halfOfHorizontal, halfOfVertical);AffineTransform transformation = new AffineTransform();transformation.setToIdentity();double angle = Math.atan2(line.y2 - line.y1, line.x2 - line.x1);transformation.translate(line.x1, line.y1);transformation.rotate((angle - Math.PI / 2d));AffineTransform originalTransformation = g.getTransform();g.setTransform(transformation);g.draw(conditionalIndicator);Paint originalPaint = g.getPaint();g.setPaint(CONDITIONAL_INDICATOR_COLOR);g.fill(conditionalIndicator);g.setPaint(originalPaint);g.setTransform(originalTransformation);}public void drawTask(BufferedImage icon, String name, GraphicInfo graphicInfo, double scaleFactor) {drawTask(name, graphicInfo);g.drawImage(icon, (int) (graphicInfo.getX() + ICON_PADDING / scaleFactor), (int) (graphicInfo.getY() + ICON_PADDING / scaleFactor), (int) (icon.getWidth() / scaleFactor),(int) (icon.getHeight() / scaleFactor), null);}public void drawTask(String name, GraphicInfo graphicInfo) {drawTask(name, graphicInfo, false);}public void drawPoolOrLane(String name, GraphicInfo graphicInfo) {int x = (int) graphicInfo.getX();int y = (int) graphicInfo.getY();int width = (int) graphicInfo.getWidth();int height = (int) graphicInfo.getHeight();g.drawRect(x, y, width, height);// Add the name as text, verticalif (name != null && name.length() > 0) {// Include some paddingint availableTextSpace = height - 6;// Create rotation for derived fontAffineTransform transformation = new AffineTransform();transformation.setToIdentity();transformation.rotate(270 * Math.PI / 180);Font currentFont = g.getFont();Font theDerivedFont = currentFont.deriveFont(transformation);g.setFont(theDerivedFont);String truncated = fitTextToWidth(name, availableTextSpace);int realWidth = fontMetrics.stringWidth(truncated);g.drawString(truncated, x + 2 + fontMetrics.getHeight(), 3 + y + availableTextSpace - (availableTextSpace - realWidth) / 2);g.setFont(currentFont);}}protected void drawTask(String name, GraphicInfo graphicInfo, boolean thickBorder) {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 relativeg.setPaint(TASK_BOX_COLOR);int arcR = 6;if (thickBorder) {arcR = 3;}// shapeRoundRectangle2D 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);// textif (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 void drawMultilineCentredText(String text, int x, int y, int boxWidth, int boxHeight) {drawMultilineText(text, x, y, boxWidth, boxHeight, true);}protected void drawMultilineAnnotationText(String text, int x, int y, int boxWidth, int boxHeight) {drawMultilineText(text, x, y, boxWidth, boxHeight, false);}protected void drawMultilineText(String text, int x, int y, int boxWidth, int boxHeight, boolean centered) {// Create an attributed string based in input textAttributedString attributedString = new AttributedString(text);attributedString.addAttribute(TextAttribute.FONT, g.getFont());attributedString.addAttribute(TextAttribute.FOREGROUND, Color.black);AttributedCharacterIterator characterIterator = attributedString.getIterator();int currentHeight = 0;// Prepare a list of lines of text we'll be drawingList<TextLayout> layouts = new ArrayList<TextLayout>();String lastLine = null;LineBreakMeasurer measurer = new LineBreakMeasurer(characterIterator, g.getFontRenderContext());TextLayout layout = null;while (measurer.getPosition() < characterIterator.getEndIndex() && currentHeight <= boxHeight) {int previousPosition = measurer.getPosition();// Request next layoutlayout = measurer.nextLayout(boxWidth);int height = ((Float) (layout.getDescent() + layout.getAscent() + layout.getLeading())).intValue();if (currentHeight + height > boxHeight) {// The line we're about to add should NOT be added anymore, append three dots to previous one instead// to indicate more text is truncatedif (!layouts.isEmpty()) {layouts.remove(layouts.size() - 1);if (lastLine.length() >= 4) {lastLine = lastLine.substring(0, lastLine.length() - 4) + "...";}layouts.add(new TextLayout(lastLine, g.getFont(), g.getFontRenderContext()));}} else {layouts.add(layout);lastLine = text.substring(previousPosition, measurer.getPosition());currentHeight += height;}}int currentY = y + (centered ? ((boxHeight - currentHeight) / 2) : 0);int currentX = 0;// Actually draw the linesfor (TextLayout textLayout : layouts) {currentY += textLayout.getAscent();currentX = x + (centered ? ((boxWidth - ((Double) textLayout.getBounds().getWidth()).intValue()) / 2) : 0);textLayout.draw(g, currentX, currentY);currentY += textLayout.getDescent() + textLayout.getLeading();}}protected String fitTextToWidth(String original, int width) {String text = original;// remove length for "..."int maxWidth = width - 10;while (fontMetrics.stringWidth(text + "...") > maxWidth && text.length() > 0) {text = text.substring(0, text.length() - 1);}if (!text.equals(original)) {text = text + "...";}return text;}public void drawUserTask(String name, GraphicInfo graphicInfo, double scaleFactor) {drawTask(USERTASK_IMAGE, name, graphicInfo, scaleFactor);}public void drawScriptTask(String name, GraphicInfo graphicInfo, double scaleFactor) {drawTask(SCRIPTTASK_IMAGE, name, graphicInfo, scaleFactor);}public void drawServiceTask(String name, GraphicInfo graphicInfo, double scaleFactor) {drawTask(SERVICETASK_IMAGE, name, graphicInfo, scaleFactor);}public void drawReceiveTask(String name, GraphicInfo graphicInfo, double scaleFactor) {drawTask(RECEIVETASK_IMAGE, name, graphicInfo, scaleFactor);}public void drawSendTask(String name, GraphicInfo graphicInfo, double scaleFactor) {drawTask(SENDTASK_IMAGE, name, graphicInfo, scaleFactor);}public void drawManualTask(String name, GraphicInfo graphicInfo, double scaleFactor) {drawTask(MANUALTASK_IMAGE, name, graphicInfo, scaleFactor);}public void drawBusinessRuleTask(String name, GraphicInfo graphicInfo, double scaleFactor) {drawTask(BUSINESS_RULE_TASK_IMAGE, name, graphicInfo, scaleFactor);}public void drawCamelTask(String name, GraphicInfo graphicInfo, double scaleFactor) {drawTask(CAMEL_TASK_IMAGE, name, graphicInfo, scaleFactor);}public void drawMuleTask(String name, GraphicInfo graphicInfo, double scaleFactor) {drawTask(MULE_TASK_IMAGE, name, graphicInfo, scaleFactor);}public void drawExpandedSubProcess(String name, GraphicInfo graphicInfo, Boolean isTriggeredByEvent, double scaleFactor) {RoundRectangle2D rect = new RoundRectangle2D.Double(graphicInfo.getX(), graphicInfo.getY(), graphicInfo.getWidth(), graphicInfo.getHeight(), 8, 8);// Use different stroke (dashed)if (isTriggeredByEvent) {Stroke originalStroke = g.getStroke();g.setStroke(EVENT_SUBPROCESS_STROKE);g.draw(rect);g.setStroke(originalStroke);} else {Paint originalPaint = g.getPaint();g.setPaint(SUBPROCESS_BOX_COLOR);g.fill(rect);g.setPaint(SUBPROCESS_BORDER_COLOR);g.draw(rect);g.setPaint(originalPaint);}if (scaleFactor == 1.0 && name != null && !name.isEmpty()) {String text = fitTextToWidth(name, (int) graphicInfo.getWidth());g.drawString(text, (int) graphicInfo.getX() + 10, (int) graphicInfo.getY() + 15);}}public void drawCollapsedSubProcess(String name, GraphicInfo graphicInfo, Boolean isTriggeredByEvent) {drawCollapsedTask(name, graphicInfo, false);}public void drawCollapsedCallActivity(String name, GraphicInfo graphicInfo) {drawCollapsedTask(name, graphicInfo, true);}protected void drawCollapsedTask(String name, GraphicInfo graphicInfo, boolean thickBorder) {// The collapsed marker is now visualized separatelydrawTask(name, graphicInfo, thickBorder);}public void drawCollapsedMarker(int x, int y, int width, int height) {// rectangleint rectangleWidth = MARKER_WIDTH;int rectangleHeight = MARKER_WIDTH;Rectangle rect = new Rectangle(x + (width - rectangleWidth) / 2, y + height - rectangleHeight - 3, rectangleWidth, rectangleHeight);g.draw(rect);// plus inside rectangleLine2D.Double line = new Line2D.Double(rect.getCenterX(), rect.getY() + 2, rect.getCenterX(), rect.getMaxY() - 2);g.draw(line);line = new Line2D.Double(rect.getMinX() + 2, rect.getCenterY(), rect.getMaxX() - 2, rect.getCenterY());g.draw(line);}public void drawActivityMarkers(int x, int y, int width, int height, boolean multiInstanceSequential, boolean multiInstanceParallel, boolean collapsed) {if (collapsed) {if (!multiInstanceSequential && !multiInstanceParallel) {drawCollapsedMarker(x, y, width, height);} else {drawCollapsedMarker(x - MARKER_WIDTH / 2 - 2, y, width, height);if (multiInstanceSequential) {drawMultiInstanceMarker(true, x + MARKER_WIDTH / 2 + 2, y, width, height);} else {drawMultiInstanceMarker(false, x + MARKER_WIDTH / 2 + 2, y, width, height);}}} else {if (multiInstanceSequential) {drawMultiInstanceMarker(true, x, y, width, height);} else if (multiInstanceParallel) {drawMultiInstanceMarker(false, x, y, width, height);}}}public void drawGateway(GraphicInfo graphicInfo) {Polygon rhombus = new Polygon();int x = (int) graphicInfo.getX();int y = (int) graphicInfo.getY();int width = (int) graphicInfo.getWidth();int height = (int) graphicInfo.getHeight();rhombus.addPoint(x, y + (height / 2));rhombus.addPoint(x + (width / 2), y + height);rhombus.addPoint(x + width, y + (height / 2));rhombus.addPoint(x + (width / 2), y);g.draw(rhombus);}public void drawParallelGateway(GraphicInfo graphicInfo, double scaleFactor) {// rhombusdrawGateway(graphicInfo);int x = (int) graphicInfo.getX();int y = (int) graphicInfo.getY();int width = (int) graphicInfo.getWidth();int height = (int) graphicInfo.getHeight();if (scaleFactor == 1.0) {// plus inside rhombusStroke orginalStroke = g.getStroke();g.setStroke(GATEWAY_TYPE_STROKE);Line2D.Double line = new Line2D.Double(x + 10, y + height / 2, x + width - 10, y + height / 2); // horizontalg.draw(line);line = new Line2D.Double(x + width / 2, y + height - 10, x + width / 2, y + 10); // verticalg.draw(line);g.setStroke(orginalStroke);}}public void drawExclusiveGateway(GraphicInfo graphicInfo, double scaleFactor) {// rhombusdrawGateway(graphicInfo);int x = (int) graphicInfo.getX();int y = (int) graphicInfo.getY();int width = (int) graphicInfo.getWidth();int height = (int) graphicInfo.getHeight();int quarterWidth = width / 4;int quarterHeight = height / 4;if (scaleFactor == 1.0) {// X inside rhombusStroke orginalStroke = g.getStroke();g.setStroke(GATEWAY_TYPE_STROKE);Line2D.Double line = new Line2D.Double(x + quarterWidth + 3, y + quarterHeight + 3, x + 3 * quarterWidth - 3, y + 3 * quarterHeight - 3);g.draw(line);line = new Line2D.Double(x + quarterWidth + 3, y + 3 * quarterHeight - 3, x + 3 * quarterWidth - 3, y + quarterHeight + 3);g.draw(line);g.setStroke(orginalStroke);}}public void drawInclusiveGateway(GraphicInfo graphicInfo, double scaleFactor) {// rhombusdrawGateway(graphicInfo);int x = (int) graphicInfo.getX();int y = (int) graphicInfo.getY();int width = (int) graphicInfo.getWidth();int height = (int) graphicInfo.getHeight();int diameter = width / 2;if (scaleFactor == 1.0) {// circle inside rhombusStroke orginalStroke = g.getStroke();g.setStroke(GATEWAY_TYPE_STROKE);Ellipse2D.Double circle = new Ellipse2D.Double(((width - diameter) / 2) + x, ((height - diameter) / 2) + y, diameter, diameter);g.draw(circle);g.setStroke(orginalStroke);}}public void drawEventBasedGateway(GraphicInfo graphicInfo, double scaleFactor) {// rhombusdrawGateway(graphicInfo);if (scaleFactor == 1.0) {int x = (int) graphicInfo.getX();int y = (int) graphicInfo.getY();int width = (int) graphicInfo.getWidth();int height = (int) graphicInfo.getHeight();double scale = .6;GraphicInfo eventInfo = new GraphicInfo();eventInfo.setX(x + width * (1 - scale) / 2);eventInfo.setY(y + height * (1 - scale) / 2);eventInfo.setWidth(width * scale);eventInfo.setHeight(height * scale);drawCatchingEvent(eventInfo, true, null, "eventGateway", scaleFactor);double r = width / 6.;// create pentagon (coords with respect to center)int topX = (int) (.95 * r); // top right cornerint topY = (int) (-.31 * r);int bottomX = (int) (.59 * r); // bottom right cornerint bottomY = (int) (.81 * r);int[] xPoints = new int[]{0, topX, bottomX, -bottomX, -topX};int[] yPoints = new int[]{-(int) r, topY, bottomY, bottomY, topY};Polygon pentagon = new Polygon(xPoints, yPoints, 5);pentagon.translate(x + width / 2, y + width / 2);// drawg.drawPolygon(pentagon);}}public void drawMultiInstanceMarker(boolean sequential, int x, int y, int width, int height) {int rectangleWidth = MARKER_WIDTH;int rectangleHeight = MARKER_WIDTH;int lineX = x + (width - rectangleWidth) / 2;int lineY = y + height - rectangleHeight - 3;Stroke orginalStroke = g.getStroke();g.setStroke(MULTI_INSTANCE_STROKE);if (sequential) {g.draw(new Line2D.Double(lineX, lineY, lineX + rectangleWidth, lineY));g.draw(new Line2D.Double(lineX, lineY + rectangleHeight / 2, lineX + rectangleWidth, lineY + rectangleHeight / 2));g.draw(new Line2D.Double(lineX, lineY + rectangleHeight, lineX + rectangleWidth, lineY + rectangleHeight));} else {g.draw(new Line2D.Double(lineX, lineY, lineX, lineY + rectangleHeight));g.draw(new Line2D.Double(lineX + rectangleWidth / 2, lineY, lineX + rectangleWidth / 2, lineY + rectangleHeight));g.draw(new Line2D.Double(lineX + rectangleWidth, lineY, lineX + rectangleWidth, lineY + rectangleHeight));}g.setStroke(orginalStroke);}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);//TODO 长方形弧度修改//RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20);RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 0, 0);g.draw(rect);g.setPaint(originalPaint);g.setStroke(originalStroke);}/*** Desc: 绘制正在执行中的节点红色高亮显示** @param x* @param y* @param width* @param height* @author Fuxs*/public void drawRunningActivitiHighLight(int x, int y, int width, int height) {Paint originalPaint = g.getPaint();Stroke originalStroke = g.getStroke();g.setPaint(RUNNING_HIGHLIGHT_COLOR);g.setStroke(THICK_TASK_BORDER_STROKE);//todo 修改长方形弧度//RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20);RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 0, 0);g.draw(rect);g.setPaint(originalPaint);g.setStroke(originalStroke);}public void drawTextAnnotation(String text, GraphicInfo graphicInfo) {int x = (int) graphicInfo.getX();int y = (int) graphicInfo.getY();int width = (int) graphicInfo.getWidth();int height = (int) graphicInfo.getHeight();Font originalFont = g.getFont();Stroke originalStroke = g.getStroke();g.setFont(ANNOTATION_FONT);Path2D path = new Path2D.Double();x += .5;int lineLength = 18;path.moveTo(x + lineLength, y);path.lineTo(x, y);path.lineTo(x, y + height);path.lineTo(x + lineLength, y + height);path.lineTo(x + lineLength, y + height - 1);path.lineTo(x + 1, y + height - 1);path.lineTo(x + 1, y + 1);path.lineTo(x + lineLength, y + 1);path.closePath();g.draw(path);int boxWidth = width - (2 * ANNOTATION_TEXT_PADDING);int boxHeight = height - (2 * ANNOTATION_TEXT_PADDING);int boxX = x + width / 2 - boxWidth / 2;int boxY = y + height / 2 - boxHeight / 2;if (text != null && text.isEmpty() == false) {drawMultilineAnnotationText(text, boxX, boxY, boxWidth, boxHeight);}// restore originalsg.setFont(originalFont);g.setStroke(originalStroke);}public void drawLabel(String text, GraphicInfo graphicInfo) {drawLabel(text, graphicInfo, true);}/*** 绘制流程线名称* Desc:** @param text* @param graphicInfo* @param centered* @author Fuxs*/public void drawLabel(String text, GraphicInfo graphicInfo, boolean centered) {float interline = 1.0f;// textif (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() + graphicInfo.getHeight());// 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 originalsg.setFont(originalFont);g.setPaint(originalPaint);}}/*** This method makes coordinates of connection flow better.** @param sourceShapeType* @param targetShapeType* @param sourceGraphicInfo* @param targetGraphicInfo* @param graphicInfoList*/public List<GraphicInfo> connectionPerfectionizer(SHAPE_TYPE sourceShapeType, SHAPE_TYPE targetShapeType, GraphicInfo sourceGraphicInfo, GraphicInfo targetGraphicInfo,List<GraphicInfo> graphicInfoList) {Shape shapeFirst = createShape(sourceShapeType, sourceGraphicInfo);Shape shapeLast = createShape(targetShapeType, targetGraphicInfo);if (graphicInfoList != null && graphicInfoList.size() > 0) {GraphicInfo graphicInfoFirst = graphicInfoList.get(0);GraphicInfo graphicInfoLast = graphicInfoList.get(graphicInfoList.size() - 1);if (shapeFirst != null) {graphicInfoFirst.setX(shapeFirst.getBounds2D().getCenterX());graphicInfoFirst.setY(shapeFirst.getBounds2D().getCenterY());}if (shapeLast != null) {graphicInfoLast.setX(shapeLast.getBounds2D().getCenterX());graphicInfoLast.setY(shapeLast.getBounds2D().getCenterY());}Point p = null;if (shapeFirst != null) {Line2D.Double lineFirst = new Line2D.Double(graphicInfoFirst.getX(), graphicInfoFirst.getY(), graphicInfoList.get(1).getX(), graphicInfoList.get(1).getY());p = getIntersection(shapeFirst, lineFirst);if (p != null) {graphicInfoFirst.setX(p.getX());graphicInfoFirst.setY(p.getY());}}if (shapeLast != null) {Line2D.Double lineLast = new Line2D.Double(graphicInfoLast.getX(), graphicInfoLast.getY(), graphicInfoList.get(graphicInfoList.size() - 2).getX(),graphicInfoList.get(graphicInfoList.size() - 2).getY());p = getIntersection(shapeLast, lineLast);if (p != null) {graphicInfoLast.setX(p.getX());graphicInfoLast.setY(p.getY());}}}return graphicInfoList;}/*** This method creates shape by type and coordinates.** @param shapeType* @param graphicInfo* @return Shape*/private static Shape createShape(SHAPE_TYPE shapeType, GraphicInfo graphicInfo) {if (SHAPE_TYPE.Rectangle.equals(shapeType)) {// source is rectanglereturn new Rectangle2D.Double(graphicInfo.getX(), graphicInfo.getY(), graphicInfo.getWidth(), graphicInfo.getHeight());} else if (SHAPE_TYPE.Rhombus.equals(shapeType)) {// source is rhombusPath2D.Double rhombus = new Path2D.Double();rhombus.moveTo(graphicInfo.getX(), graphicInfo.getY() + graphicInfo.getHeight() / 2);rhombus.lineTo(graphicInfo.getX() + graphicInfo.getWidth() / 2, graphicInfo.getY() + graphicInfo.getHeight());rhombus.lineTo(graphicInfo.getX() + graphicInfo.getWidth(), graphicInfo.getY() + graphicInfo.getHeight() / 2);rhombus.lineTo(graphicInfo.getX() + graphicInfo.getWidth() / 2, graphicInfo.getY());rhombus.lineTo(graphicInfo.getX(), graphicInfo.getY() + graphicInfo.getHeight() / 2);rhombus.closePath();return rhombus;} else if (SHAPE_TYPE.Ellipse.equals(shapeType)) {// source is ellipsereturn new Ellipse2D.Double(graphicInfo.getX(), graphicInfo.getY(), graphicInfo.getWidth(), graphicInfo.getHeight());} else {// unknown source element, just do not correct coordinates}return null;}/*** This method returns intersection point of shape border and line.** @param shape* @param line* @return Point*/private static Point getIntersection(Shape shape, Line2D.Double line) {if (shape instanceof Ellipse2D) {return getEllipseIntersection(shape, line);} else if (shape instanceof Rectangle2D || shape instanceof Path2D) {return getShapeIntersection(shape, line);} else {// something strangereturn null;}}/*** This method calculates ellipse intersection with line** @param shape Bounds of this shape used to calculate parameters of inscribed into this bounds ellipse.* @param line* @return Intersection point*/private static Point getEllipseIntersection(Shape shape, Line2D.Double line) {double angle = Math.atan2(line.y2 - line.y1, line.x2 - line.x1);double x = shape.getBounds2D().getWidth() / 2 * Math.cos(angle) + shape.getBounds2D().getCenterX();double y = shape.getBounds2D().getHeight() / 2 * Math.sin(angle) + shape.getBounds2D().getCenterY();Point p = new Point();p.setLocation(x, y);return p;}/*** This method calculates shape intersection with line.** @param shape* @param line* @return Intersection point*/private static Point getShapeIntersection(Shape shape, Line2D.Double line) {PathIterator it = shape.getPathIterator(null);double[] coords = new double[6];double[] pos = new double[2];Line2D.Double l = new Line2D.Double();while (!it.isDone()) {int type = it.currentSegment(coords);switch (type) {case PathIterator.SEG_MOVETO:pos[0] = coords[0];pos[1] = coords[1];break;case PathIterator.SEG_LINETO:l = new Line2D.Double(pos[0], pos[1], coords[0], coords[1]);if (line.intersectsLine(l)) {return getLinesIntersection(line, l);}pos[0] = coords[0];pos[1] = coords[1];break;case PathIterator.SEG_CLOSE:break;default:// whatever}it.next();}return null;}/*** This method calculates intersections of two lines.** @param a Line 1* @param b Line 2* @return Intersection point*/private static Point getLinesIntersection(Line2D a, Line2D b) {double d = (a.getX1() - a.getX2()) * (b.getY2() - b.getY1()) - (a.getY1() - a.getY2()) * (b.getX2() - b.getX1());double da = (a.getX1() - b.getX1()) * (b.getY2() - b.getY1()) - (a.getY1() - b.getY1()) * (b.getX2() - b.getX1());// double db = (a.getX1()-a.getX2())*(a.getY1()-b.getY1()) - (a.getY1()-a.getY2())*(a.getX1()-b.getX1());double ta = da / d;// double tb = db/d;Point p = new Point();p.setLocation(a.getX1() + ta * (a.getX2() - a.getX1()), a.getY1() + ta * (a.getY2() - a.getY1()));return p;}
}

效果:…Activiti资料是真的少…

在这里插入图片描述

这篇关于Activiti6.0查看流程图 流程追踪图,双色高亮显示的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/527082

相关文章

Security OAuth2 单点登录流程

单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一注销(single sign-off)就是指

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

第10章 中断和动态时钟显示

第10章 中断和动态时钟显示 从本章开始,按照书籍的划分,第10章开始就进入保护模式(Protected Mode)部分了,感觉从这里开始难度突然就增加了。 书中介绍了为什么有中断(Interrupt)的设计,中断的几种方式:外部硬件中断、内部中断和软中断。通过中断做了一个会走的时钟和屏幕上输入字符的程序。 我自己理解中断的一些作用: 为了更好的利用处理器的性能。协同快速和慢速设备一起工作

安卓链接正常显示,ios#符被转义%23导致链接访问404

原因分析: url中含有特殊字符 中文未编码 都有可能导致URL转换失败,所以需要对url编码处理  如下: guard let allowUrl = webUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {return} 后面发现当url中有#号时,会被误伤转义为%23,导致链接无法访问

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

flume系列之:查看flume系统日志、查看统计flume日志类型、查看flume日志

遍历指定目录下多个文件查找指定内容 服务器系统日志会记录flume相关日志 cat /var/log/messages |grep -i oom 查找系统日志中关于flume的指定日志 import osdef search_string_in_files(directory, search_string):count = 0

查看提交历史 —— Git 学习笔记 11

查看提交历史 查看提交历史 不带任何选项的git log-p选项--stat 选项--pretty=oneline选项--pretty=format选项git log常用选项列表参考资料 在提交了若干更新,又或者克隆了某个项目之后,你也许想回顾下提交历史。 完成这个任务最简单而又有效的 工具是 git log 命令。 接下来的例子会用一个用于演示的 simplegit

kubelet组件的启动流程源码分析

概述 摘要: 本文将总结kubelet的作用以及原理,在有一定基础认识的前提下,通过阅读kubelet源码,对kubelet组件的启动流程进行分析。 正文 kubelet的作用 这里对kubelet的作用做一个简单总结。 节点管理 节点的注册 节点状态更新 容器管理(pod生命周期管理) 监听apiserver的容器事件 容器的创建、删除(CRI) 容器的网络的创建与删除

lvgl8.3.6 控件垂直布局 label控件在image控件的下方显示

在使用 LVGL 8.3.6 创建一个垂直布局,其中 label 控件位于 image 控件下方,你可以使用 lv_obj_set_flex_flow 来设置布局为垂直,并确保 label 控件在 image 控件后添加。这里是如何步骤性地实现它的一个基本示例: 创建父容器:首先创建一个容器对象,该对象将作为布局的基础。设置容器为垂直布局:使用 lv_obj_set_flex_flow 设置容器

C# dateTimePicker 显示年月日,时分秒

dateTimePicker默认只显示日期,如果需要显示年月日,时分秒,只需要以下两步: 1.dateTimePicker1.Format = DateTimePickerFormat.Time 2.dateTimePicker1.CustomFormat = yyyy-MM-dd HH:mm:ss Tips:  a. dateTimePicker1.ShowUpDown = t