Beispiel #1
0
    def generate(self):
        tex_file = os.path.join(self.tempdir, 'input.tex')
        pdf_file = os.path.join(self.tempdir,
                                'input.pdf')  # Auto-generate by pdflatex
        svg_file = os.path.join(self.tempdir, 'output.svg')

        with open(tex_file, 'w') as fhl:
            self.write_latex(fhl)

        call('pdflatex', tex_file,\
            output_directory=self.tempdir,\
            halt_on_error=True, oldie=True)

        inkscape(pdf_file,
                 export_filename=svg_file,
                 pdf_page=1,
                 pdf_poppler=True,
                 export_type="svg")

        if not os.path.isfile(svg_file):
            fn = os.path.basename(svg_file)
            if os.path.isfile(fn):
                # Inkscape bug detected, file got saved wrong
                svg_file = fn

        with open(svg_file, 'r') as fhl:
            svg = load_svg(fhl).getroot()
            svg.set_random_ids(backlinks=True)
            for child in svg:
                if isinstance(child, ShapeElement):
                    yield child
                elif isinstance(child, Defs):
                    for def_child in child:
                        #def_child.set_random_id()
                        self.svg.defs.append(def_child)
    def effect(self):
        """Main entry point to process current document. Not to be called externally."""

        actions_list = self.custom_effect(self)

        if actions_list is None or actions_list == []:
            self.msg(
                "No actions received. Perhaps you are calling inkex object methods?"
            )
        elif isinstance(actions_list, list):
            tempfile = self.options.input_file + "-BaseExtension.svg"

            # prepare
            copy2(self.options.input_file, tempfile)

            actions_list.append("FileSave")
            actions_list.append("FileQuit")

            actions = ";".join(actions_list)
            inkscape(tempfile, "--with-gui", actions=actions)

            # finish up
            # replace current document with content of temp copy file
            self.document = inkex.load_svg(tempfile)
            # update self.svg
            self.svg = self.document.getroot()

        # Clean up tempfile
        try:
            os.remove(tempfile)
        except Exception:  # pylint: disable=broad-except
            pass
    def exportNode(self, node, dpi=None, num='1', size=None):
        skip, kwargs = self.formatExport(node, dpi, num, size)
        if skip:
            raise inkex.AbortExtension("File is available in destination.\nPlease change directory location or make sure overwrite option is checked!")

        svgFile = self.options.input_file
        inkscape(svgFile, **kwargs)
Beispiel #4
0
 def export_slice(self, sli, filename):
     """
     Runs inkscape's command line interface and exports the image
     slice from the 4 coordinates in s, and saves as the filename
     given.
     """
     coords = ":".join([self.get_localised_string(dim) for dim in sli])
     inkscape(self.options.input_file, export_dpi=self.options.dpi, export_area=coords, export_filename=filename)
Beispiel #5
0
 def export_node(self, node, height=None, width=None):
     color, kwargs = self.get_color_and_command_kwargs(node, height, width)
     node_id = node.attrib["id"]
     self.color_map[node_id] = color
     if color == ExportSlices.GREY:  # skipping
         return
     svg_file = self.options.input_file
     inkscape(svg_file, **kwargs)
 def export_image(self, name, size):
     dir = self.options.output_path
     file = "{}.png".format(name)
     output_file = os.path.join(dir, file)
     kwargs = {
         'export-area': '0:0:108:108',
         'export-filename': output_file,
         'export-width': str(size),
         'export-height': str(size)
     }
     inkscape(self.options.input_file, **kwargs)
 def exportImageToPNG(self, assetID, assetLABEL):
     filename = assetLABEL + ".png"
     svg_file = self.options.input_file
     #DOCNAME = self.document.getroot().xpath('@sodipodi:docname', namespaces=inkex.NSS)
     #svg_file = os.path.join(self.DIRNAME, DOCNAME[0])
     if not os.path.exists(os.path.join(self.DIRNAME, "temp")):
         os.mkdir(os.path.join(self.DIRNAME, "temp"))
     filepath = os.path.join(self.DIRNAME, "temp", filename)
     inkscape(self.options.input_file,
              export_id=assetID,
              export_filename=filepath)
Beispiel #8
0
 def run_pathops(self, svgfile, top_path, id_list, ink_verb, dry_run=False):
     """Run path ops with top_path on a list of other object ids."""
     # build list with command line arguments
     actions_list = []
     for node_id in id_list:
         actions_list.append("select-by-id:" + top_path)
         actions_list.append("EditDuplicate")
         actions_list.append("select-by-id:" + node_id)
         actions_list.append(ink_verb)
         actions_list.append("EditDeselect")
     actions_list.append("FileSave")
     actions_list.append("FileQuit")
     actions = ";".join(actions_list)
     # process command list
     if dry_run:
         inkex.utils.debug(" ".join([
             "inkscape", "--with-gui", "--actions=" + "\"" + actions + "\"",
             svgfile
         ]))
     else:
         inkscape(svgfile, "--with-gui", actions=actions)
