社内向けにブラウザ上でSVGデータを作りDXFファイルに変換して、最終的にCADへ読み込むようにする仕組みを作りました。ブラウザでのSVGの作成はSnap.svgで。SVGからDXF変換はInkscapeのコマンドラインを採用です。
SVGの作成といっても、簡単な円と直線。そしてすでにあるSVGデータの配置をするだけのものなので、7年前のSnap.svg v0.5.1で十分対応できました。
SVGからDXFへの変換はUbuntu 22.04の標準パッケージでInkscape 1.1.2となります。InkscapeのDXF変換はR12とR14向けがあって、デフォルトのR12では円弧が分割された直線で表現されます。これはちょっとCADデータとしてよくないです。R14ではポリラインで出力可能。ということで、こちらの投稿を参考に、以下のようにしてR14形式でSVGからDXFに変換可能となりました。
$ /usr/bin/inkscape --actions="select-all:all;object-to-path" --export-type=dxf --export-extension=org.ekips.output.dxf_outlines --export-filename=out.dxf in.svg
試行錯誤したのがSVG、DXFの単位系の処理でした。配置するSVGのPATHはmm単位で作ったCADのDXFファイルをIllustratorで読み込んでSVGに出力したもの。ブラウザ上のSVG作成と配置は基本px。最終的なDXFファイルはmmと、96dpiと72dpi、そして1inch=25.4mmの数値をなんとか逆算のやりくりで最終的なmm単位でのDXFファイル出力にたどり着けた感じです。
InkscapeでのDXF出力でバグっぽい挙動がありました。図形の大きさによって、DXFファイルのデータが1.33倍になります。通常は思い通りの寸法なのに、円の半径を少し変えるだけでそうなってしまいます。テストしたほとんどの例では問題はなく、出力されたSVGファイルもpxからmmに変換すると正しく、数値以外に違いはないのでSnap.svgが原因ではなさそう。1.33倍は96/72なので、Inkscapeでの単位系の変換処理が関係してそうです。UbuntuのPPAを使ってのInkscape 1.2.2を導入しても改善しませんでした。
InkscapeのR14形式でのDXF変換を実施しているpythonスクリプトは、
$ python3 /usr/share/inkscape/extensions/dxf_outlines.py --help
usage: dxf_outlines.py [-h] [--output OUTPUT] [--tab TAB] [-R ROBO] [-P POLY] [--units UNITS]
[--encoding CHAR_ENCODE] [--layer_option LAYER_OPTION]
[--layer_name LAYER_NAME] [--id IDS] [--selected-nodes SELECTED_NODES]
[INPUT_FILE]positional arguments:
INPUT_FILE Filename of the input file (default is stdin)options:
-h, --help show this help message and exit
--output OUTPUT Optional output filename for saving the result (default is stdout).
--tab TAB
-R ROBO, --ROBO ROBO
-P POLY, --POLY POLY
--units UNITS
--encoding CHAR_ENCODE
--layer_option LAYER_OPTION
--layer_name LAYER_NAME
--id IDS id attribute of object to manipulate
--selected-nodes SELECTED_NODES
id:subpath:position of selected nodes, if any
のようにunitsオプションがあって、これを指定すればいいようですが、コマンドラインにてInkscapeからエクステンションへオプションを渡す方法はないようです。
このシステムにインストールしたInkscapeは、このコマンドラインでのDXF変換しか使わないので、最終的にdxf_outlines.pyを直接編集してunitsを固定する強引な解決方法としました。終わりの方にあるunitsからスケールを実行する
self.groupmat = [[[scale, 0.0, 0.0], [0.0, -scale, h * scale]]]
の直前に
scale = 72./ 96
self.groupmat = [[[scale, 0.0, 0.0], [0.0, -scale, h * scale]]]
とscaleの値を強制的に0.75としていちおう解決です。
dxf_outlines.pyのscaleを決定するのは、上記のちょっと上らへんを見るとviewBoxの数値から決定されているようです。
問題のないSVGが
viewBox="-39.08061599731445 -40.8072395324707 78.16123962402344 81.25476837158203"
問題のSVGが
viewBox="-37.1966667175293 -38.83620071411133 74.3933334350586 77.33402252197266"
なんで、小数点以下の桁数がバグに関係してそうなんですが、実際のところどうなんでしょうね。Inkscapeのソースはちらっと見て追いかけるのはあきらめました。
追記
PPAでInkscapeの現在の最新版1.3.2をインストールして試してみました。
どうやら一律、1.33倍で出力されているように思います。ソースを見るとバージョン1.3からこの辺のDXF出力まわりのpyファイルが大幅に書き換えられているようで、まあ全件で1.33倍となるならまだ対応できそうです。