SVGImporter.py 8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
# -*- coding: utf-8 -*-
"""
$Id$

Copyright 2010 Lars Kruse <devel@sumpfralle.de>

This file is part of PyCAM.

PyCAM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

PyCAM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with PyCAM.  If not, see <http://www.gnu.org/licenses/>.
"""

import pycam.Importers.DXFImporter
24
from pycam.Utils.locations import get_external_program_location
25
import pycam.Utils
26 27 28 29
import tempfile
import subprocess
import os

30

31 32
log = pycam.Utils.log.get_logger()

33

34 35
def convert_svg2eps(svg_filename, eps_filename, location=None):
    if location is None:
36
        location = get_external_program_location("inkscape")
37 38
        if location is None:
            location = "inkscape"
39 40 41
    try:
        process = subprocess.Popen(stdin=subprocess.PIPE,
                stdout=subprocess.PIPE, stderr=subprocess.PIPE,
42 43
                args = [location, "--export-area-page", "--export-eps",
                        eps_filename, svg_filename])
44
    except OSError, err_msg:
sumpfralle's avatar
sumpfralle committed
45 46 47
        log.error(("SVGImporter: failed to execute 'inkscape' (%s): %s%s" + \
                "Maybe you need to install Inkscape (http://inkscape.org)?") % \
                (location, err_msg, os.linesep))
48 49 50 51 52 53 54 55 56 57
        return False
    returncode = process.wait()
    if returncode == 0:
        return True
    else:
        log.warn(("SVGImporter: failed to convert SVG file (%s) to EPS file " \
                + "(%s): %s") % (svg_filename, eps_filename,
                process.stderr.read()))
        return False

58
def convert_eps2dxf(eps_filename, dxf_filename, location=None, unit="mm"):
59
    if location is None:
60
        location = get_external_program_location("pstoedit")
61 62
        if location is None:
            location = "pstoedit"
63 64 65 66 67 68
    args = [location, "-dt", "-nc", "-f", "dxf:-polyaslines"]
    if unit == "mm":
        # eps uses inch by default - we need to scale
        args.extend(("-xscale", "25.4", "-yscale", "25.4"))
    args.append(eps_filename)
    args.append(dxf_filename)
69
    try:
70 71
        process = subprocess.Popen(stdin=subprocess.PIPE,
                stdout=subprocess.PIPE, stderr=subprocess.PIPE, args=args)
72
    except OSError, err_msg:
sumpfralle's avatar
sumpfralle committed
73 74 75
        log.error(("SVGImporter: failed to execute 'pstoedit' (%s): %s%s" + \
                "Maybe you need to install pstoedit (http://pstoedit.net)?") % \
                (location, err_msg, os.linesep))
76 77 78
        return False
    returncode = process.wait()
    if returncode == 0:
sumpfralle's avatar
sumpfralle committed
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
        try:
            # pstoedit fails with exitcode=0 if ghostscript is not installed.
            # The resulting file seems to be quite small (268 byte). But it is
            # not certain, that this filesize is fixed in case of this problem.
            if os.path.getsize(dxf_filename) < 280:
                log.warn(("SVGImporter: maybe there was a problem with " + \
                        "the conversion from EPS (%s) to DXF.\nProbably " + \
                        "you need to install 'ghostscript' " + \
                        "(http://pages.cs.wisc.edu/~ghost).") % \
                        str(eps_filename))
            return True
        except OSError:
            # The dxf file was not created.
            log.warn("SVGImporter: no DXF file was created, even though " + \
                    "no error code was returned. This seems to be a bug " + \
                    "of 'pstoedit'. Please send the original model file " + \
                    "to the PyCAM developers. Thanks!")
            return False
97
    elif returncode == -11:
sumpfralle's avatar
sumpfralle committed
98
        log.warn(("SVGImporter: maybe there was a problem with the " + \
sumpfralle's avatar
sumpfralle committed
99
                "conversion from EPS (%s) to DXF.\n Users of Ubuntu 'lucid' " + \
sumpfralle's avatar
sumpfralle committed
100 101 102
                "should install the package 'libpstoedit0c2a' from the " + \
                "'maverick' repository to avoid this warning.") % \
                str(eps_filename))
103
        return True
