本文主要对CAD文件解析后,若一份CAD文件中整个图有多个图形需要切割拆分,根据指定规则用独特颜色(内容中没有与其相同的颜色,确保不会因为颜色影响图形内容)的框将需要切割图形单独框起来,再做切割,切割后进行文件的大小缩放计算
直接上代码
一、图形计算相关代码
* [检查一个path的点位是否在框内]
* @author caiji
* @param pathPoint path点位
* @param framePoint 矩形框的坐标点
* @return boolean
* @date 2022/4/21 17:21
*/
public static boolean checkIsInFrame(List<PointEntity> pathPoint, List<PointEntity> framePoint){
boolean flag = false;
//统一排序后的顺序为 左上,左下,右上,右下
framePoint = framePoint.stream().sorted(Comparator.comparing(PointEntity::getPointX).thenComparing(PointEntity::getPointY)).collect(Collectors.toList());
for (PointEntity path : pathPoint){
//判断点位是否在框的四个点范围内
//与左上点比较,x和y都应该大于对应的x,y
//与左下点比较,x大于对应x,y小于对应y
//与右上点比较,x小于对应x,y大于对应y
//与右下点比较,x小于对应x,y小于对应y
if((path.getPointX()>framePoint.get(0).getPointX() && path.getPointY()>framePoint.get(0).getPointY()) &&
(path.getPointX()>framePoint.get(1).getPointX() && path.getPointY()<framePoint.get(1).getPointY()) &&
(path.getPointX()<framePoint.get(2).getPointX() && path.getPointY()>framePoint.get(2).getPointY()) &&
(path.getPointX()<framePoint.get(3).getPointX() && path.getPointY()<framePoint.get(3).getPointY())) {
flag = true;
}
}
return flag;
}
/**
* [根据path的data数据 获取对应x、y点位]
* @author caiji
* @param path
* @return java.util.List<com.example.importexceldemo.common.PointEntity>
* @date 2022/4/21 17:45
*/
public static List<PointEntity> pathPointHandle(String path){
List<PointEntity> result = new ArrayList<>();
//切割M
List<String> splitArray = Arrays.asList(path.split("M"));
for (String o : splitArray) {
if (!"".equals(o)) {
//切割L
List<String> pointArray = Arrays.asList(o.split("L"));
for (int k = 0; k < pointArray.size(); k++) {
String strItem = pointArray.get(k);
//处理其他字母做结尾的坐标
if (!Character.isDigit(strItem.substring(strItem.length() - 1).charAt(0))) {
strItem = strItem.substring(0, strItem.length() - 1);
}
//0为x 1为y
PointEntity pointEntity = new PointEntity();
pointEntity.setPointX(Double.parseDouble(strItem.split(" ")[0]));
pointEntity.setPointY(Double.parseDouble(strItem.split(" ")[1]));
result.add(pointEntity);
}
}
}
return result;
}
/**
* [获取图框的数量(根据颜色),以及点位]
* 思路:按照一个矩形框有四条path属性计算,并且矩形的点位仅四个
* @author caiji
* @param node
* @param framePoint
* @return void
* @date 2022/4/25 18:05
*/
public static void getFrameCount(final Node node,List<PointEntity> framePoint){
for (int i = 0;i<node.getChildNodes().getLength();i++) {// 遍历所有一级子节点
NamedNodeMap map = node.getChildNodes().item(i).getAttributes();
if(null != map){
if(map.getNamedItem("stroke") != null && map.getNamedItem("stroke").getNodeValue().equals("#0000FF")) {
//如果为特定颜色,则为指定的矩形框
//取当前data数据,并且取第一个点,因为是矩形,所以存在点位重合,只需要取第一个点即可
String point = map.getNamedItem("d").getNodeValue();
PointEntity pointEntity = new PointEntity();
//取第一个点
String firstPoint = point.substring(point.indexOf("M")+1,point.indexOf("L"));
pointEntity.setPointX(Double.parseDouble(firstPoint.split(" ")[0]));
pointEntity.setPointY(Double.parseDouble(firstPoint.split(" ")[1]));
framePoint.add(pointEntity);
}
}
getFrameCount(node.getChildNodes().item(i),framePoint);// 递归
}
}
/**
* [根据图框数量生成新的DOM格式字符串 -- 即SVG (前提:CAD解析后并计算绝对坐标点位后再做这些处理)]
* 思路:传入整个图的node,然后根据计算是否在框内范围将数据存入新的node
* @author songxy
* @param node
* @param framePoint
* @param newDoc
* @return void
* @date 2022/4/25 18:05
*/
public static void generateNewDomByFrame(final Node node,List<PointEntity> framePoint,Node newDoc){
for (int i = 0;i<node.getChildNodes().getLength();i++) {// 遍历所有一级子节点
NamedNodeMap map = node.getChildNodes().item(i).getAttributes();
if(null != map && null != map.getNamedItem("d")){
String data = map.getNamedItem("d").getNodeValue();
List<PointEntity> dataPoint = pathPointHandle(data);
//取到本次path的data点位,并与框的点位比较,如果在范围内则保存
boolean flag = checkIsInFrame(dataPoint,framePoint);
if(flag){
Node newChild = node.getChildNodes().item(i).cloneNode(false);
newDoc.appendChild(newChild);
}
}
generateNewDomByFrame(node.getChildNodes().item(i),framePoint,newDoc);// 递归
}
}
/**
* [处理文件切割后的尺寸计算]
* 思路:CAD解析时存在大样图实际尺寸较大(几万+),所以解析时设置固定尺寸后,文件中的图也会相应压缩,切割后可能要处理显示上存在的位置以及尺寸问题
* @author songxy
* @param node
* @return void
* @date 2022/4/26 09:54
*/
public static void handleDomSize(Node node){
List<String> list = new ArrayList<>();
List<Double> pointListX = new ArrayList<>();
List<Double> pointListY = new ArrayList<>();
getCadSizeData(node,list,pointListX,pointListY);
Map<String,Double> map = calculationCadSize(pointListX,pointListY);
Double x = map.get("width");
Double y = map.get("height");
Double minXnum = map.get("minXnum");
Double minYnum = map.get("minYnum");
//根据最大最小坐标值计算内容的高宽值
//因为svg中y轴越往上越小,所以用高度相减
//设置初始点位10,10 计算相对坐标值,不设置为0,0 留一点边距
Double initX = 10d;
Double initY = 10d;
Double offsetX = minXnum - initX;
Double offsetY = minYnum - initY;
handleNodeForOffsetPoint(node,offsetX,offsetY);
x = x+initX*2;
y = y+initY*2;
String viewBox = "0 0 "+x+" "+y;
Node attr = node.getOwnerDocument().createAttribute("viewBox");
attr.setNodeValue(viewBox);
node.getAttributes().setNamedItem(attr);
}
/**
* [将所有的框的点位平分,得出有几个框就有几个图]
* 注意:此处必须是绘制的框是一次性绘制的,不能有框绘制到一半绘制另一个框的情况
* @author songxy
* @param pointEntities
* @return java.util.List<java.util.List<com.example.importexceldemo.common.PointEntity>>
* @date 2022/4/22 14:10
*/
public static List<List<PointEntity>> averagePoint(List<PointEntity> pointEntities){
List<List<PointEntity>> result = new ArrayList<>();
//框的数量,也是图的数量、需要将pointEntities平分的份数
int frameCount = pointEntities.size()/4;
//余数
int remainder = pointEntities.size()%frameCount;
//商
int number = pointEntities.size()/frameCount;
int offset = 0;
for (int i = 0;i<frameCount;i++){
List<PointEntity> item = new ArrayList<>();
if(remainder > 0){
item = pointEntities.subList(i*number+offset,(i+1)*number+offset+1);
remainder--;
offset++;
}else{
item = pointEntities.subList(i*number+offset,(i+1)*number+offset);
}
result.add(item);
}
return result;
}
/**
* 获取图形宽高尺寸
* @author songxy
* @param node
* @return java.util.Map<java.lang.String,java.lang.Double>
* @date 2021/11/5 20:33
*/
public static void getCadSizeData(Node node, List<String> list,List<Double> pointListX,List<Double> pointListY){
for (int i = 0;i<node.getChildNodes().getLength();i++){
NamedNodeMap map = node.getChildNodes().item(i).getAttributes();
if(null != map && map.getNamedItem("d") != null){
list.add(map.getNamedItem("d").getNodeValue());
String data = map.getNamedItem("d").getNodeValue().replace("[","").replace("]","");
//切割M
List<String> splitArray = Arrays.asList(data.split("M"));
for (String o : splitArray){
if(!"".equals(o)) {
//切割L
List<String> pointArray = Arrays.asList(o.split("L"));
for (int k = 0; k < pointArray.size(); k++) {
String strItem = pointArray.get(k);
//处理其他字母做结尾的坐标
if(!Character.isDigit(strItem.substring(strItem.length()-1).charAt(0))){
strItem = strItem.substring(0,strItem.length()-1);
}
//0为x 1为y
Double numX = Double.parseDouble(strItem.split(" ")[0]);
Double numY = Double.parseDouble(strItem.split(" ")[1]);
pointListX.add(numX);
pointListY.add(numY);
}
}
}
}
getCadSizeData(node.getChildNodes().item(i),list,pointListX,pointListY);
}
}
/**
* 计算图形高宽
* @author songxy
* @param pointListX
* @param pointListY
* @return java.util.Map<java.lang.String,java.lang.Double>
* @date 2021/11/6 12:55
*/
public static Map<String,Double> calculationCadSize(List<Double> pointListX, List<Double> pointListY){
Double maxXnum = Double.parseDouble(Collections.max(pointListX).toString());
Double minXnum = Double.parseDouble(Collections.min(pointListX).toString());
Double maxYnum = Double.parseDouble(Collections.max(pointListY).toString());
Double minYnum = Double.parseDouble(Collections.min(pointListY).toString());
//根据最大最小坐标值计算内容的高宽值
Double x = maxXnum-minXnum;
Double y = maxYnum-minYnum;
Map<String,Double> map = new HashMap<>();
map.put("width",x);
map.put("height",y);
map.put("minXnum",minXnum);
map.put("minYnum",minYnum);
return map;
}
/**
* 按公式计算d属性里面的坐标并且重新按偏移量计算赋值
* @author songxy
* @param node
* @param offsetX
* @param offsetY
* @return void
* @date 2021/11/4 10:18
*/
public static void handleNodeForOffsetPoint(Node node, Double offsetX,Double offsetY){
for (int i = 0;i<node.getChildNodes().getLength();i++){
if(node.getChildNodes().item(i).hasAttributes() && node.getChildNodes().item(i).getAttributes().getNamedItem("d") != null){
String data = node.getChildNodes().item(i).getAttributes().getNamedItem("d").getNodeValue().replace("[","").replace("]","");
//切割M
List<String> splitArray = Arrays.asList(data.split("M"));
StringBuffer newData = new StringBuffer();
for (String o : splitArray){
if(!"".equals(o)) {
//切割L
List<String> pointArray = Arrays.asList(o.split("L"));
for (int k = 0; k < pointArray.size(); k++) {
String lastChar = "";
String strItem = pointArray.get(k);
if(!Character.isDigit(strItem.substring(strItem.length()-1).charAt(0))){
lastChar = strItem.substring(strItem.length()-1);
strItem = strItem.substring(0,strItem.length()-1);
}
//0为x 1为y
Double numX = Double.parseDouble(strItem.split(" ")[0]);
Double numY = Double.parseDouble(strItem.split(" ")[1]);
//根据偏移量计算新点位
Double newX = numX-offsetX;
Double newY = numY-offsetY;
//拼接d属性值的新坐标
if (k == 0) {
newData.append("M");
newData.append(newX);
newData.append(" ");
newData.append(newY);
}else{
newData.append("L");
newData.append(newX);
newData.append(" ");
newData.append(newY);
newData.append(lastChar);
}
}
}
}
node.getChildNodes().item(i).getAttributes().getNamedItem("d").setNodeValue(newData.toString());
}
handleNodeForOffsetPoint(node.getChildNodes().item(i),offsetX,offsetY);
}
}
/**
* 将element转换成XML字符串
* @param element
* @return
*/
public static String convertElemToSVG(Node element) {
TransformerFactory transFactory = TransformerFactory.newInstance();
Transformer transformer = null;
try {
transformer = transFactory.newTransformer();
} catch (TransformerConfigurationException e) {
e.printStackTrace();
}
StringWriter buffer = new StringWriter();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
try {
transformer.transform(new DOMSource(element), new StreamResult(buffer));
} catch (TransformerException e) {
e.printStackTrace();
}
String elementStr = buffer.toString();
return elementStr;
}
二、测试代码 万年main方法调试
public static void main(String[] args) {
String svgFilePath = "/CAD文件/04253.svg";
try {
File file = new File(svgFilePath);
String parser = XMLResourceDescriptor.getXMLParserClassName();
SAXSVGDocumentFactory f = new SAXSVGDocumentFactory(parser);
Document doc = f.createDocument(file.toURI().toString());
List<PointEntity> pointEntities = new ArrayList<>();
//获取所有特定框的点位
getFrameCount(doc.getDocumentElement(),pointEntities);
//处理点位数据,得到有多少个框,即多少个图形和图形的框的四个点位
List<List<PointEntity>> frameList = averagePoint(pointEntities);
//根据框数将图形全部生成新的文件
for (int i = 1;i<=frameList.size();i++){
Node newDoc = doc.getDocumentElement().cloneNode(false);
//生成新的dom
generateNewDomByFrame(doc.getDocumentElement(),frameList.get(i-1),newDoc);
//切割后图形所在的位置不会变更,所以需要处理dom尺寸
handleDomSize(newDoc);
String str = convertElemToSVG(newDoc);
OutputStream outputStream = new FileOutputStream("/CAD文件/0455"+i+".svg");
OutputStreamWriter os = new OutputStreamWriter(outputStream,"utf-8");
os.write(str);
os.close();
}
}catch (Exception e){
e.printStackTrace();
}
}