summaryrefslogtreecommitdiffstats
path: root/share/extensions/media_zip.py
blob: 82fb25d1354ba6ef841333cd9614901dc967aea8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
#!/usr/bin/env python
# coding=utf-8
#
# Copyright (C) 2005 Pim Snel, pim@lingewoud.com
# Copyright (C) 2008 Aaron Spike, aaron@ekips.org
# Copyright (C) 2011 Nicolas Dufour, nicoduf@yahoo.fr
#
#    * Fix for a bug related to special characters in the path (LP #456248).
#    * Fix for Windows support (LP #391307 ).
#    * Font list and image directory features.
#
# this is  the first Python script  ever created
# its based on embedimage.py
#
# This program 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 2 of the License, or
# (at your option) any later version.
#
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# TODOs
# - fix bug: not saving existing .zip after a Collect for Output is run
#     this bug occurs because after running an effect extension the inkscape:output_extension is reset to svg.inkscape
#     the file name is still xxx.zip. after saving again the file xxx.zip is written with a plain .svg which
#     looks like a corrupt zip
# - maybe add better extension
# - consider switching to lzma in order to allow cross platform compression with no encoding problem...
#
"""
An extension which collects all images to the documents directory and
creates a zip archive containing all images and the document
"""

import os
import tempfile
import zipfile

from typing import List, Tuple
from urllib.parse import urlparse
from urllib.request import url2pathname

import inkex
from inkex import TextElement, Tspan, FlowRoot, FlowPara, FlowSpan
from inkex.localization import inkex_gettext as _


class CompressedMedia(inkex.OutputExtension):
    """Output a compressed file"""

    def __init__(self):
        super().__init__()
        self.path_dict = {}

    def add_arguments(self, pars):
        pars.add_argument("--image_dir", help="Image directory", default="images")
        pars.add_argument("--font_list", type=inkex.Boolean, help="Add font list")

    def process_path(self, path: str) -> Tuple[str, bool]:
        """Processes an absolute path and returns
        (unique filename, exists)."""

        if path in self.path_dict:
            return os.path.split(path)[1], True

        index = 0
        pth, ext = os.path.splitext(path)
        _, filename = os.path.split(pth)
        trypath = filename + ext
        while True:
            if trypath in list(self.path_dict.values()):
                index += 1
                trypath = f"{filename}{index}{ext}"
            else:
                self.path_dict[path] = trypath
                return trypath, False

    def collect_images(self, docname, z: zipfile.ZipFile):
        """
        Collects all images in the document
        and copy them to the temporary directory.
        """
        imgdir = self.options.image_dir

        for node in self.svg.xpath("//svg:image"):
            xlink = node.get("xlink:href")
            if xlink[:4] != "data":
                url = urlparse(xlink)
                href = url2pathname(url.path)

                image_path = self.absolute_href(href or "")

                # Backup directory where we can find the image
                if not os.path.isfile(image_path):
                    image_path = node.get("sodipodi:absref", image_path)

                if not os.path.isfile(image_path):
                    inkex.errormsg(
                        _('File not found "{}". Unable to embed image.').format(
                            image_path
                        )
                    )
                    continue
                else:
                    zippath, exists = self.process_path(image_path)
                    if not exists:
                        z.write(image_path, os.path.join(imgdir, zippath))

                    node.set("xlink:href", f"{imgdir}/{zippath}")

    def collect_svg(self, docstripped, z: zipfile.ZipFile):
        """
        Copy SVG document to the temporary directory
        and add it to the temporary compressed file
        """
        dst_file = os.path.join(self.tmp_dir, docstripped)
        with open(dst_file, "wb") as stream:
            self.document.write(stream)
        z.write(dst_file, docstripped + ".svg")

    def is_text(self, node):
        """
        Returns true if the tag in question is an element that
        can hold text.
        """
        return isinstance(node, (TextElement, Tspan, FlowRoot, FlowPara, FlowSpan))

    def get_fonts(self, node):
        """
        Given a node, returns a list containing all the fonts that
        the node is using.
        """
        fonts = []
        s = ""
        if "style" in node.attrib:
            s = dict(inkex.Style.parse_str(node.attrib["style"]))
        if not s:
            return fonts

        if "font-family" in s:
            if "font-weight" in s:
                fonts.append(s["font-family"] + " " + s["font-weight"])
            else:
                fonts.append(s["font-family"])
        elif "-inkscape-font-specification" in s:
            fonts.append(s["-inkscape-font-specification"])
        return fonts

    def list_fonts(self, z: zipfile.ZipFile):
        """
        Walks through nodes, building a list of all fonts found, then
        reports to the user with that list.
        Based on Craig Marshall's replace_font.py
        """
        nodes: List[inkex.BaseElement] = []
        items = self.svg.iterdescendants()
        nodes.extend(filter(self.is_text, items))
        fonts_found = []
        for node in nodes:
            for f in self.get_fonts(node):
                if not f in fonts_found:
                    fonts_found.append(f)
        findings = sorted(fonts_found)
        # Write list to the temporary compressed file
        filename = "fontlist.txt"
        dst_file = os.path.join(self.tmp_dir, filename)
        with open(dst_file, "w") as stream:
            if len(findings) == 0:
                stream.write(_("Didn't find any fonts in this document/selection."))
            else:
                if len(findings) == 1:
                    stream.write(_("Found the following font only: %s") % findings[0])
                else:
                    stream.write(
                        _("Found the following fonts:\n%s") % "\n".join(findings)
                    )
        z.write(dst_file, filename)

    def save(self, stream):
        docname = self.svg.get("sodipodi:docname")

        if docname is None:
            docname = self.options.input_file

        # TODO: replace whatever extension
        docstripped = os.path.basename(docname.replace(".zip", ""))
        docstripped = docstripped.replace(".svg", "")
        docstripped = docstripped.replace(".svgz", "")

        # Create os temp dir
        self.tmp_dir = tempfile.mkdtemp()

        # Create destination zip in same directory as the document
        with zipfile.ZipFile(stream, "w") as z:
            self.collect_images(docname, z)
            self.collect_svg(docstripped, z)
            if self.options.font_list:
                self.list_fonts(z)


if __name__ == "__main__":
    CompressedMedia().run()