def test_draw_scaled_entities(msp, ctx):
    msp.add_point((1, 1))
    msp.add_point((2, 3))
    out = cast(PathBackend, BackendScaler(PathBackend(), factor=2))
    frontend = Frontend(ctx, out)
    frontend.draw_entities(msp)
    result = out.collector
    assert len(result) == 2
    assert result[0][1] == (2, 2)
    assert result[1][1] == (4, 6)
Beispiel #2
0
    def convert_dxf2img(self,
                        names,
                        img_format=default_img_format,
                        img_res=default_img_res,
                        clr=default_bg_color):
        for name in names:
            doc = ezdxf.readfile(name)
            msp = doc.modelspace()
            # Recommended: audit & repair DXF document before rendering
            auditor = doc.audit()
            # The auditor.errors attribute stores severe errors,
            # which *may* raise exceptions when rendering.
            if len(auditor.errors) != 0:
                raise Exception(
                    "This DXF document is damaged and can't be converted! --> ",
                    name)
                name = name = +1
            else:
                fig = plt.figure()
                ax = fig.add_axes([0, 0, 1, 1])
                ctx = RenderContext(doc)
                ctx.set_current_layout(msp)
                ezdxf.addons.drawing.properties.MODEL_SPACE_BG_COLOR = clr
                out = MatplotlibBackend(ax)
                Frontend(ctx, out).draw_layout(msp, finalize=True)

                img_name = re.findall(
                    "(\S+)\.", name
                )  # select the image name that is the same as the dxf file name
                first_param = ''.join(
                    img_name) + img_format  #concatenate list and string
                fig.savefig(first_param, dpi=img_res)
                print(name, " Converted Successfully")
Beispiel #3
0
    def convert_dxf2img(self,
                        names,
                        img_format=default_img_format,
                        img_res=default_img_res):
        for name in names:
            doc = ezdxf.readfile(name)
            msp = doc.modelspace()
            # Recommended: audit & repair DXF document before rendering
            auditor = doc.audit()
            # The auditor.errors attribute stores severe errors,
            # which *may* raise exceptions when rendering.
            if len(auditor.errors) != 0:
                raise exception(
                    "The DXF document is damaged and can't be converted!")
            else:
                fig = plt.figure()
                ax = fig.add_axes([0, 0, 1, 1])
                ctx = RenderContext(doc)
                ctx.set_current_layout(msp)
                ctx.current_layout.set_colors(bg='#FFFFFF')
                out = MatplotlibBackend(ax)
                Frontend(ctx, out).draw_layout(msp, finalize=True)

                img_name = re.findall(
                    "(\S+)\.", name
                )  # select the image name that is the same as the dxf file name
                first_param = ''.join(
                    img_name) + img_format  #concatenate list and string
                fig.savefig(first_param, dpi=img_res)


#if __name__ == '__main__':
#    first =  DXF2IMG()
#    first.convert_dxf2img(['slot_star.dxf'],img_format='.png')
Beispiel #4
0
def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('dxf_file')
    parser.add_argument('output_path')
    parser.add_argument(
        "--scale",
        "-s",
        type=float,
        default=1.0,
        help="text scaling factor",
    )
    args = parser.parse_args()

    doc = ezdxf.readfile(args.dxf_file)
    layout = doc.modelspace()

    fig: plt.Figure = plt.figure()
    ax: plt.Axes = fig.add_axes((0, 0, 1, 1))
    ctx = RenderContext(layout.doc)
    layout_properties = LayoutProperties.from_layout(layout)
    out = FixedSizedTextMatplotlibBackend(ax, text_size_scale=args.scale)
    Frontend(ctx, out).draw_layout(
        layout,
        finalize=True,
        layout_properties=layout_properties,
    )
    fig.savefig(
        args.output_path,
        dpi=300,
        facecolor=ax.get_facecolor(),
        transparent=True,
    )
    plt.close(fig)
Beispiel #5
0
def _main():
    parser = argparse.ArgumentParser(
        description='draw the given CAD file and save it to a file or view it')
    parser.add_argument('cad_file', nargs='?')
    parser.add_argument('--supported_formats', action='store_true')
    parser.add_argument('--layout', default='Model')
    parser.add_argument('--out', required=False)
    parser.add_argument('--dpi', type=int, default=300)
    parser.add_argument('--ltype', default='internal')
    args = parser.parse_args()

    if args.supported_formats:
        fig = plt.figure()
        for extension, description in fig.canvas.get_supported_filetypes().items():
            print(f'{extension}: {description}')
        sys.exit()

    if args.cad_file is None:
        print('no CAD file specified')
        sys.exit(1)

    try:
        doc = ezdxf.readfile(args.cad_file)
    except IOError:
        print(f'Not a DXF file or a generic I/O error.')
        sys.exit(2)
    except ezdxf.DXFError:
        try:
            doc, auditor = recover.readfile(args.cad_file)
        except ezdxf.DXFStructureError:
            print(f'Invalid or corrupted DXF file: {args.cad_file}')
            sys.exit(3)
    else:
        auditor = doc.audit()

    if auditor.has_errors:
        # But is most likely good enough for rendering.
        print(f'Found {len(auditor.errors)} unrecoverable errors.')
    if auditor.has_fixes:
        print(f'Fixed {len(auditor.fixes)} errors.')

    try:
        layout = doc.layouts.get(args.layout)
    except KeyError:
        print(f'Could not find layout "{args.layout}". '
              f'Valid layouts: {[l.name for l in doc.layouts]}')
        sys.exit(4)

    fig: plt.Figure = plt.figure()
    ax: plt.Axes = fig.add_axes([0, 0, 1, 1])
    ctx = RenderContext(doc)
    out = MatplotlibBackend(ax, params={'linetype_renderer': args.ltype})
    Frontend(ctx, out).draw_layout(layout, finalize=True)
    if args.out is not None:
        print(f'saving to "{args.out}"')
        fig.savefig(args.out, dpi=args.dpi)
        plt.close(fig)
    else:
        plt.show()
