Let's talk about libredwg support

Have some feature requests, feedback, cool stuff to share, or want to know where FreeCAD is going? This is the place.
Forum rules
Be nice to others! Read the FreeCAD code of conduct!
User avatar
looo
Veteran
Posts: 3941
Joined: Mon Nov 11, 2013 5:29 pm

Let's talk about libredwg support

Post by looo »

We definetly need to support dwg-format via libredwg. libredwg now has python bindings.

Conda-forge now has packages for libredwg (linux/ osx) [1]. Releases for windows are also available [2]. But I am not sure if these are compatible with freecad. We also tried to create a conda-package for windows but failed with the mingw toolchain. There was one suggestion to use clang on windows to build libredwg [3]. I didn't try that yet, but this for sure is a possibility for the future.

@yorik
Can you briefly explain how to replace our current dwg-library (tigher?) with libredwg?

[1] https://github.com/conda-forge/libredwg-feedstock
[2] https://github.com/LibreDWG/libredwg/releases
[3] https://github.com/conda-forge/staged-r ... -496008470
vocx
Veteran
Posts: 5197
Joined: Thu Oct 18, 2018 9:18 pm

Re: Let's talk about libredwg support

Post by vocx »

looo wrote: Fri Oct 04, 2019 1:15 pm ...
@yorik
Can you briefly explain how to replace our current dwg-library (tigher?) with libredwg?
...
"TeighaConverter" is the old name, the new name is "ODAConverter".

Is libredwg a series of dynamically linked libraries, or does it provide an executable?

The current implementation in Draft/importDWG.py is that it searches for an executable defined in the parameter editor.

Code: Select all

    p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft")
    p = p.GetString("TeighaFileConverter")
    if p:
        # path set manually
        teigha = p
Otherwise it looks for standard locations:

Code: Select all

teigha = "/usr/bin/TeighaFileConverter"
teigha = "/usr/bin/ODAFileConverter"
teigha = "TeighaFileConverter.exe"
Then it just runs the executable with the appropriate input file and options through subprocess

Code: Select all

cmdline = ('"%s" "%s" "%s" "ACAD2000" "DXF" "0" "1" "%s"'
                   % (teigha, indir, outdir, basename))
...
subprocess.call(cmdline, shell=True)
So it runs something like this

Code: Select all

/usr/bin/ODAFileConverter indir outdir ACAD2000 DXF 0 1 filename.dwg
This is done to convert the DWG to DXF. Once this is done, the DXF is imported by Draft/importDXF.py.

Code: Select all

    dxf = convertToDxf(filename)
    if dxf:
        import importDXF
        doc = importDXF.open(dxf)
        return doc
