svg图形计算、矩阵函数计算、图形点位绝对坐标计算

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:希望这篇文章可以对你提供思路或减少困惑,如果计算存在问题或文章存在问题欢迎大佬指点,对于之前只看不写的人,如果文章内容出入比较大还望包涵!!

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值