Beispiel #6
0
 def draw_layout(self, layout_name: str):
     print(f'drawing {layout_name}')
     self._current_layout = layout_name
     self.renderer.clear()
     self.view.clear()
     layout = self.doc.layout(layout_name)
     self._update_render_context(layout)
     Frontend(self._render_context, self.renderer).draw_layout(layout)
     self.view.fit_to_scene()
Beispiel #7
0
def _get_text_visible_when(doc: Drawing, active_layers: Set[str]) -> List[str]:
    ctx = RenderContext(doc)
    # set given layer to ON, others to OFF
    ctx.set_layers_state(active_layers, state=True)

    backend = BasicBackend()
    Frontend(ctx, backend).draw_layout(doc.modelspace())
    visible_text = [x[1] for x in backend.collector if x[0] == 'text']
    return visible_text
def main(rows: int, cols: int):
    doc = ezdxf.new()
    msp = doc.modelspace()
    create_content(msp)

    # Detecting the drawing extents by ezdxf:
    # The bounding box cache can be reused for entity filtering.
    # This cache is a lightweight object, which is compatible to the pickle
    # module, DXF entities are referenced by handle strings. (multiprocessing!)
    cache = bbox.Cache()

    # The bounding box calculation can take along time for big DXF files!
    # If flatten=0 the bounding box calculation for curves (SPLINE, ELLIPSE, ...)
    # is based on the control points of the Path class, this is less precise but
    # can speed up the calculation and for this task is a precise bounding box
    # not required.
    # This has no impact on this example which uses only straight lines
    extents = bbox.extents(msp, cache=cache, flatten=0)

    ctx = RenderContext(doc)
    for tile, render_area in enumerate(render_areas(extents, (rows, cols))):
        # Setup drawing add-on:
        fig = plt.figure(dpi=DPI)
        ax = fig.add_axes([0, 0, 1, 1])
        out = MatplotlibBackend(ax)

        ax.set_xlim(render_area.extmin.x, render_area.extmax.x)
        ax.set_ylim(render_area.extmin.y, render_area.extmax.y)

        # Disable rendering of entities outside of the render area:
        def is_intersecting_render_area(entity):
            """Returns True if entity should be rendered. """
            entity_bbox = bbox.extents([entity], cache=cache, flatten=0)
            return render_area.has_intersection(entity_bbox)

        # Finalizing invokes auto-scaling!
        Frontend(ctx, out).draw_layout(msp,
                                       finalize=False,
                                       filter_func=is_intersecting_render_area)

        # Set output size in inches:
        # width = 6 inch x 300 dpi = 1800 px
        # height = 3 inch x 300 dpi = 900 px
        fig.set_size_inches(6, 3, forward=True)

        filename = f"tile-{tile:02d}.png"
        print(f'saving tile #{tile} to "{filename}"')
        fig.savefig(DIR / filename, dpi=DPI)
        plt.close(fig)
Beispiel #9
0
 def draw_layout(self, layout_name: str):
     print(f'drawing {layout_name}')
     self._current_layout = layout_name
     self.renderer.clear()
     self.view.clear()
     layout = self.doc.layout(layout_name)
     self._update_render_context(layout)
     try:
         Frontend(self._render_context, self.renderer).draw_layout(layout)
     except DXFStructureError as e:
         qw.QMessageBox.critical(
             self, 'DXF Structure Error',
             f'Abort rendering of layout "{layout_name}": {str(e)}')
     finally:
         self.renderer.finalize()
     self.view.fit_to_scene()
Beispiel #10
0
 def draw_layout(self, layout_name: str, reset_view: bool = True):
     print(f'drawing {layout_name}')
     self._current_layout = layout_name
     self.view.begin_loading()
     new_scene = qw.QGraphicsScene()
     renderer = PyQtBackend(new_scene)
     layout = self.doc.layout(layout_name)
     self._update_render_context(layout)
     try:
         Frontend(self._render_context, renderer).draw_layout(layout)
     except DXFStructureError as e:
         qw.QMessageBox.critical(
             self, 'DXF Structure Error',
             f'Abort rendering of layout "{layout_name}": {str(e)}')
     finally:
         renderer.finalize()
     self.view.end_loading(new_scene)
     self.view.buffer_scene_rect()
     if reset_view:
         self.view.fit_to_scene()
