summaryrefslogtreecommitdiffstats
path: root/share/extensions/image_embed.py
blob: e04bc85aec2cf5e09d3782b2d7f8ad93a7fda77b (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
#!/usr/bin/env python
# coding=utf-8
#
# Copyright (C) 2005,2007 Aaron Spike, aaron@ekips.org
#
# 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.
#
# pylint: disable=ungrouped-imports
"""
Embed images so they are base64 encoded data inside the svg.
"""

from __future__ import unicode_literals

import os

import inkex
from inkex import Image
from inkex.localization import inkex_gettext as _

try:
    import urllib.request as urllib
    import urllib.parse as urlparse
    from base64 import encodebytes
except ImportError:
    # python2 compatibility, remove when python3 only.
    import urllib
    import urlparse
    from base64 import encodestring as encodebytes


class EmbedImage(inkex.EffectExtension):
    """Allow selected image tags to become embeded image tags"""

    def add_arguments(self, pars):
        pars.add_argument(
            "--selectedonly", type=inkex.Boolean, help="embed only selected images"
        )

    def effect(self):
        # if slectedonly is enabled and there is a selection
        # only embed selected images. otherwise embed all images
        if self.options.selectedonly:
            images = self.svg.selection.get(Image)
        else:
            images = self.svg.xpath("//svg:image")

        for node in images:
            self.embed_image(node)

    def embed_image(self, node):
        """Embed the data of the selected Image Tag element"""
        xlink = node.get("xlink:href")
        if xlink is not None and xlink[:5] == "data:":
            # No need, data already embedded
            return
        if xlink is None:
            inkex.errormsg(
                _('Attribute "xlink:href" not set on node {}.'.format(node.get_id()))
            )
            return

        url = urlparse.urlparse(xlink)
        href = urllib.url2pathname(url.path)

        # Look relative to the *temporary* filename instead of the original filename.
        try:
            cwd = os.path.dirname(self.options.input_file)
        except TypeError:
            # input_file was actually stdin, fall back.
            cwd = None

        path = self.absolute_href(href or "", cwd=cwd)

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

        if not os.path.isfile(path):
            inkex.errormsg(
                _('File not found "{}". Unable to embed image.').format(path)
            )
            return

        with open(path, "rb") as handle:
            # Don't read the whole file to check the header
            file_type = get_type(path, handle.read(10))
            handle.seek(0)

            if file_type:
                # Future: Change encodestring to encodebytes when python3 only
                node.set(
                    "xlink:href",
                    "data:{};base64,{}".format(
                        file_type, encodebytes(handle.read()).decode("ascii")
                    ),
                )
                node.pop("sodipodi:absref")
            else:
                inkex.errormsg(
                    _(
                        "%s is not of type image/png, image/jpeg, "
                        "image/bmp, image/gif, image/tiff, or image/x-icon"
                    )
                    % path
                )


def get_type(path, header):
    """Basic magic header checker, returns mime type"""
    for head, mime in (
        (b"\x89PNG", "image/png"),
        (b"\xff\xd8", "image/jpeg"),
        (b"BM", "image/bmp"),
        (b"GIF87a", "image/gif"),
        (b"GIF89a", "image/gif"),
        (b"MM\x00\x2a", "image/tiff"),
        (b"II\x2a\x00", "image/tiff"),
    ):
        if header.startswith(head):
            return mime

    # ico files lack any magic... therefore we check the filename instead
    for ext, mime in (
        # official IANA registered MIME is 'image/vnd.microsoft.icon' tho
        (".ico", "image/x-icon"),
        (".svg", "image/svg+xml"),
    ):
        if path.endswith(ext):
            return mime
    return None


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