Beispiel #9
0
    def export(self, export_base_name):

        export_file_name = '{0}.{1}'.format(export_base_name,
                                            self.options.format)

        if os.path.exists(self.options.output_folder):
            export_file_path = os.path.join(self.options.output_folder,
                                            export_file_name)
        else:
            inkex.errormsg("The selected output folder does not exist.")
            return False

        if self.options.format == 'svg':
            # would use this, but it cannot overwrite, nor handle strings for writing...:
            # write_svg(self.new_doc, export_file_path)
            with open(export_file_path, 'w') as f:
                f.write(self.new_doc)
        else:

            actions = {
                'png' : 'export-dpi:{dpi};export-filename:{file_name};export-do;FileClose'.\
                        format(dpi=self.options.dpi, file_name=export_file_path),
                'pdf' : 'export-dpi:{dpi};export-pdf-version:1.5;export-text-to-path;export-filename:{file_name};export-do;FileClose'.\
                        format(dpi=self.options.dpi, file_name=export_file_path),
                'ps'  : 'export-dpi:{dpi};export-text-to-path;export-filename:{file_name};export-do;FileClose'.\
                        format(dpi=self.options.dpi, file_name=export_file_path),
                'eps' : 'export-dpi:{dpi};export-text-to-path;export-filename:{file_name};export-do;FileClose'.\
                        format(dpi=self.options.dpi, file_name=export_file_path),
                }

            # create a temporary svg file from our string
            temp_svg_name = '{0}.{1}'.format(export_base_name, 'svg')
            temp_svg_path = os.path.join(self.tempdir, temp_svg_name)
            #inkex.utils.debug("temp_svg_path=" + temp_svg_path)
            with open(temp_svg_path, 'w') as f:
                f.write(self.new_doc)
                #inkex.utils.debug("self.new_doc=" + self.new_doc)
            # let Inkscape do the exporting
            # self.debug(actions[self.options.format])
            cli_output = inkscape(temp_svg_path,
                                  actions=actions[self.options.format])

            if len(cli_output) > 0:
                self.debug(
                    _("Inkscape returned the following output when trying to run the file export; the file export may still have worked:"
                      ))
                self.debug(cli_output)
                return False
        return True
Beispiel #10
0
def generate_png_separations(temp_dir, area_to_export, resolution, alpha):
    if alpha:
        alpha_command = ""
    else:
        alpha_command = ";export-background:white"
    for color in ['C', 'M', 'Y', 'K']:
        cmd = area_to_export + alpha_command + ';export-dpi:' + str(
            resolution
        ) + ';export-background-opacity:1;export-filename:' + os.path.join(
            temp_dir, "separated" + area_to_export.replace(' ', '') + color +
            ".png") + ';export-do'
        #inkex.utils.debug(cmd)
        cli_output = inkscape(os.path.join(temp_dir,
                                           "separation" + color + ".svg"),
                              actions=cmd)
        if len(cli_output) > 0:
            inkex.utils.debug(cli_output)