Beispiel #11
0
 def draw_layout(self, layout_name: str, reset_view: bool = True):
     print(f'drawing {layout_name}')
     self._current_layout = layout_name
     self.view.begin_loading()
     new_scene = qw.QGraphicsScene()
     self._backend.set_scene(new_scene)
     layout = self.doc.layout(layout_name)
     self._update_render_context(layout)
     try:
         start = time.perf_counter()
         Frontend(self._render_context, self._backend).draw_layout(layout)
         duration = time.perf_counter() - start
         print(f'took {duration:.4f} seconds')
     except DXFStructureError as e:
         qw.QMessageBox.critical(
             self, 'DXF Structure Error',
             f'Abort rendering of layout "{layout_name}": {str(e)}')
     finally:
         self._backend.finalize()
     self.view.end_loading(new_scene)
     self.view.buffer_scene_rect()
     if reset_view:
         self.view.fit_to_scene()
Beispiel #12
0
    def run(args):
        # Import on demand for a quicker startup:
        try:
            import matplotlib.pyplot as plt
        except ImportError:
            print('Matplotlib package not found.')
            sys.exit(2)

        from ezdxf.addons.drawing import RenderContext, Frontend
        from ezdxf.addons.drawing.matplotlib import MatplotlibBackend

        # Print all supported export formats:
        if args.formats:
            fig = plt.figure()
            for extension, description in fig.canvas.get_supported_filetypes(
            ).items():
                print(f'{extension}: {description}')
            sys.exit(0)

        if args.file:
            filename = args.file
        else:
            print('argument FILE is required')
            sys.exit(1)

        doc, _ = load_document(filename)
        fig = plt.figure()
        ax = fig.add_axes([0, 0, 1, 1])
        ctx = RenderContext(doc)
        out = MatplotlibBackend(ax, params={'linetype_renderer': args.ltype})
        Frontend(ctx, out).draw_layout(doc.modelspace(), finalize=True)
        if args.out is not None:
            print(f'exporting to "{args.out}"')
            fig.savefig(args.out, dpi=args.dpi)
            plt.close(fig)
        else:
            plt.show()
Beispiel #13
0
def _main():
    parser = argparse.ArgumentParser(description='draw the given CAD file and save it to a file or view it')
    parser.add_argument('cad_file', nargs='?')
    parser.add_argument('--supported_formats', action='store_true')
    parser.add_argument('--layout', default='Model')
    parser.add_argument('--out', required=False)
    parser.add_argument('--dpi', type=int, default=300)
    args = parser.parse_args()

    if args.supported_formats:
        fig = plt.figure()
        for extension, description in fig.canvas.get_supported_filetypes().items():
            print(f'{extension}: {description}')
        sys.exit()

    if args.cad_file is None:
        print('no CAD file specified')
        sys.exit(1)

    doc = ezdxf.readfile(args.cad_file)
    try:
        layout = doc.layouts.get(args.layout)
    except KeyError:
        print(f'could not find layout "{args.layout}". Valid layouts: {[l.name for l in doc.layouts]}')
        sys.exit(1)

    fig: plt.Figure = plt.figure()
    ax: plt.Axes = fig.add_axes([0, 0, 1, 1])
    ctx = RenderContext(doc)
    out = MatplotlibBackend(ax)
    Frontend(ctx, out).draw_layout(layout, finalize=True)
    if args.out is not None:
        print(f'saving to "{args.out}"')
        fig.savefig(args.out, dpi=args.dpi)
        plt.close(fig)
    else:
        plt.show()
def path_backend(doc, ctx):
    return Frontend(ctx, PathBackend())
def basic(doc, ctx):
    return Frontend(ctx, BasicBackend())
Beispiel #16
0
 def create_frontend(self):
     return Frontend(
         self._render_context,
         self._backend,
         self._config,
     )
Beispiel #17
0
    def run(args):
        # Import on demand for a quicker startup:
        try:
            import matplotlib.pyplot as plt
        except ImportError:
            print("Matplotlib package not found.")
            sys.exit(2)

        from ezdxf.addons.drawing import RenderContext, Frontend
        from ezdxf.addons.drawing.matplotlib import MatplotlibBackend
        from ezdxf.addons.drawing.config import Configuration, LinePolicy

        # Print all supported export formats:
        if args.formats:
            fig = plt.figure()
            for (
                extension,
                description,
            ) in fig.canvas.get_supported_filetypes().items():
                print(f"{extension}: {description}")
            sys.exit(0)

        if args.file:
            filename = args.file
        else:
            print("argument FILE is required")
            sys.exit(1)

        doc, _ = load_document(filename)

        try:
            layout = doc.layouts.get(args.layout)
        except KeyError:
            print(
                f'Could not find layout "{args.layout}". '
                f"Valid layouts: {[l.name for l in doc.layouts]}"
            )
            sys.exit(1)

        fig = plt.figure()
        ax = fig.add_axes([0, 0, 1, 1])
        ctx = RenderContext(doc)
        out = MatplotlibBackend(ax)

        if args.all_layers_visible:
            for layer_properties in ctx.layers.values():
                layer_properties.is_visible = True

        config = Configuration.defaults()
        config = config.with_changes(
            line_policy=LinePolicy.ACCURATE
            if args.ltype == "accurate"
            else config.line_policy
        )

        if args.all_entities_visible:

            class AllVisibleFrontend(Frontend):
                def override_properties(
                    self, entity: "DXFGraphic", properties: "Properties"
                ) -> None:
                    properties.is_visible = True

            frontend = AllVisibleFrontend(ctx, out, config=config)
        else:
            frontend = Frontend(ctx, out, config=config)
        frontend.draw_layout(layout, finalize=True)

        if args.out is not None:
            print(f'exporting to "{args.out}"')
            fig.savefig(args.out, dpi=args.dpi)
            plt.close(fig)
        else:
            plt.show()