Then the DXF is read with the legacy importer (downloads the files from Yorik's repository) or with the Import module of FreeCAD.

Code: Select all

if dxfUseLegacyImporter:
    processdxf(doc, filename)
        return doc
else:
    import Import
    Import.readDXF(filename)
As I see, the Import module uses an ImpExpDxfRead class, which uses a CDxfRead class, defined in C++ in Draft/App/dxf.cpp.

Therefore, libredwg could be used at different stages. If it includes an executable, it can be used instead of ODAConverter to convert to DXF, and then follow that path.

Code: Select all

if libredwg:
    command = libreconvert
else:
    command = ODAFileConverter

subprocess.call(command)
If it has Python bindings and can perform direct DWG to DXF then its much simpler

Code: Select all

if libredwg:
    dxf = libredwg(filename, "DXF")
else:
    # ODA or Teigha
    dxf = convertToDxf(filename)

if dxf:
    import importDXF
    doc = importDXF.open(dxf)
    return doc
Always add the important information to your posts if you need help. Also see Tutorials and Video tutorials.
To support the documentation effort, and code development, your donation is appreciated: liberapay.com/FreeCAD.
User avatar
looo
Veteran
Posts: 3941
Joined: Mon Nov 11, 2013 5:29 pm

Re: Let's talk about libredwg support

Post by looo »

vocx wrote: Fri Oct 04, 2019 5:01 pm Is libredwg a series of dynamically linked libraries, or does it provide an executable?
As far as I know, it's both: a c-library with python bindings and some executables for converting between different file formats (dxf, dwg, svg). So pretty generic. This are the executables which are included in the libredwg package:

Code: Select all

dwg2dxf  dwg2SVG  dwgbmp  dwggrep  dwglayers  dwgread  dwgrewrite  dxf2dwg
Thanks for diving into this. So currently we import dwg by converting dwg-fieles to dxf and afterwards read the dxf. So a replacement with libredwg should be straight forward. The easiest option is to use the dwg2dxf executable. Afterwards we might try to create a direct dwg importer by using the libredwg python interface (not sure if python is too slow for this task)
vocx
Veteran
Posts: 5197
Joined: Thu Oct 18, 2018 9:18 pm

Re: Let's talk about libredwg support

Post by vocx »

looo wrote: Fri Oct 04, 2019 8:37 pm Thanks for diving into this. So currently we import dwg by converting dwg-fieles to dxf and afterwards read the dxf. So a replacement with libredwg should be straight forward. The easiest option is to use the dwg2dxf executable.
Something like this

Code: Select all

if libredwg:
    command = "dwg2dxf"
    dxf = subprocess.popen(command, filename)
    # or if the program is available as a Python module
    # dxf = libredwg.dwg2dxf(filename)
else:
    # ODA or Teigha
    dxf = convertToDxf(filename)

if dxf:
    import importDXF
    doc = importDXF.open(dxf)
    return doc
Afterwards we might try to create a direct dwg importer by using the libredwg python interface (not sure if python is too slow for this task)
Well, the legacy importer is made of 4 Python libraries, and I think it works okay, https://github.com/yorikvanhavre/Draft-dxf-importer

But I haven't tested with extremely large DXF files. As matter of fact, Draft will show a dialog when there are more than 1000 objects, maybe to warn the user, "this may take a while".

If you know of a test file that can be used to test many things, it would be nice to have it. There is currently no unit test in Draft to test DXF import and export, so just the slightest change can break the code, so it would be nice to have this unit test.
Always add the important information to your posts if you need help. Also see Tutorials and Video tutorials.
To support the documentation effort, and code development, your donation is appreciated: liberapay.com/FreeCAD.
User avatar
sgrogan
Veteran
Posts: 6499
Joined: Wed Oct 22, 2014 5:02 pm

Re: Let's talk about libredwg support

Post by sgrogan »

vocx wrote: Fri Oct 04, 2019 8:53 pm But I haven't tested with extremely large DXF files
Yorik recommends the internal C++ importer for large files, like the BIM guys use. The legacy importer has more features though, bsplines for example (unless this has changed recently)

I think it's a good idea to add libredwg and leave ODA as your code suggests.

I will look at looo's links, hopefully the python bindings are pure python.
"fight the good fight"
vocx
Veteran
Posts: 5197
Joined: Thu Oct 18, 2018 9:18 pm

Re: Let's talk about libredwg support

Post by vocx »

sgrogan wrote: Fri Oct 04, 2019 9:17 pm Yorik recommends the internal C++ importer for large files, like the BIM guys use. The legacy importer has more features though, bsplines for example (unless this has changed recently)
Yeah, I don't understand why it's called "legacy". It makes it sound old. It should just be called "Python importer", and "C++ importer". As you say, the Python importer may work better in some cases.
I think it's a good idea to add libredwg and leave ODA as your code suggests.
...
Yes, I think it would be good to keep the ODA option available for whoever wants it. Adding a new DWG importer shouldn't be a big problem, as long as it converts to DXF it should be fine.
Always add the important information to your posts if you need help. Also see Tutorials and Video tutorials.
To support the documentation effort, and code development, your donation is appreciated: liberapay.com/FreeCAD.
User avatar
looo
Veteran
Posts: 3941
Joined: Mon Nov 11, 2013 5:29 pm

Re: Let's talk about libredwg support

Post by looo »

sgrogan wrote: Fri Oct 04, 2019 9:17 pm I will look at looo's links, hopefully the python bindings are pure python.
no, it's wrapped with swig. :o
User avatar
looo
Veteran
Posts: 3941
Joined: Mon Nov 11, 2013 5:29 pm

Re: Let's talk about libredwg support

Post by looo »

First step was easy. But testing to export (to dwg) and import (from dwg) a circle created in freecad didn't work. But opening dwg's from libredwg/tests is working.

Here is the diff used so far:

Code: Select all

diff --git a/src/Mod/Draft/importDWG.py b/src/Mod/Draft/importDWG.py
index bcc005770..ea619f02d 100644
--- a/src/Mod/Draft/importDWG.py
+++ b/src/Mod/Draft/importDWG.py
@@ -203,6 +203,17 @@ def convertToDxf(dwgfilename):
         The new file produced.
     """
     import os, tempfile, subprocess, sys
+
+    ############################ try libredwg
+    import shutil
+    if shutil.which("dwg2dxf"):
+        outdir = tempfile.mkdtemp()
+        basename = os.path.basename(dwgfilename)
+        result = outdir + os.sep + os.path.splitext(basename)[0] + ".dxf"
+        subprocess.call(("dwg2dxf", dwgfilename, "-o", result))
+        return result
+    ############################ end libredwg
+
     teigha = getTeighaConverter()
     if teigha:
         indir = os.path.dirname(dwgfilename)
@@ -249,6 +260,14 @@ def convertToDwg(dxffilename, dwgfilename):
         The same `dwgfilename` file path.
     """
     import os, subprocess
+
+    ############################ try libredwg
+    import shutil
+    if shutil.which("dxf2dwg"):
+        subprocess.call(("dxf2dwg", dxffilename, "-o", dwgfilename))
+        return dwgfilename
+    ############################ end libredwg
+
     teigha = getTeighaConverter()
     if teigha:
         indir = os.path.dirname(dxffilename)

User avatar
Kunda1
Veteran
Posts: 13434
Joined: Thu Jan 05, 2017 9:03 pm

Re: Let's talk about libredwg support

Post by Kunda1 »

There is a comment in that file:

Code: Select all

# TODO: use subprocess.popen() instead of subprocess.call()
@looo doesn't it make sense to follow this advice ?
Alone you go faster. Together we go farther
Please mark thread [Solved]
Want to contribute back to FC? Checkout:
'good first issues' | Open TODOs and FIXMEs | How to Help FreeCAD | How to report Bugs
vocx
Veteran
Posts: 5197
Joined: Thu Oct 18, 2018 9:18 pm

Re: Let's talk about libredwg support

Post by vocx »

Kunda1 wrote: Sat Oct 12, 2019 3:45 am @looo doesn't it make sense to follow this advice ?
It's basically the same thing, subprocess.call() internally uses subprocess.Popen(). The latter is more low level, and provides more control over the communication with external programs. The call() function was used in older Python 2 code, so this shows that the code was written a while ago, but it should work the same in Python 3. I don't think it's very urgent to change the functions because the call to the external program is rather simple.

subprocess.Popen
subprocess.call

According to the documentation, for Python 3.5 and above, the recommendation is subprocess.run. Using this would completely break Python 2. Although we don't need to support Python 2, I think the best is to use Popen, because then it's available for every Python 3 version, not only for Python 3.5 and above.
Always add the important information to your posts if you need help. Also see Tutorials and Video tutorials.
To support the documentation effort, and code development, your donation is appreciated: liberapay.com/FreeCAD.
Post Reply