Beispiel #11
0
    def effect(self):
        # Adjust the document view for the desired sticker size
        root = self.svg.getElement("//svg:svg")

        subline_fontsize = 40  #px; one line of bottom text (id and owner) creates a box of that height

        #our DataMatrix has size 16x16, each cube is sized by 16x16px -> total size is 256x256px. We use 4px padding for all directions
        DataMatrix_xy = 16
        DataMatrix_height = 16 * DataMatrix_xy
        DataMatrix_width = DataMatrix_height
        sticker_padding = 4
        sticker_height = DataMatrix_height + subline_fontsize + 3 * sticker_padding
        sticker_width = 696

        #configure font sizes and box heights to define how large the font size may be at maximum (to omit overflow)
        objectNameMaxHeight = sticker_height - 2 * subline_fontsize - 4 * sticker_padding
        objectNameMaxLines = 5
        objectNameFontSize = objectNameMaxHeight / objectNameMaxLines  #px; generate main font size from lines and box size

        root.set("width", str(sticker_width) + "px")
        root.set("height", str(sticker_height) + "px")
        root.set("viewBox",
                 "%f %f %f %f" % (0, 0, sticker_width, sticker_height))

        #clean the document (make it blank) to avoid printing duplicated things
        ct = 0
        for node in self.document.xpath('//*', namespaces=inkex.NSS):
            ct = ct + 1
            if ct > 3:  #we keep svg:svg, sodipodi:namedview and svg:defs which defines the default canvas without any content inside
                #inkex.errormsg(str(node))
                try:
                    root.remove(node)
                except Exception as e:
                    pass

        #set the document units
        self.document.getroot().find(inkex.addNS("namedview", "sodipodi")).set(
            "inkscape:document-units", "px")

        # Download the recent inventory CSV file and parse line by line to create an inventory sticker
        password_mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
        password_mgr.add_password(None, self.options.server_address,
                                  self.options.htuser, self.options.htpassword)
        handler = urllib.request.HTTPBasicAuthHandler(password_mgr)
        opener = urllib.request.build_opener(handler)

        try:
            inventoryData = opener.open(
                self.options.server_address).read().decode("utf-8")
            urllib.request.install_opener(opener)

            inventoryCSVParent = os.path.join(self.options.export_dir,
                                              "InventorySticker")
            inventoryCSV = os.path.join(inventoryCSVParent, "inventory.csv")

            # To avoid messing with old stickers we remove the directory on Client before doing something new
            shutil.rmtree(inventoryCSVParent, ignore_errors=True
                          )  #remove the output directory before doing new job

            # we are going to write the imported Server CSV file temporarily. Otherwise CSV reader seems to mess with the file if passed directly
            if not os.path.exists(inventoryCSVParent):
                os.mkdir(inventoryCSVParent)
            with open(inventoryCSV, 'w', encoding="utf-8") as f:
                f.write(inventoryData)
                f.close()

            #parse sticker Ids from user input
            if self.options.sticker_ids != "*":
                sticker_ids = self.options.sticker_ids.split(",")
            else:
                sticker_ids = None

            with open(inventoryCSV, 'r', encoding="utf-8") as csv_file:
                csv_reader = csv.reader(csv_file, delimiter=",")
                for row in csv_reader:
                    internal_id = row[0]
                    doc_title = row[1]
                    sticker_id = row[2]
                    level = row[3]
                    zone = row[4]

                    if sticker_ids is None or sticker_id in sticker_ids:
                        #create new sub directories for each non-existent FabLab zone (if flat export is disabled)
                        if self.options.flat_export == False:
                            if not zone:
                                zoneDir = os.path.join(
                                    inventoryCSVParent,
                                    "Keinem Bereich zugeordnet")
                            else:
                                zoneDir = os.path.join(
                                    inventoryCSVParent,
                                    get_valid_filename(zone)
                                )  #remove invalid charaters from zone
                            if not os.path.exists(zoneDir):
                                os.mkdir(zoneDir)
                        else:
                            zoneDir = inventoryCSVParent  #use top directory

                        #Generate the recent sticker content
                        stickerGroup = self.document.getroot().add(
                            inkex.Group(
                                id="InventorySticker_Id" +
                                sticker_id))  #make a new group at root level
                        DataMatrixStyle = inkex.Style({
                            "stroke": "none",
                            "stroke-width": "1",
                            "fill": "#000000"
                        })
                        DataMatrixAttribs = {
                            "style": str(DataMatrixStyle),
                            "height": str(DataMatrix_xy) + "px",
                            "width": str(DataMatrix_xy) + "px"
                        }

                        # 1 - create DataMatrix (create a 2d list corresponding to the 1"s and 0s of the DataMatrix)
                        encoded = self.encode(self.options.target_url + "/" +
                                              sticker_id)
                        DataMatrixGroup = stickerGroup.add(
                            inkex.Group(
                                id="DataMatrix_Id" +
                                sticker_id))  #make a new group at root level
                        for x, y in self.render_data_matrix(
                                encoded, DataMatrix_xy):
                            DataMatrixAttribs.update({
                                "x":
                                str(x + sticker_padding),
                                "y":
                                str(y + sticker_padding)
                            })
                            etree.SubElement(DataMatrixGroup,
                                             inkex.addNS("rect", "svg"),
                                             DataMatrixAttribs)

                        inline_size = sticker_width - DataMatrix_width - 3 * sticker_padding  #remaining width for objects next to the DataMatrix
                        x_pos = DataMatrix_width + 2 * sticker_padding

                        # 2 - Add Object Name Text
                        objectName = etree.SubElement(
                            stickerGroup,
                            inkex.addNS("text", "svg"),
                            {
                                "font-size":
                                str(objectNameFontSize) + "px",
                                "x":
                                str(x_pos) + "px",
                                #"xml:space": "preserve", #we cannot add this here because InkScape throws an error
                                #"y": "4px", #if set it does not correctly apply
                                "text-align":
                                "left",
                                "text-anchor":
                                "left",
                                "vertical-align":
                                "bottom",
                                #style: inline-size required for text wrapping inside box; letter spacing is required to remove the additional whitespaces. The letter spacing depends to the selected font family (Miso)
                                "style":
                                str(
                                    inkex.Style(
                                        {
                                            "fill": "#000000",
                                            "inline-size":
                                            str(inline_size) + "px",
                                            "stroke": "none",
                                            "font-family": "Miso",
                                            "font-weight": "bold",
                                            "letter-spacing": "-3.66px"
                                        }))
                            })
                        objectName.set("id", "objectName_Id" + sticker_id)
                        objectName.set(
                            "xml:space", "preserve"
                        )  #so we add it here instead .. if multiple whitespaces in text are coming after each other just render them (preserve!)
                        objectNameTextSpan = etree.SubElement(
                            objectName, inkex.addNS("tspan", "svg"), {})
                        objectNameTextSpan.text = splitAt(
                            doc_title, 1
                        )  #add 1 whitespace after each chacter. So we can simulate a in-word line break (break by char instead by word)

                        # 3 - Add Object Id Text - use the same position but revert text anchors/align
                        objectId = etree.SubElement(
                            stickerGroup,
                            inkex.addNS("text", "svg"),
                            {
                                "font-size":
                                str(subline_fontsize) + "px",
                                "x":
                                str(sticker_padding) + "px",
                                "transform":
                                "translate(0," +
                                str(sticker_height - subline_fontsize) + ")",
                                "text-align":
                                "left",
                                "text-anchor":
                                "left",
                                "vertical-align":
                                "bottom",
                                "style":
                                str(
                                    inkex.Style(
                                        {
                                            "fill": "#000000",
                                            "inline-size":
                                            str(inline_size) + "px",
                                            "stroke": "none",
                                            "font-family": "Miso",
                                            "font-weight": "bold"
                                        })
                                )  #inline-size required for text wrapping
                            })
                        objectId.set("id", "objectId_Id" + sticker_id)
                        objectIdTextSpan = etree.SubElement(
                            objectId, inkex.addNS("tspan", "svg"), {})
                        objectIdTextSpan.text = "Thing #" + sticker_id

                        # 4 - Add Owner Text
                        owner = etree.SubElement(
                            stickerGroup,
                            inkex.addNS("text", "svg"),
                            {
                                "font-size":
                                str(subline_fontsize) + "px",
                                "x":
                                str(x_pos) + "px",
                                "transform":
                                "translate(0," +
                                str(sticker_height - subline_fontsize) + ")",
                                "text-align":
                                "right",
                                "text-anchor":
                                "right",
                                "vertical-align":
                                "bottom",
                                "style":
                                str(
                                    inkex.Style(
                                        {
                                            "fill": "#000000",
                                            "inline-size":
                                            str(inline_size) + "px",
                                            "stroke": "none",
                                            "font-family": "Miso",
                                            "font-weight": "300"
                                        })
                                )  #inline-size required for text wrapping
                            })
                        owner.set("id", "owner_Id" + sticker_id)
                        ownerTextSpan = etree.SubElement(
                            owner, inkex.addNS("tspan", "svg"), {})
                        ownerTextSpan.text = self.options.target_owner

                        # 5 - Add Level Text
                        levelText = etree.SubElement(
                            stickerGroup,
                            inkex.addNS("text", "svg"),
                            {
                                "font-size":
                                str(subline_fontsize) + "px",
                                "x":
                                str(x_pos) + "px",
                                "transform":
                                "translate(0," +
                                str(sticker_height - subline_fontsize -
                                    subline_fontsize) + ")",
                                "text-align":
                                "right",
                                "text-anchor":
                                "right",
                                "vertical-align":
                                "bottom",
                                "style":
                                str(
                                    inkex.Style(
                                        {
                                            "fill": "#000000",
                                            "inline-size":
                                            str(inline_size) + "px",
                                            "stroke": "none",
                                            "font-family": "Miso",
                                            "font-weight": "bold"
                                        })
                                )  #inline-size required for text wrapping
                            })
                        levelText.set("id", "level_Id" + sticker_id)
                        levelTextTextSpan = etree.SubElement(
                            levelText, inkex.addNS("tspan", "svg"), {})
                        levelTextTextSpan.text = level

                        # 6 - Add horizontal divider line
                        line_thickness = 2  #px
                        line_x_pos = 350  #px; start of the line (left coord)
                        line_length = sticker_width - line_x_pos
                        divider = etree.SubElement(
                            stickerGroup,
                            inkex.addNS("path", "svg"),
                            {
                                "d":
                                "m " + str(line_x_pos) + "," +
                                str(sticker_height - subline_fontsize -
                                    subline_fontsize) + " h " +
                                str(line_length),
                                "style":
                                str(
                                    inkex.Style(
                                        {
                                            "fill": "none",
                                            "stroke": "#000000",
                                            "stroke-width":
                                            str(line_thickness) + "px",
                                            "stroke-linecap": "butt",
                                            "stroke-linejoin": "miter",
                                            "stroke-opacity": "1"
                                        })
                                )  #inline-size required for text wrapping
                            })
                        divider.set("id", "divider_Id" + sticker_id)

                        if self.options.preview == False:
                            export_file_name = sticker_id + "_" + get_valid_filename(
                                doc_title)
                            export_file_path = os.path.join(
                                zoneDir, export_file_name)

                            #"Export" as SVG by just copying the recent SVG document to the target directory. We need to remove special characters to have valid file names on Windows/Linux
                            export_file_svg = open(export_file_path + ".svg",
                                                   "w",
                                                   encoding="utf-8")
                            export_file_svg.write(
                                str(etree.tostring(self.document), "utf-8"))
                            export_file_svg.close()

                            if self.options.export_png == False and self.options.export_svg == False:
                                inkex.errormsg(
                                    "Nothing to export. Generating preview only ..."
                                )
                                break

                            if self.options.export_png == True:  #we need to generate SVG before to get PNG. But if user selected PNG only we need to remove SVG afterwards
                                #Make PNG from SVG (slow because each file is picked up separately. Takes about 10 minutes for 600 files
                                inkscape(
                                    export_file_path + ".svg",
                                    actions=
                                    "export-dpi:96;export-background:white;export-filename:{file_name};export-do;FileClose"
                                    .format(file_name=export_file_path +
                                            ".png"))

                            #fix for "usb.core.USBError: [Errno 13] Access denied (insufficient permissions)"
                            #echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="04f9", ATTR{idProduct}=="2044", MODE="666"' > /etc/udev/rules.d/99-garmin.rules && sudo udevadm trigger
                            if self.options.print_png > 0:
                                if self.options.export_png == False:
                                    inkex.errormsg(
                                        "No file output for printing. Please set 'Export PNG' to true first."
                                    )
                                else:
                                    for x in range(self.options.print_png):
                                        command = "brother_ql -m QL-720NW --backend pyusb --printer usb://" + self.options.print_device + " print -l 62 --600dpi -r auto " + export_file_path + ".png"
                                        p = Popen(command,
                                                  shell=True,
                                                  stdout=PIPE,
                                                  stderr=PIPE
                                                  )  #forr Windows: shell=False
                                        stdout, stderr = p.communicate()
                                        p.wait()
                                        if p.returncode != 0:
                                            inkex.errormsg(
                                                "brother_ql returned: %d %s %s"
                                                %
                                                (p.returncode, stdout, stderr))

                            if self.options.export_svg != True:  #If user selected PNG only we need to remove SVG again
                                os.remove(export_file_path + ".svg")

                            self.document.getroot().remove(
                                stickerGroup)  #remove the stickerGroup again
                        else:  #create preview by just breaking the for loop without executing remove(stickerGroup)
                            break
                csv_file.close()
        except Exception as e:
            inkex.errormsg(e)