Beispiel #18
0
def extended(doc, ctx):
    return Frontend(ctx, ExtendedBackend())
Beispiel #19
0
# Copyright (c) 2020, Manfred Moitzi
# License: MIT License

import ezdxf
from pathlib import Path
import matplotlib.pyplot as plt
from ezdxf.addons.drawing import Frontend, RenderContext
from ezdxf.addons.drawing.matplotlib_backend import MatplotlibBackend
from ezdxf.math import global_bspline_interpolation

wave = [(0.0, 0.0), (0.897597901, 0.78183148), (1.79519580, 0.97492791),
        (2.69279370, 0.433883739), (3.59039160, -0.43388373),
        (4.48798950, -0.97492791), (5.38558740, -0.78183148),
        (6.28318530, 0.0)]

DIR = Path('~/Desktop/Outbox').expanduser()
FILE = 'wave'
doc = ezdxf.new()
msp = doc.modelspace()
s = global_bspline_interpolation(wave)
msp.add_spline(dxfattribs={'color': 2}).apply_construction_tool(s)

fig = plt.figure()
ax = fig.add_axes([0, 0, 1, 1])
backend = MatplotlibBackend(ax)
Frontend(RenderContext(doc), backend).draw_layout(msp)
fig.savefig(DIR / f'{FILE}.png', dpi=300)
Beispiel #20
0
    def effect(self):
        #get input file and copy it to some new temporary directory
        inputfile = self.options.inputfile
        if not os.path.exists(inputfile):
            inkex.utils.debug(
                "The input file does not exist. Please select a *.dxf or *.dwg file and try again."
            )
            exit(1)
        temp_input_dir = os.path.join(tempfile.gettempdir(), "dxfdwg_input")
        shutil.rmtree(temp_input_dir, ignore_errors=True
                      )  #remove the input directory before doing new job
        if not os.path.exists(temp_input_dir):
            os.mkdir(temp_input_dir)  #recreate blank dir
        shutil.copy2(
            inputfile, os.path.join(
                temp_input_dir,
                Path(inputfile).name))  # complete target filename given

        #Prepapre output conversion
        outputfilebase = os.path.splitext(os.path.basename(inputfile))[0]
        inputfile_ending = os.path.splitext(os.path.basename(inputfile))[1]
        temp_output_dir = os.path.join(tempfile.gettempdir(), "dxfdwg_output")
        shutil.rmtree(temp_output_dir, ignore_errors=True
                      )  #remove the output directory before doing new job
        if not os.path.exists(temp_output_dir):
            os.mkdir(temp_output_dir)

        #Prepare some more options for proceeding
        autocad_version = self.options.oda_outputformat.split("_")[0]
        autocad_format = self.options.oda_outputformat.split("_")[1]
        self.options.oda_audit_repair = "1" if self.options.oda_audit_repair else "0"  #overwrite string bool with int value
        entityspace = []
        if self.options.allentities or self.options.THREE_DFACE:
            entityspace.append("3DFACE")
        if self.options.allentities or self.options.ARC:
            entityspace.append("ARC")
        if self.options.allentities or self.options.BLOCK:
            entityspace.append("BLOCK")
        if self.options.allentities or self.options.CIRCLE:
            entityspace.append("CIRCLE")
        if self.options.allentities or self.options.ELLIPSE:
            entityspace.append("ELLIPSE")
        if self.options.allentities or self.options.LINE:
            entityspace.append("LINE")
        if self.options.allentities or self.options.LWPOLYLINE:
            entityspace.append("LWPOLYLINE")
        if self.options.allentities or self.options.POINT:
            entityspace.append("POINT")
        if self.options.allentities or self.options.POLYLINE:
            entityspace.append("POLYLINE")
        if self.options.allentities or self.options.POP_TRAFO:
            entityspace.append("POP_TRAFO")
        if self.options.allentities or self.options.SEQEND:
            entityspace.append("SEQEND")
        if self.options.allentities or self.options.SOLID:
            entityspace.append("SOLID")
        if self.options.allentities or self.options.SPLINE:
            entityspace.append("SPLINE")
        if self.options.allentities or self.options.TABLE:
            entityspace.append("TABLE")
        if self.options.allentities or self.options.VERTEX:
            entityspace.append("VERTEX")
        if self.options.allentities or self.options.VIEWPORT:
            entityspace.append("VIEWPORT")

        if self.options.allentities or self.options.THREE_DSOLID:
            entityspace.append("3DSOLID")
        if self.options.allentities or self.options.ATTRIB:
            entityspace.append("ATTRIB")
        if self.options.allentities or self.options.BODY:
            entityspace.append("BODY")
        if self.options.allentities or self.options.ARC_DIMENSION:
            entityspace.append("ARC_DIMENSION")
        if self.options.allentities or self.options.HATCH:
            entityspace.append("HATCH")
        if self.options.allentities or self.options.IMAGE:
            entityspace.append("IMAGE")
        if self.options.allentities or self.options.INSERT:
            entityspace.append("INSERT")
        if self.options.allentities or self.options.MESH:
            entityspace.append("MESH")
        if self.options.allentities or self.options.MTEXT:
            entityspace.append("MTEXT")
        if self.options.allentities or self.options.RAY:
            entityspace.append("RAY")
        if self.options.allentities or self.options.REGION:
            entityspace.append("REGION")
        if self.options.allentities or self.options.SHAPE:
            entityspace.append("SHAPE")
        if self.options.allentities or self.options.SURFACE:
            entityspace.append("SURFACE")
        if self.options.allentities or self.options.TRACE:
            entityspace.append("TRACE")
        if self.options.allentities or self.options.UNDERLAY:
            entityspace.append("UNDERLAY")
        if self.options.allentities or self.options.XLINE:
            entityspace.append("XLINE")

        #ODA to ezdxf mapping
        oda_ezdxf_mapping = []
        oda_ezdxf_mapping.append(
            ["ACAD9", "R12", "AC1004"]
        )  #this mapping is not supported directly. so we use the lowest possible which is R12
        oda_ezdxf_mapping.append(
            ["ACAD10", "R12", "AC1006"]
        )  #this mapping is not supported directly. so we use the lowest possible which is R12
        oda_ezdxf_mapping.append(["ACAD12", "R12", "AC1009"])
        oda_ezdxf_mapping.append(
            ["ACAD13", "R2000", "AC1012"]
        )  #R13 was overwritten by R2000 which points to AC1015 instead of AC1014 (see documentation)
        oda_ezdxf_mapping.append(
            ["ACAD14", "R2000", "AC1014"]
        )  #R14 was overwritten by R2000 which points to AC1015 instead of AC1014 (see documentation)
        oda_ezdxf_mapping.append(["ACAD2000", "R2000", "AC1015"])
        oda_ezdxf_mapping.append(["ACAD2004", "R2004", "AC1018"])
        oda_ezdxf_mapping.append(["ACAD2007", "R2007", "AC1021"])
        oda_ezdxf_mapping.append(["ACAD2010", "R2010", "AC1024"])
        oda_ezdxf_mapping.append(["ACAD2013", "R2013", "AC1027"])
        oda_ezdxf_mapping.append(["ACAD2018", "R2018", "AC1032"])

        ezdxf_autocad_format = None
        for oe in oda_ezdxf_mapping:
            if oe[0] == autocad_version:
                ezdxf_autocad_format = oe[1]
                break
        if ezdxf_autocad_format is None:
            inkex.errormsg("ezdxf conversion format version unknown")

        #Prepare DXF and SVG paths
        dxf_file = os.path.join(temp_output_dir, outputfilebase + ".dxf")
        svg_file = os.path.join(temp_output_dir, outputfilebase + ".svg")

        # Run ODA File Converter
        if self.options.oda_skip_dxf_to_dxf == False or inputfile_ending == ".dwg":
            # Executable test (check for proper configuration by checking mime type. Should return octet stream for a binary executable)
            if os.name == "nt" and "application/octet-stream" not in str(
                    MimeTypes().guess_type(
                        urllib.pathname2url(self.options.oda_fileconverter))):
                inkex.utils.debug(
                    "You selected to use ODA File Converter but it is not configured properly. Check for installation and path location or select 'Skip conversion from DXF to DXF'. You can download ODA Converter from 'https://www.opendesign.com/guestfiles/oda_file_converter'. You need to install it in order to use it."
                )
                exit(1)
            elif os.path.isfile(self.options.oda_fileconverter) == False:
                inkex.utils.debug(
                    "You selected to use ODA File Converter but it is not configured properly. Check for installation and path location or select 'Skip conversion from DXF to DXF'. You can download ODA Converter from 'https://www.opendesign.com/guestfiles/oda_file_converter'. You need to install it in order to use it."
                )
                exit(1)
            else:
                # Build and run ODA File Converter command
                oda_cmd = [
                    self.options.oda_fileconverter, temp_input_dir,
                    temp_output_dir, autocad_version, autocad_format, "0",
                    self.options.oda_audit_repair
                ]
                if os.name == 'nt' and self.options.oda_hidewindow:
                    info = subprocess.STARTUPINFO(
                    )  #hide the ODA File Converter window because it is annoying (does not work for Linux :-()
                    info.dwFlags = 1
                    info.wShowWindow = 0
                    proc = subprocess.Popen(oda_cmd,
                                            startupinfo=info,
                                            shell=False,
                                            stdout=PIPE,
                                            stderr=PIPE)
                else:
                    proc = subprocess.Popen(oda_cmd,
                                            shell=False,
                                            stdout=PIPE,
                                            stderr=PIPE)
                stdout, stderr = proc.communicate()
                if proc.returncode != 0 or (len(stderr) > 0 and
                                            stderr != b"Quit (core dumped)\n"):
                    inkex.utils.debug("ODAFileConverter failed: %d %s %s" %
                                      (proc.returncode, stdout, stderr))
                    if os.name != 'nt':
                        inkex.utils.debug(
                            "If the error message above contains a warning about wrong/missing Qt version please install the required version. You can get the installer from 'https://download.qt.io/archive/qt/'. Sadly you will need to create a free account to install. After installation please configure the shell script '/usr/bin/ODAFileConverter' to add a preceding line with content similar to 'LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/Qt5.14.2/5.14.2/gcc_64/lib/'."
                        )
                    exit(1)
            # check if ODA converted successfully. This is the case if no error file was created
            oda_errorfile = os.path.join(temp_output_dir,
                                         Path(inputfile).name + ".err")
            if os.path.exists(oda_errorfile):
                inkex.utils.debug(
                    "ODA File Converter failed to process the file. Cannot continue DXF/DWG import. The error message is:"
                )
                errormessage = open(oda_errorfile, 'r')
                errorlines = errormessage.readlines()
                for errorline in errorlines:
                    inkex.utils.debug(errorline.strip())
                errormessage.close()
                exit(1)

        # Do some movings/copies of skipped or processed DXF
        if self.options.oda_skip_dxf_to_dxf:  #if true we need to move the file to simulate "processed"
            shutil.move(os.path.join(temp_input_dir,
                                     Path(inputfile).name),
                        os.path.join(temp_output_dir,
                                     Path(inputfile).name))

        if self.options.oda_keepconverted_dxf:
            shutil.copy2(
                dxf_file,
                os.path.join(os.path.dirname(inputfile), outputfilebase +
                             "_oda.dxf"))  # complete target filename given

        # Preprocessing DXF to DXF (entity filter) by using ezdxf the first time
        if self.options.ezdxf_preprocessing:
            # uniconverter does not handle all entities. we parse the file to exlude stuff which lets uniconverter fail
            dxf = ezdxf.readfile(dxf_file)
            modelspace = dxf.modelspace()
            allowed_entities = []
            # supported entities by UniConverter- impossible: MTEXT TEXT INSERT and a lot of others
            query_string = str(entityspace)[1:-1].replace("'",
                                                          "").replace(",", "")
            if query_string != "":
                for e in modelspace.query(query_string):
                    allowed_entities.append(e)
            #inkex.utils.debug(ezdxf_autocad_format)
            #inkex.utils.debug(self.options.ezdxf_output_version)
            if self.options.ezdxf_output_version == "SAME":
                doc = ezdxf.new(ezdxf_autocad_format)
            else:
                doc = ezdxf.new(
                    self.options.ezdxf_output_version
                )  #use the string values from inx file. Required to match the values from ezdxf library. See Python reference
            msp = doc.modelspace()
            for e in allowed_entities:
                msp.add_foreign_entity(e)
            doc.saveas(dxf_file)
            if self.options.ezdfx_keep_preprocessed:
                shutil.copy2(
                    dxf_file,
                    os.path.join(
                        os.path.dirname(inputfile), outputfilebase +
                        "_ezdxf.dxf"))  # complete target filename given

        # Make SVG from DXF
        if self.options.dxf_to_svg_parser == "sk1":
            if os.name != "nt":
                inkex.utils.debug(
                    "You selected sk1 UniConvertor but you are not running on a Windows platform. On Linux uniconverter 1.1.X can be installed using the now obsolete Python 2.7, but it will not run correctly because you finally will fail at installing liblcms1-dev library on newer systems. That leads to uncompilable sk1libs package. Unfortunately sk1 UniConvertor 2.X does not support dxf format. So please use another DXF to SVG converter."
                )
                exit(1)
            sk1_command_ending = os.path.splitext(
                os.path.splitext(
                    os.path.basename(self.options.sk1_uniconverter))[1])[0]
            if sk1_command_ending != ".cmd":
                inkex.utils.debug(
                    "You selected sk1 UniConverter but it was not configured properly. Check the path to the executable."
                )
                exit(1)
            uniconverter_cmd = [
                self.options.sk1_uniconverter, dxf_file, svg_file
            ]
            #inkex.utils.debug(uniconverter_cmd)
            proc = subprocess.Popen(uniconverter_cmd,
                                    shell=False,
                                    stdout=PIPE,
                                    stderr=PIPE)
            stdout, stderr = proc.communicate()
            if proc.returncode != 0:
                inkex.errormsg("UniConverter failed: %d %s %s" %
                               (proc.returncode, stdout, stderr))
                if self.options.opendironerror:
                    self.openExplorer(temp_output_dir)

        elif self.options.dxf_to_svg_parser == "bjnortier":
            if which("node") is None:
                inkex.utils.debug(
                    "NodeJS executable not found on path. Please check your installation."
                )
                exit(1)
            else:
                bjnortier_cmd = [
                    "node",
                    os.path.join("node_modules", "dxf", "lib", "cli.js"),
                    dxf_file, svg_file
                ]
                #inkex.utils.debug(bjnortier_cmd)
                proc = subprocess.Popen(bjnortier_cmd,
                                        shell=False,
                                        stdout=PIPE,
                                        stderr=PIPE)
                stdout, stderr = proc.communicate()
                if proc.returncode != 0:
                    inkex.errormsg(
                        "node.js DXF to SVG conversion failed: %d %s %s" %
                        (proc.returncode, stdout, stderr))
                    if self.options.opendironerror:
                        self.openExplorer(temp_output_dir)

        elif self.options.dxf_to_svg_parser == "kabeja":
            wd = os.path.join(os.getcwd(), "kabeja")
            #inkex.utils.debug(wd)
            proc = subprocess.Popen(
                "java -jar launcher.jar -nogui -pipeline svg " + dxf_file +
                " " + svg_file,
                cwd=wd,
                shell=True,
                stdout=PIPE,
                stderr=PIPE)
            stdout, stderr = proc.communicate()
            if proc.returncode != 0:
                inkex.errormsg("kabeja failed: %d %s %s" %
                               (proc.returncode, stdout, stderr))
                if self.options.opendironerror:
                    self.openExplorer(temp_output_dir)

        elif self.options.dxf_to_svg_parser == "vpype_dxf":
            try:
                from inkex.command import inkscape
                import vpype
                from vpype_cli import execute
            except Exception as e:
                inkex.errormsg(
                    "Error importing vpype. Did you properly install the vpype and vpype-dxf python modules?"
                )
                exit(1)
            doc = vpype.Document()  #create new vpype document
            command = "dread  --quantization " + str(
                self.options.vpype_quantization)
            if self.options.vpype_simplify is True:
                command += " --simplify"
            if self.options.vpype_parallel is True:
                command += " --parallel"
            #command += " '" + inputfile + "'"
            command += " '" + dxf_file + "'"

            #inkex.errormsg(command)
            doc = execute(command, doc)
            if doc.length() == 0:
                inkex.errormsg(
                    'No lines left after vpype conversion. Conversion result is empty. Cannot continue'
                )
                exit(1)
            # save the vpype document to new svg file and close it afterwards
            output_fileIO = open(svg_file, "w", encoding="utf-8")
            vpype.write_svg(output_fileIO,
                            doc,
                            page_size=None,
                            center=False,
                            source_string='',
                            layer_label_format='%d',
                            show_pen_up=False,
                            color_mode='layer')
            output_fileIO.close()
            # convert vpype polylines/lines/polygons to regular paths again. We need to use "--with-gui" to respond to "WARNING: ignoring verb FileSave - GUI required for this verb."
            cli_output = inkscape(
                svg_file,
                "--with-gui",
                actions=
                "EditSelectAllInAllLayers;EditUnlinkClone;ObjectToPath;FileSave;FileQuit"
            )
            if len(cli_output) > 0:
                self.debug(
                    _("Inkscape returned the following output when trying to run the vpype object to path back-conversion:"
                      ))
                self.debug(cli_output)

        elif self.options.dxf_to_svg_parser == "ezdxf":
            try:
                doc = ezdxf.readfile(dxf_file)
                msp = doc.modelspace()
                #for e in msp: #loop through entities
                #    inkex.errormsg(e)
                #doc.header['$DIMSCALE'] = 0.2 does not apply to the plot :-(
                #inkex.utils.debug(doc.header['$DIMSCALE'])
                #inkex.utils.debug(doc.header['$MEASUREMENT'])
                auditor = doc.audit(
                )  #audit & repair DXF document before rendering
                # The auditor.errors attribute stores severe errors, which *may* raise exceptions when rendering.
                if len(auditor.errors) == 0:
                    fig = plt.figure()
                    ax = plt.axes([0., 0., 1., 1.], xticks=[], yticks=[])
                    #ax = plt.axes([0., 0., 1., 1.], frameon=False, xticks=[], yticks=[])
                    ax.patches = []
                    #plt.axis('off')
                    plt.margins(0, 0)
                    plt.gca().xaxis.set_major_locator(plt.NullLocator())
                    plt.gca().yaxis.set_major_locator(plt.NullLocator())
                    plt.subplots_adjust(top=1,
                                        bottom=0,
                                        right=1,
                                        left=0,
                                        hspace=0,
                                        wspace=0)
                    out = MatplotlibBackend(fig.add_axes(ax))
                    Frontend(RenderContext(doc),
                             out).draw_layout(msp, finalize=True)
                    #plt.show()
                    #fig.savefig(os.path.join(temp_output_dir, outputfilebase + ".png"), dpi=300)
                    fig.savefig(
                        svg_file
                    )  #see https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.savefig.html
            except IOError:
                inkex.errormsg("Not a DXF file or a generic I/O error.")
                exit(1)
            except ezdxf.DXFStructureError:
                inkex.errormsg("Invalid or corrupted DXF file.")
                exit(1)

        elif self.options.dxf_to_svg_parser == "legacy":
            inkex.utils.debug(
                "The selected legacy DXF to SVG parser is not supported by this extension yet. Use File > Import > *.dxf. This calls the \"dxf_input.inx\" extension."
            )
            exit(1)
        else:
            inkex.utils.debug("undefined parser")
            exit(1)

        # Write the generated SVG into InkScape's canvas
        try:
            stream = open(svg_file, 'r')
        except FileNotFoundError as e:
            inkex.utils.debug(
                "There was no SVG output generated. Cannot continue")
            exit(1)
        p = etree.XMLParser(huge_tree=True)
        doc = etree.parse(stream,
                          parser=etree.XMLParser(huge_tree=True)).getroot()
        stream.close()
        doc.set(
            'id',
            self.svg.get_unique_id("dxf_dwg_import-" +
                                   self.options.dxf_to_svg_parser + "-"))
        self.document.getroot().append(doc)

        #get children of the doc and move them one group above - we don't do this for bjnortier tool because this has different structure which we don't want to disturb
        if self.options.dxf_to_svg_parser == "sk1":
            elements = []
            emptyGroup = None
            for firstGroup in doc.getchildren():
                emptyGroup = firstGroup
                for element in firstGroup.getchildren():
                    elements.append(element)
                #break #only one cycle - could be bad idea or not
            for element in elements:
                doc.set('id', self.svg.get_unique_id('dxf_dwg_import'))
                doc.insert(doc.index(firstGroup), element)

            if emptyGroup is not None:
                emptyGroup.getparent().remove(emptyGroup)

        #empty the following vals because they destroy the size aspects of the import / make viewbox looking wrong
        if self.options.dxf_to_svg_parser == "bjnortier" or self.options.dxf_to_svg_parser == "kabeja":
            doc.set('width', '')
            doc.set('height', '')
            doc.set('viewBox', '')
            doc.getchildren()[0].set('transform', '')

        #adjust viewport and width/height to have the import at the center of the canvas
        if self.options.resizetoimport:
            bbox = inkex.elements._selected.ElementList.bounding_box(
                doc.getchildren()[0])
            if bbox is not None:
                root = self.svg.getElement('//svg:svg')
                offset = self.svg.unittouu(
                    str(self.options.extraborder) +
                    self.options.extraborder_units)
                root.set(
                    'viewBox', '%f %f %f %f' %
                    (bbox.left - offset, bbox.top - offset,
                     bbox.width + 2 * offset, bbox.height + 2 * offset))
                root.set('width', bbox.width + 2 * offset)
                root.set('height', bbox.height + 2 * offset)
