InDesignで、選択範囲をSVG書き出しするスクリプト


自分だけで持ってても使いどころがないので蔵出し。

* 適当なオブジェクトを選択してスクリプト実行、保存ダイアログに「xxx.svg」とつけて保存してください。
* 線、四角形、楕円、多角形しか書き出しません。
* 文字はアウトラインを取ってください。

```javascript:SVG書き出し.jsx
(function(){
    if(app.documents.length){
        main(app.activeDocument);
    }
}());

function main(doc){
    if(!doc.selection.length){ return; };
    doc.viewPreferences.properties={
        horizontalMeasurementUnits:MeasurementUnits.POINTS,
        verticalMeasurementUnits:MeasurementUnits.POINTS,
        strokeMeasurementUnits:MeasurementUnits.POINTS,
    };
    var objs = doc.selection;
    var pos = getVisiblePosition(objs);
    var svgTxtAry = [];
    svgTxtAry.push('<?xml version="1.0" encoding="utf-8"?>');
    svgTxtAry.push('<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">');
    svgTxtAry.push(
        '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"'
        +' x="0pt" y="0pt" width="'+round1000(pos.w)+'pt" height="'+round1000(pos.h)+'pt"'
        +' viewBox="0 0 '+round1000(pos.w)+' '+round1000(pos.h)+'">'
    );
    for(var i=0,len=objs.length;i<len;i++){
        var obj = objs[i];
        //$.writeln(obj.constructor.name);
        switch(obj.constructor.name){
            case 'GraphicLine':
            case 'Oval':
            case 'Rectangle':
            case 'Polygon':
                svgTxtAry.push(getSvgPath(obj,pos));
                break;
            default:
                break;
        }
    }
    svgTxtAry.push('</svg>');
    //$.writeln(svgTxtAry.join('\n'));
    var svgFile = File.saveDialog('SVGを保存');
    if(svgFile){
        svgFile.encoding = 'UTF8';
        svgFile.lineFeed = 'Unix';
        if(svgFile.open('w')){
            svgFile.write(svgTxtAry.join('\n'));
            svgFile.close();
        }else{
            alert('SVGを保存できません');
        }
    }
}

function getVisiblePosition(objs){
    var bns = objs[0].visibleBounds.slice();
    for(var i=1,len=objs.length;i<len;i++){
        var _bns = objs[i].visibleBounds;
        bns[0]=Math.min(bns[0],_bns[0]);
        bns[1]=Math.min(bns[1],_bns[1]);
        bns[2]=Math.max(bns[2],_bns[2]);
        bns[3]=Math.max(bns[3],_bns[3]);
    }
    return {x:bns[1],y:bns[0],w:bns[3]-bns[1],h:bns[2]-bns[0]};
}

function getSvgPath(obj,pos){
    var paths = obj.paths;
    var pathAry = [];
    for(var i=0,len=paths.length;i<len;i++){
        var path = paths[i];
        var roundPath = normalizePath(path.entirePath,[pos.x,pos.y],true);
        var svgPath = pathToSvgPath(
            roundPath,(path.pathType===PathType.OPEN_PATH)
        );
        pathAry.push(svgPath);
    }
    return '<path fill="'+ getSvgColor(obj.fillColor)+'"'
        +' stroke="'+ getSvgColor(obj.strokeColor)+'"'
        +' stroke-width="'+ obj.strokeWeight+'"'
        +' d="'+pathAry.join(' ')+'"'+getLineCapAndjoin(obj)+' />';
}

function getSvgColor(color){
    switch(color.name){
        case 'None':return 'none';
        case 'Black':return '#000000';
        case 'Paper':return '#FFFFFF';
        default: break;
    }
    var colorValue;
    if(color.space==ColorSpace.RGB){
        colorValue = color.colorValue;
    }else{
        var tmpColor = color.duplicate();
        tmpColor.space = ColorSpace.RGB;
        colorValue = tmpColor.colorValue;
        tmpColor.remove();
    }
    return '#'+toHex(colorValue[0])+toHex(colorValue[1])+toHex(colorValue[2]);
};

function getLineCapAndjoin(obj){
    var ary = [];
    var strokeLinecap,strokeLinejoin,strokeDasharray;
    if(obj.strokeWeight){
        //線の先
        switch(obj.endCap){
            case EndCap.ROUND_END_CAP:
              strokeLinecap="round";break;
            case EndCap.PROJECTING_END_CAP:
              strokeLinecap="square";break;
            case EndCap.BUTT_END_CAP:
              strokeLinecap="butt";break;
            default:break;
        };
        if(strokeLinecap){
            ary.push('stroke-linecap="'+strokeLinecap+'"');
        }
        //線の角
        switch(obj.endJoin){
            case EndJoin.ROUND_END_JOIN:
              strokeLinejoin="round";break;
            case EndJoin.BEVEL_END_JOIN:
              strokeLinejoin="bevel";break;
            case EndJoin.MITER_END_JOIN:
              strokeLinejoin="miter";break;
            default:break;
        };
        if(strokeLinejoin){
            ary.push('stroke-linejoin="'+strokeLinejoin+'"');
        };
        try{
            strokeDasharray = obj.strokeDashAndGap;
        }catch(e){};
        if(strokeDasharray){
            ary.push('stroke-dasharray="'+strokeDasharray.join(',')+'"');
        };
    }
    return ary.length? ' '+ary.join(' '):'';
}

function toHex(N) {
   if (N==null) return "00";
   N=parseInt(N); if (N==0 || isNaN(N)) return "00";
   N=Math.max(0,N); N=Math.min(N,255); N=Math.round(N);
   return "0123456789ABCDEF".charAt((N-N%16)/16)
        + "0123456789ABCDEF".charAt(N%16);
};

function pathToSvgPath(entirePath,pathOpen){
    var pathAry = [];
    var command = 'L';//デフォルトはL
    var startPoint,endPoint;
    for(var p=0,max=(pathOpen?entirePath.length-1:entirePath.length);p<max;p++){
        startPoint = null, endPoint = null;
        startPoint = entirePath[p];
        if(startPoint.length==2) startPoint = [startPoint,startPoint,startPoint];
        endPoint = entirePath[p+1]||entirePath[0];
        if(endPoint.length==2) endPoint = [endPoint,endPoint,endPoint];

        pathAry.push(startPoint[1].join(','));//支点
        if(startPoint[1][0]==startPoint[2][0] && startPoint[1][1]==startPoint[2][1]
            && endPoint[0][0]==endPoint[1][0] && endPoint[0][1]==endPoint[1][1]){
            if(command!=='L'){ pathAry.push('L');command = 'L'; };
        }else{
            if(command!=='C'){ pathAry.push('C');command = 'C'; };
            pathAry.push(startPoint[2].join(','));
            pathAry.push(endPoint[0].join(','));
        }
    }
    pathAry.push(endPoint[1].join(','));
    if(!pathOpen){ pathAry.push('z'); };
    pathAry.unshift('M');
    return pathAry.join(' ');
};

function normalizePath(pathPoints,zeroPoint){
    //オブジェクトの0位置から計算したパスを書き出す。
    if(!zeroPoint) zeroPoint = [0,0];
    var i = pathPoints.length;
    var _pathPoints = new Array(i);
    while(i--){
        var pathPoint = pathPoints[i];
        if(typeof pathPoint[0]=='number'){
            _pathPoints[i] = [
                round1000(pathPoint[0]-zeroPoint[0]),
                round1000(pathPoint[1]-zeroPoint[1])
            ];
        }else{
            _pathPoints[i] = normalizePath(pathPoint,zeroPoint);
        }
    };
    return _pathPoints;
};

function round1000(n){
    return Math.round(n*1000)/1000;
};

```