cad文件解析成svg文件后,若需要对svg文件在页面结合前端技术konva.js上进行绘制或编辑操作时,需要在已有svg文件基础上进行图形的点位换算等操作,将SVG代码格式化成DOM形式,本文章主要对于svg的一些规定格式进行计算与操作,若不符合规则即无法有效计算。
说明:本文主要对svg中的矩阵函数(matrix)进行点位计算,计算后文件代码中不含函数部分,所有点位均为绝对点位的值
matrix函数公式
一、对SVG文件内图形的计算处理
/**
* 提取transform(函数)和d(点位)属性的值,用于公式计算
* @author caiji
* @param node
* @param list //点位属性
* @param transform //函数属性
* @return void
* @date 2021/11/4 10:19
*/
public static void handleFileNode(Node node, List<String> list, List<String> transform){
for (int i = 0;i<node.getChildNodes().getLength();i++){
NamedNodeMap map = node.getChildNodes().item(i).getAttributes();
if(null != map){
if(map.getNamedItem("d") != null){
list.add(map.getNamedItem("d").getNodeValue());
}
if(map.getNamedItem("transform") != null){
transform.add(map.getNamedItem("transform").getNodeValue().substring(map.getNamedItem("transform").getNodeValue().indexOf("(")+1,map.getNamedItem("transform").getNodeValue().lastIndexOf(")")));
}
}
handleFileNode(node.getChildNodes().item(i),list,transform);
}
}
/**
* 处理transform中matrix矩阵函数的公式参数值
* @author caiji
* @param transformAttribute matrix函数值
* @return java.util.List<java.lang.Double>
* @date 2021/11/4 13:50
*/
public static List<Double> getTransformItem(String transformAttribute){
List<String> strings = Arrays.asList(transformAttribute.split(" "));
List<Double> pointItems = new ArrayList<>();
Double a = 0d;
Double b = 0d;
Double c = 0d;
Double d = 0d;
Double e = 0d;
Double f = 0d;
for (int i = 0 ; i< strings.size();i++){
switch (i){
case 0:
a = Double.parseDouble(strings.get(i));
pointItems.add(a);
break;
case 1:
b = Double.parseDouble(strings.get(i));
pointItems.add(b);
break;
case 2:
c = Double.parseDouble(strings.get(i));
pointItems.add(c);
break;
case 3:
d = Double.parseDouble(strings.get(i));
pointItems.add(d);
break;
case 4:
e = Double.parseDouble(strings.get(i));
pointItems.add(e);
break;
case 5:
f = Double.parseDouble(strings.get(i));
pointItems.add(f);
break;
default:
break;
}
}
return pointItems;
}
/**
* 按公式计算d属性里面的坐标并且重新赋值
* @author caiji
* @param node
* @param pointItem 矩阵函数matrix的公式参数
* @return void
* @date 2021/11/4 10:18
*/
public static void handleNodeForNewPoint(Node node, List<Double> pointItem,List<Double> pointListX,List<Double> pointListY){
for (int i = 0;i<node.getChildNodes().getLength();i++){
if(node.getChildNodes().item(i).hasAttributes()){
//text标签的点位值计算
if("text".equals(node.getChildNodes().item(i).getNodeName())){
if(node.getChildNodes().item(i).getAttributes().getNamedItem("x") != null && node.getChildNodes().item(i).getAttributes().getNamedItem("y") != null){
List<Double> textPointItem = getTransformItem(node.getChildNodes().item(i).getAttributes().getNamedItem("transform").getNodeValue().substring(node.getChildNodes().item(i).getAttributes().getNamedItem("transform").getNodeValue().indexOf("(")+1,node.getChildNodes().item(i).getAttributes().getNamedItem("transform").getNodeValue().lastIndexOf(")")));
Double tx = Double.parseDouble(node.getChildNodes().item(i).getAttributes().getNamedItem("x").getNodeValue());
Double ty = Double.parseDouble(node.getChildNodes().item(i).getAttributes().getNamedItem("y").getNodeValue());
Double newX = textPointItem.get(0)*tx+ty*textPointItem.get(2)+textPointItem.get(4);
Double newY = tx*textPointItem.get(1)+ty*textPointItem.get(3)+textPointItem.get(5);
Double newXs = pointItem.get(0)*newX+newY*pointItem.get(2)+pointItem.get(4);
Double newYs = newX*pointItem.get(1)+newY*pointItem.get(3)+pointItem.get(5);
node.getChildNodes().item(i).getAttributes().getNamedItem("x").setNodeValue(newXs.toString());
node.getChildNodes().item(i).getAttributes().getNamedItem("y").setNodeValue(newYs.toString());
pointListX.add(newXs);
pointListY.add(newYs);
}
}
//d属性的点位值计算
if(node.getChildNodes().item(i).getAttributes().getNamedItem("d") != null){
String data = node.getChildNodes().item(i).getAttributes().getNamedItem("d").getNodeValue();
data.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]);
//通过transform中的公式算出正确x,y点位
Double newX = pointItem.get(0)*numX+numY*pointItem.get(2)+pointItem.get(4);
Double newY = numX*pointItem.get(1)+numY*pointItem.get(3)+pointItem.get(5);
pointListX.add(newX);
pointListY.add(newY);
//拼接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());
}
//将transform属性设置为空
if(node.getChildNodes().item(i).getAttributes().getNamedItem("transform") != null){
node.getChildNodes().item(i).getAttributes().getNamedItem("transform").setNodeValue("");
}
}
handleNodeForNewPoint(node.getChildNodes().item(i),pointItem,pointListX,pointListY);
}
}
/**
* 按公式计算d属性里面的坐标并且重新按偏移量计算赋值
* @author caiji
* @param node
* @param offsetX
* @param offsetY
* @return void
* @date 2021/11/4 10:18
*/
public static void handleNodeForOffsetPoint(Node node, Double offsetX,Double offsetY,Double scale){
for (int i = 0;i<node.getChildNodes().getLength();i++){
if(node.getChildNodes().item(i).hasAttributes()){
//text标签点位值计算
if("text".equals(node.getChildNodes().item(i).getNodeName())){
if(node.getChildNodes().item(i).getAttributes().getNamedItem("x") != null && node.getChildNodes().item(i).getAttributes().getNamedItem("y") != null){
Double tx = Double.parseDouble(node.getChildNodes().item(i).getAttributes().getNamedItem("x").getNodeValue());
Double ty = Double.parseDouble(node.getChildNodes().item(i).getAttributes().getNamedItem("y").getNodeValue());
Double newX = tx-offsetX;
Double newY = ty-offsetY;
node.getChildNodes().item(i).getAttributes().getNamedItem("x").setNodeValue(newX.toString());
node.getChildNodes().item(i).getAttributes().getNamedItem("y").setNodeValue(newY.toString());
}
}
//data点位值计算
if(node.getChildNodes().item(i).getAttributes().getNamedItem("d") != null){
String data = node.getChildNodes().item(i).getAttributes().getNamedItem("d").getNodeValue();
data.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,scale);
}
}
二、处理文件调试
public static void main(String[] args) {
try {
String pdfFilePath = "/Users/Desktop/图形计算1.svg";
String imgPath = "/Users/Desktop/图形计算2.svg";
File file = new File(pdfFilePath);
//svg文件内容转换成xml格式
String parser = XMLResourceDescriptor.getXMLParserClassName();
SAXSVGDocumentFactory fs = new SAXSVGDocumentFactory(parser);
Document document = fs.createDocument(file.toURI().toString());
List<String> fileData = new ArrayList<>();
List<String> transformData = new ArrayList<>();
//svg的高宽值
Double svgWidth = Double.parseDouble(document.getDocumentElement().getAttribute("width"));
Double svgHeight = Double.parseDouble(document.getDocumentElement().getAttribute("height"));
//获取内容中data属性的值
handleFileNode(document.getDocumentElement(),fileData,transformData);
//单个transformData
List<Double> pointItems = getTransformItem(transformData.get(0));
List<Double> pointListX = new ArrayList<>();
List<Double> pointListY = new ArrayList<>();
//根据data数据获取所有的坐标数据分x,y两个集合存储
handleNodeForNewPoint(document.getDocumentElement(), pointItems,pointListX,pointListY);
//取图形的最大最小的x、y点位,计算图形高宽
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;
//计算大约在中间的图形最左上角点位(x轴点位为svg宽度/2-图形宽度/2;y轴点位为svg高度/2-图形高度/2)
//因为svg中y轴越往上越小,所以用高度相减 不设置为0,0 留一点编距 按需调整
Double initX = 5d;
Double initY = 5d;
Double offsetX = minXnum - initX;
Double offsetY = minYnum - initY;
Double ratio = svgHeight>svgWidth?svgHeight:svgWidth;
Double scale = ratio/x;
//根据偏移量重新计算所有点位
handleNodeForOffsetPoint(document.getDocumentElement(),offsetX,offsetY,scale);
String viewBox = "0 0 "+(x+initX*2)+" "+(y+initX*2);
document.getDocumentElement().setAttribute("viewBox",viewBox);
//dom节点转字符串,重新生成svg文件
String str = convertElemToSVG(document.getDocumentElement());
OutputStream outputStream = new FileOutputStream(imgPath);
OutputStreamWriter os = new OutputStreamWriter(outputStream,"utf-8");
os.write(str);
os.close();
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 将dom对象转换成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;
}
ps:希望这篇文章可以对你提供思路或减少困惑,如果计算存在问题或文章存在问题欢迎大佬指点,对于之前只看不写的人,如果文章内容出入比较大还望包涵!!