Beispiel #21
0
def _main():
    parser = argparse.ArgumentParser(
        description="draw the given CAD file and save it to a file or view it"
    )
    parser.add_argument("cad_file", nargs="?")
    parser.add_argument("--supported_formats", action="store_true")
    parser.add_argument("--layout", default="Model")
    parser.add_argument("--out", required=False)
    parser.add_argument("--dpi", type=int, default=300)
    parser.add_argument("--ltype", default="internal")
    args = parser.parse_args()

    if args.supported_formats:
        fig = plt.figure()
        for (
            extension,
            description,
        ) in fig.canvas.get_supported_filetypes().items():
            print(f"{extension}: {description}")
        sys.exit()

    if args.cad_file is None:
        print("no CAD file specified")
        sys.exit(1)

    try:
        doc = ezdxf.readfile(args.cad_file)
    except IOError:
        print(f"Not a DXF file or a generic I/O error.")
        sys.exit(2)
    except ezdxf.DXFError:
        try:
            doc, auditor = recover.readfile(args.cad_file)
        except ezdxf.DXFStructureError:
            print(f"Invalid or corrupted DXF file: {args.cad_file}")
            sys.exit(3)
    else:
        auditor = doc.audit()

    if auditor.has_errors:
        # But is most likely good enough for rendering.
        print(f"Found {len(auditor.errors)} unrecoverable errors.")
    if auditor.has_fixes:
        print(f"Fixed {len(auditor.fixes)} errors.")

    try:
        layout = doc.layouts.get(args.layout)
    except KeyError:
        print(
            f'Could not find layout "{args.layout}". '
            f"Valid layouts: {[l.name for l in doc.layouts]}"
        )
        sys.exit(4)

    # setup drawing add-on configuration
    config = Configuration.defaults()
    config = config.with_changes(
        line_policy=LinePolicy.ACCURATE
        if args.ltype == "ezdxf"
        else config.line_policy
    )

    fig: plt.Figure = plt.figure(dpi=args.dpi)
    ax: plt.Axes = fig.add_axes([0, 0, 1, 1])
    ctx = RenderContext(doc)
    out = MatplotlibBackend(ax)
    Frontend(ctx, out, config=config).draw_layout(layout, finalize=True)
    if args.out is not None:
        print(f'saving to "{args.out}"')
        fig.savefig(args.out, dpi=args.dpi)
        plt.close(fig)
    else:
        plt.show()