104 105 106 107 108 109
    else:
        log.warn(("SVGImporter: failed to convert EPS file (%s) to DXF file " \
                + "(%s): %s") % (eps_filename, dxf_filename,
                process.stderr.read()))
        return False

110 111
def import_model(filename, program_locations=None, unit="mm", callback=None,
        **kwargs):
112 113 114
    local_file = False
    if hasattr(filename, "read"):
        infile = filename
115
        svg_file_handle, svg_file_name = tempfile.mkstemp(suffix=".svg")
116 117 118 119 120 121 122 123
        try:
            temp_file = os.fdopen(svg_file_handle, "w")
            temp_file.write(infile.read())
            temp_file.close()
        except IOError, err_msg:
            log.error("SVGImporter: Failed to create temporary local file " + \
                    "(%s): %s" % (svg_file_name, err_msg))
            return
124 125
        filename = svg_file_name
    else:
126 127
        uri = pycam.Utils.URIHandler(filename)
        if not uri.exists():
128 129
            log.error("SVGImporter: file (%s) does not exist" % filename)
            return None
130
        if not uri.is_local():
131 132 133 134 135
            # non-local file - write it to a temporary file first
            svg_file_handle, svg_file_name = tempfile.mkstemp(suffix=".svg")
            os.close(svg_file_handle)
            log.debug("Retrieving SVG file for local access: %s -> %s" % \
                    (uri, svg_file_name))
136
            if not uri.retrieve_remote_file(svg_file_name, callback=callback):
137 138 139 140
                log.error("SVGImporter: Failed to retrieve the SVG model " + \
                        "file: %s -> %s" % (uri, svg_file_name))
            filename = svg_file_name
        else:
141
            filename = uri.get_local_path()
142
            local_file = True
143 144 145 146

    if program_locations and "inkscape" in program_locations:
        inkscape_path = program_locations["inkscape"]
    else:
147
        inkscape_path = None
148 149 150 151

    if program_locations and "pstoedit" in program_locations:
        pstoedit_path = program_locations["pstoedit"]
    else:
152
        pstoedit_path = None
153 154 155

    # the "right" way would be:
    # inkscape --print='| pstoedit -dt -f dxf:-polyaslines - -' input.svg
sumpfralle's avatar
sumpfralle committed
156 157
    # Sadly a bug in v0.47 breaks this:
    # https://bugs.launchpad.net/inkscape/+bug/511361
158 159 160 161 162 163 164 165 166 167 168 169 170

    # convert svg to eps via inkscape
    eps_file_handle, eps_file_name = tempfile.mkstemp(suffix=".eps")
    os.close(eps_file_handle)
    success = convert_svg2eps(filename, eps_file_name, location=inkscape_path)
    def remove_temp_file(filename):
        if os.path.isfile(filename):
            try:
                os.remove(filename)
            except OSError, err_msg:
                log.warn("SVGImporter: failed to remove temporary file " \
                        + "(%s): %s" % (filename, err_msg))
    # remove the temporary file
171 172
    if not local_file:
        remove_temp_file(svg_file_name)
173 174 175
    if not success:
        remove_temp_file(eps_file_name)
        return None
176 177 178 179
    if callback and callback():
        remove_temp_file(eps_file_name)
        log.warn("SVGImporter: load model operation was cancelled")
        return None
180 181 182 183 184
    log.info("Successfully converted SVG file to EPS file")

    # convert eps to dxf via pstoedit
    dxf_file_handle, dxf_file_name = tempfile.mkstemp(suffix=".dxf")
    os.close(dxf_file_handle)
185 186
    success = convert_eps2dxf(eps_file_name, dxf_file_name, unit=unit,
            location=pstoedit_path)
187 188 189 190
    # we don't need the eps file anymore
    remove_temp_file(eps_file_name)
    if not success:
        result = None
191 192 193
    elif callback and callback():
        log.warn("SVGImporter: load model operation was cancelled")
        result = None
194 195
    else:
        log.info("Successfully converted EPS file to DXF file")
196
        result = pycam.Importers.DXFImporter.import_model(dxf_file_name,
197
                unit=unit, color_as_height=True, callback=callback)
198 199 200 201
    # always remove the dxf file
    remove_temp_file(dxf_file_name)
    return result