Beispiel #12
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 #13
0
    def effect(self):
        lc = vpype.LineCollection() # create a new array of LineStrings consisting of Points. We convert selected paths to polylines and grab their points
        elementsToWork = [] # we make an array of all collected nodes to get the boundingbox of that array. We need it to place the vpype converted stuff to the correct XY coordinates
          
        applyTransformAvailable = False
        
        # at first we apply external extension
        try:
            sys.path.append("..") # add parent directory to path to allow importing applytransform (vpype extension is encapsulated in sub directory)
            import applytransform
            applyTransformAvailable = True
        except Exception as e:
            # inkex.utils.debug(e)
            inkex.utils.debug("Calling 'Apply Transformations' extension failed. Maybe the extension is not installed. You can download it from official InkScape Gallery. Skipping this step")

        def flatten(node):
            path = node.path.to_superpath()
            bezier.cspsubdiv(path, self.options.flatness)
            newpath = []
            for subpath in path:
                first = True
                for csp in subpath:
                    cmd = 'L'
                    if first:
                        cmd = 'M'
                    first = False
                    newpath.append([cmd, [csp[1][0], csp[1][1]]])
            node.path = newpath

        # flatten the node's path to linearize, split up the path to it's subpaths (break apart) and add all points to the vpype lines collection
        def convertPath(node, nodes = None):
            if nodes is None:
                nodes = []
            if node.tag == inkex.addNS('path','svg'):
                nodes.append(node)
                if self.options.flattenbezier is True:
                    flatten(node)

                raw = node.path.to_arrays()
                subPaths, prev = [], 0
                for i in range(len(raw)): # Breaks compound paths into simple paths
                    if raw[i][0] == 'M' and i != 0:
                        subPaths.append(raw[prev:i])
                        prev = i
                subPaths.append(raw[prev:])
                for subPath in subPaths:
                    points = []
                    for csp in subPath:
                        if len(csp[1]) > 0: #we need exactly two points per straight line segment
                            points.append(Point(round(csp[1][0], self.options.decimals), round(csp[1][1], self.options.decimals)))
                    if  subPath[-1][0] == 'Z' or subPath[0][1] == subPath[-1][1]:  #check if path is closed by Z or first pont == last point
                        points.append(Point(round(subPath[0][1][0], self.options.decimals), round(subPath[0][1][1], self.options.decimals))) #if closed, we add the first point again
                    lc.append(LineString(points))
                      
            children = node.getchildren()
            if children is not None: 
                for child in children:
                    convertPath(child, nodes)
            return nodes

        doc = None #create a vpype document
        
        '''
        if 'paths' we process paths only. Objects like rectangles or strokes like polygon have to be converted before accessing them
        if 'layers' we can process all layers in the complete document
        '''
        if self.options.input_handling == "paths":
            # getting the bounding box of the current selection. We use to calculate the offset XY from top-left corner of the canvas. This helps us placing back the elements
            input_bbox = None
            if self.options.apply_transformations is True and applyTransformAvailable is True:
                '''
                we need to apply transfoms to the complete document even if there are only some single paths selected. 
                If we apply it to selected nodes only the parent groups still might contain transforms. 
                This messes with the coordinates and creates hardly controllable behaviour
                '''
                applytransform.ApplyTransform().recursiveFuseTransform(self.document.getroot())
            if len(self.svg.selected) == 0:
                elementsToWork = convertPath(self.document.getroot())
                for element in elementsToWork:
                    input_bbox += element.bounding_box()      
            else:
                elementsToWork = None
                for element in self.svg.selected.values():
                    elementsToWork = convertPath(element, elementsToWork)
                #input_bbox = inkex.elements._selected.ElementList.bounding_box(self.svg.selected) # get BoundingBox for selection
                input_bbox = self.svg.selection.bounding_box() # get BoundingBox for selection
            if len(lc) == 0:
                inkex.errormsg('Selection appears to be empty or does not contain any valid svg:path nodes. Try to cast your objects to paths using CTRL + SHIFT + C or strokes to paths using CTRL + ALT+ C')
                return  
            # find the first object in selection which has a style attribute (skips groups and other things which have no style)
            firstElementStyle = None
            for element in elementsToWork:
                if element.attrib.has_key('style'):
                    firstElementStyle = element.get('style')
            doc = vpype.Document(page_size=(input_bbox.width + input_bbox.left, input_bbox.height + input_bbox.top)) #create new vpype document     
            doc.add(lc, layer_id=None) # we add the lineCollection (converted selection) to the vpype document
            
        elif self.options.input_handling == "layers":
            doc = vpype.read_multilayer_svg(self.options.input_file, quantization = self.options.flatness, crop = False, simplify = self.options.simplify, parallel = self.options.parallel, default_width = self.document.getroot().get('width'), default_height = self.document.getroot().get('height'))

            for element in self.document.getroot().xpath("//svg:g", namespaces=inkex.NSS): #all groups/layers
                elementsToWork.append(element)

        tooling_length_before = doc.length()
        traveling_length_before = doc.pen_up_length()
        
        # build and execute the conversion command
        # the following code block is not intended to sum up the commands to build a series (pipe) of commands!
        ##########################################
        
        # Line Sorting
        if self.options.linesort is True:
            command = "linesort "
            if self.options.linesort_no_flip is True:
                command += " --no-flip"

        # Line Merging
        if self.options.linemerge is True:     
            command = "linemerge --tolerance " + str(self.options.linemerge_tolerance)
            if self.options.linemerge_no_flip is True:
                command += " --no-flip"
 
        # Trimming
        if self.options.trim is True:     
            command = "trim " + str(self.options.trim_x_margin) + " " + str(self.options.trim_y_margin)
 
        # Relooping
        if self.options.reloop is True:     
            command = "reloop --tolerance " + str(self.options.reloop_tolerance)
 
        # Multipass
        if self.options.multipass is True:     
            command = "multipass --count " + str(self.options.multipass_count)
 
        # Filter
        if self.options.filter is True:     
            command = "filter --tolerance " + str(self.options.filter_tolerance)
            if self.options.filter_min_length_enabled is True:
                command += " --min-length " + str(self.options.filter_min_length)
            if self.options.filter_max_length_enabled is True:
                command += " --max-length " + str(self.options.filter_max_length)
            if self.options.filter_closed is True and self.options.filter_not_closed is False:
                command += " --closed"
            if self.options.filter_not_closed is True and self.options.filter_closed is False:
                command += " --not-closed"
            if self.options.filter_closed is False and \
                self.options.filter_not_closed is False and \
                self.options.filter_min_length_enabled is False and \
                self.options.filter_max_length_enabled is False:
                inkex.errormsg('No filters to apply. Please select at least one filter.')
                return

        # Plugin Occult
        if self.options.plugin_occult is True:     
            command = "occult --tolerance " + str(self.options.plugin_occult_tolerance)
            if self.options.plugin_occult_keepseparatelayer is True:
                command += " --keep-occulted"

        # Split All
        if self.options.splitall is True:     
            command = " splitall"

        # Free Mode
        if self.options.freemode is True:
            command = ""
            if self.options.freemode_cmd1_enabled is True:
                command += " " + self.options.freemode_cmd1.strip()
            if self.options.freemode_cmd2_enabled is True:
                command += " " + self.options.freemode_cmd2.strip()
            if self.options.freemode_cmd3_enabled is True:
                command += " " + self.options.freemode_cmd3.strip()
            if self.options.freemode_cmd4_enabled is True:
                command += " " + self.options.freemode_cmd4.strip()
            if self.options.freemode_cmd5_enabled is True:
                command += " " + self.options.freemode_cmd5.strip()
            if self.options.freemode_cmd1_enabled is False and \
                self.options.freemode_cmd2_enabled is False and \
                self.options.freemode_cmd3_enabled is False and \
                self.options.freemode_cmd4_enabled is False and \
                self.options.freemode_cmd5_enabled is False:
                inkex.utils.debug("Warning: empty vpype pipeline. With this you are just getting read-write layerset/lineset.")
            else:
                if self.options.freemode_show_cmd is True:
                    inkex.utils.debug("Your command pipe will be the following:")
                    inkex.utils.debug(command)

        # inkex.utils.debug(command)
        try:
            doc = execute(command, doc)
        except Exception as e:
            inkex.utils.debug("Error in vpype:" + str(e))
            return

        ##########################################
        
        tooling_length_after = doc.length()
        traveling_length_after = doc.pen_up_length()        
        if tooling_length_before > 0:
            tooling_length_saving = (1.0 - tooling_length_after / tooling_length_before) * 100.0
        else:
            tooling_length_saving = 0.0            
        if traveling_length_before > 0:
            traveling_length_saving = (1.0 - traveling_length_after / traveling_length_before) * 100.0
        else:
            traveling_length_saving = 0.0  
        if self.options.output_stats is True:
            inkex.utils.debug('Total tooling length before vpype conversion: '   + str('{:0.2f}'.format(tooling_length_before))   + ' mm')
            inkex.utils.debug('Total traveling length before vpype conversion: ' + str('{:0.2f}'.format(traveling_length_before)) + ' mm')
            inkex.utils.debug('Total tooling length after vpype conversion: '    + str('{:0.2f}'.format(tooling_length_after))    + ' mm')
            inkex.utils.debug('Total traveling length after vpype conversion: '  + str('{:0.2f}'.format(traveling_length_after))  + ' mm')
            inkex.utils.debug('Total tooling length optimized: '   + str('{:0.2f}'.format(tooling_length_saving))   + ' %')
            inkex.utils.debug('Total traveling length optimized: ' + str('{:0.2f}'.format(traveling_length_saving)) + ' %')
         
        if tooling_length_after == 0:
            inkex.errormsg('No lines left after vpype conversion. Conversion result is empty. Cannot continue')
            return
         
        # show the vpype document visually
        if self.options.output_show:
            warnings.filterwarnings("ignore") # workaround to suppress annoying DeprecationWarning
            # vpype_viewer.show(doc, view_mode=ViewMode.PREVIEW, show_pen_up=self.options.output_trajectories, show_points=self.options.output_show_points, pen_width=0.1, pen_opacity=1.0, argv=None)
            vpype_viewer.show(doc, view_mode=ViewMode.PREVIEW, show_pen_up=self.options.output_trajectories, show_points=self.options.output_show_points, argv=None) # https://vpype.readthedocs.io/en/stable/api/vpype_viewer.ViewMode.html
            warnings.filterwarnings("default") # reset warning filter
            exit(0) #we leave the code loop because we only want to preview. We don't want to import the geometry
          
        # save the vpype document to new svg file and close it afterwards
        output_file = self.options.input_file + ".vpype.svg"
        output_fileIO = open(output_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=self.options.output_trajectories, color_mode='layer', single_path = True)       
        vpype.write_svg(output_fileIO, doc, page_size=None, center=False, source_string='', layer_label_format='%d', show_pen_up=self.options.output_trajectories, color_mode='layer')       
        #vpype.write_svg(output_fileIO, doc, page_size=(self.svg.unittouu(self.document.getroot().get('width')), self.svg.unittouu(self.document.getroot().get('height'))), center=False, source_string='', layer_label_format='%d', show_pen_up=self.options.output_trajectories, 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."
        if self.options.strokes_to_paths is True:
            cli_output = inkscape(output_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)
        
        # this does not work because line, polyline and polygon have no base class to execute replace_with
        #if self.options.strokes_to_paths is True:
        #    for lineLayer in lineLayers:
        #        for element in lineLayer:
        #                element.replace_with(element.to_path_element())
               
        # parse the SVG file
        try:
            stream = open(output_file, 'r')
        except FileNotFoundError as e:
            inkex.utils.debug("There was no SVG output generated by vpype. Cannot continue")
            exit(1)
        p = etree.XMLParser(huge_tree=True)
        import_doc = etree.parse(stream, parser=etree.XMLParser(huge_tree=True))
        stream.close()
          
        # handle pen_up trajectories (travel lines)
        trajectoriesLayer = import_doc.getroot().xpath("//svg:g[@id='pen_up_trajectories']", namespaces=inkex.NSS)
        if self.options.output_trajectories is True:
            if len(trajectoriesLayer) > 0:
                trajectoriesLayer[0].set('style', 'stroke:#0000ff;stroke-width:{:0.2f}px;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:none'.format(self.options.trajectories_stroke_width))
                trajectoriesLayer[0].attrib.pop('stroke') # remove unneccesary stroke attribute
                trajectoriesLayer[0].attrib.pop('fill') # remove unneccesary fill attribute
        else:
            if len(trajectoriesLayer) > 0:
                trajectoriesLayer[0].delete()
   
        lineLayers = import_doc.getroot().xpath("//svg:g[not(@id='pen_up_trajectories')]", namespaces=inkex.NSS) #all layer except the pen_up trajectories layer
        if self.options.use_style_of_first_element is True and self.options.input_handling == "paths" and firstElementStyle is not None:
            
            # if we remove the fill property and use "Use style of first element in layer" the conversion will just crash with an unknown reason
            #declarations = firstElementStyle.split(';')
            #for i, decl in enumerate(declarations):
            #    parts = decl.split(':', 2)
            #    if len(parts) == 2:
            #        (prop, val) = parts
            #        prop = prop.strip().lower()
            #        #if prop == 'fill':
            #        #   declarations[i] = prop + ':none'   
            for lineLayer in lineLayers:
                #lineLayer.set('style', ';'.join(declarations))
                lineLayer.set('style', firstElementStyle)
                lineLayer.attrib.pop('stroke') # remove unneccesary stroke attribute
                lineLayer.attrib.pop('fill') # remove unneccesary fill attribute

        else:
            for lineLayer in lineLayers:          
                if lineLayer.attrib.has_key('stroke'):
                    color = lineLayer.get('stroke')
                    lineLayer.set('style', 'stroke:' + color + ';stroke-width:{:0.2f}px;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:none'.format(self.options.lines_stroke_width))
                    lineLayer.attrib.pop('stroke') # remove unneccesary stroke attribute
                    lineLayer.attrib.pop('fill') # remove unneccesary fill attribute

        import_viewBox = import_doc.getroot().get('viewBox').split(" ")
        self_viewBox = self.document.getroot().get('viewBox').split(" ")
        scaleX = self.svg.unittouu(self_viewBox[2]) / self.svg.unittouu(import_viewBox[2])
        scaleY = self.svg.unittouu(self_viewBox[3]) / self.svg.unittouu(import_viewBox[3])

        for element in import_doc.getroot().iter("{http://www.w3.org/2000/svg}g"):
            self.document.getroot().append(element)
            if self.options.input_handling == "layers":
                element.set('transform', 'scale(' + str(scaleX) + ',' + str(scaleY) + ')') #imported groups need to be transformed. Or they have wrong size. Reason: different viewBox sizes/units in namedview definitions
                if self.options.apply_transformations is True and applyTransformAvailable is True: #we apply the transforms directly after adding them
                    applytransform.ApplyTransform().recursiveFuseTransform(element) 

        # Delete the temporary file again because we do not need it anymore
        if os.path.exists(output_file):
            os.remove(output_file)
            
        # Remove selection objects to do a real replace with new objects from vpype document
        if self.options.keep_objects is False:
            for element in elementsToWork:
                element.delete()
    def effect(self):
        if not self.svg.selected:
            inkex.errormsg(
                "Selection is empty. Please select some objects first!")
            return

        export_dir = Path(self.absolute_href(self.options.export_dir))
        os.makedirs(export_dir, exist_ok=True)

        offset = self.options.border_offset

        bbox = inkex.BoundingBox()

        for elem in self.svg.selected.values():
            transform = inkex.Transform()
            parent = elem.getparent()
            if parent is not None and isinstance(parent, inkex.ShapeElement):
                transform = parent.composed_transform()
            try:
                bbox += elem.bounding_box(transform)
            except Exception:
                logger.exception("Bounding box not computed")
                logger.info("Skipping bounding box")
                transform = elem.composed_transform()
                x1, y1 = transform.apply_to_point([0, 0])
                x2, y2 = transform.apply_to_point([1, 1])
                bbox += inkex.BoundingBox((x1, x2), (y1, y2))

        template = self.create_document()
        filename = None

        group = etree.SubElement(template, '{http://www.w3.org/2000/svg}g')
        group.attrib['id'] = GROUP_ID
        group.attrib['transform'] = str(
            inkex.Transform(((1, 0, -bbox.left), (0, 1, -bbox.top))))

        for elem in self.svg.selected.values():
            elem_copy = deepcopy(elem)
            elem_copy.attrib['transform'] = str(elem.composed_transform())
            group.append(elem_copy)

            template.attrib[
                'viewBox'] = f'{-offset} {-offset} {bbox.width + offset * 2} {bbox.height + offset * 2}'
            template.attrib[
                'width'] = f'{bbox.width + offset * 2}' + self.svg.unit
            template.attrib[
                'height'] = f'{bbox.height + offset * 2}' + self.svg.unit

            if filename is None:
                filename = elem.attrib.get('id', None)
                if filename:
                    filename = filename.replace(os.sep, '_') + '.svg'
        if not filename:  #should never be the case. Inkscape might crash if the id attribute is empty or not existent due to invalid SVG
            filename = self.svg.get_unique_id("selection") + '.svg'

        template.append(group)

        if not self.options.wrap_transform:
            self.load(
                inkscape_command(template.tostring(),
                                 select=GROUP_ID,
                                 verbs=['SelectionUnGroup']))
            template = self.svg
            for child in template.getchildren():
                if child.tag == '{http://www.w3.org/2000/svg}metadata':
                    template.remove(child)

        self.save_document(template, export_dir / filename)

        if self.options.opendir is True:
            self.openExplorer(export_dir)

        if self.options.newwindow is True:
            inkscape(os.path.join(export_dir, filename))

        if self.options.export_dxf is True:
            #ensure that python3 command is available #we pass 25.4/96 which stands for unit mm. See inkex.units.UNITS and dxf_outlines.inx
            cmd = [
                'python3', self.options.dxf_exporter_path,
                '--output=' + os.path.join(export_dir, filename + '.dxf'),
                r'--units=25.4/96',
                os.path.join(export_dir, filename)
            ]
            proc = Popen(cmd, shell=False, stdout=PIPE, stderr=PIPE)
            stdout, stderr = proc.communicate()
            #inkex.utils.debug("%d %s %s" % (proc.returncode, stdout, stderr))

        if self.options.export_pdf is True:
            cli_output = inkscape(
                os.path.join(export_dir, filename),
                actions=
                'export-pdf-version:1.5;export-text-to-path;export-filename:{file_name};export-do;FileClose'
                .format(file_name=os.path.join(export_dir, filename + '.pdf')))
            if len(cli_output) > 0:
                self.msg(
                    "Inkscape returned the following output when trying to run the file export; the file export may still have worked:"
                )
                self.msg(cli_output)
Beispiel #15
0
def export_image(source, output, resolution=None):
    args = [source, "-o", output]
    if resolution:
        args += ["-h", str(resolution)]
    debug("Calling Inkscape with args %s" % args)
    command.inkscape(*args)