summaryrefslogtreecommitdiff
path: root/lib/lettering/glyph.py
blob: fd97885bdc82c329592f38e77e64545b57cc35cf (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
# Authors: see git history
#
# Copyright (c) 2010 Authors
# Licensed under the GNU GPL version 3.0 or later.  See the file LICENSE for details.

from copy import copy

from inkex import paths, transforms, units

from ..svg import get_correction_transform, get_guides
from ..svg.tags import (CONNECTION_END, SVG_GROUP_TAG, SVG_PATH_TAG,
                        SVG_USE_TAG, XLINK_HREF)


class Glyph(object):
    """Represents a single character in a single font variant.

    For example, the font inkstitch_small may have variants for left-to-right,
    right-to-left, etc.  Each variant would have a set of Glyphs, one for each
    character in that variant.

    Properties:
      width -- total width of this glyph including all component satins
      node  -- svg:g XML node containing the component satins in this character
    """

    def __init__(self, group):
        """Create a Glyph.

        The nodes will be copied out of their parent SVG document (with nested
        transforms applied).  The original nodes will be unmodified.

        Arguments:
          group -- an svg:g XML node containing all the paths that make up
            this Glyph.  Nested groups are allowed.
        """

        self._process_baseline(group.getroottree().getroot())
        self.node = self._process_group(group)
        self._process_bbox()
        self._move_to_origin()
        self._process_commands()

    def _process_group(self, group):
        new_group = copy(group)
        # new_group.attrib.pop('transform', None)
        # delete references to the original group's children
        del new_group[:]

        for node in group:
            if node.tag == SVG_GROUP_TAG:
                new_group.append(self._process_group(node))
            else:
                node_copy = copy(node)
                transform = -transforms.Transform(get_correction_transform(node, True))

                if "d" in node.attrib:
                    node_copy.path = node.path.transform(transform).to_absolute()

                if not node.tag == SVG_USE_TAG:
                    # Delete transforms from paths and groups, since we applied
                    # them to the paths already.
                    node_copy.attrib.pop('transform', None)
                else:
                    oldx = node.get('x', 0)
                    oldy = node.get('y', 0)
                    x, y = transform.apply_to_point((oldx, oldy))
                    node_copy.set('x', x)
                    node_copy.set('y', y)

                new_group.append(node_copy)

        return new_group

    def _process_baseline(self, svg):
        for guide in get_guides(svg):
            if guide.label == "baseline":
                self.baseline = guide.position.y
                break
        else:
            # no baseline guide found, assume 0 for lack of anything better to use...
            self.baseline = 0

    def _process_bbox(self):
        bbox = [paths.Path(node.get("d")).bounding_box() for node in self.node.iterdescendants(SVG_PATH_TAG) if not node.get(CONNECTION_END, None)]
        left, right = min([box.left for box in bbox]), max([box.right for box in bbox])
        self.width = right - left
        self.min_x = left

    def _process_commands(self):
        # Save object ids with commands in a dictionary: {object_id: [connector_id, symbol_id]}
        self.commands = {}

        for node in self.node.iter(SVG_USE_TAG):
            xlink = node.get(XLINK_HREF, ' ')
            if not xlink.startswith('#inkstitch_'):
                continue

            try:
                connector = self.node.xpath(".//*[@inkscape:connection-start='#%s']" % node.get('id', ' '))[0]
                command_object = connector.get(CONNECTION_END)[1:]
                try:
                    self.commands[command_object].append([connector.get_id(), node.get_id()])
                except KeyError:
                    self.commands[command_object] = [[connector.get_id(), node.get_id()]]
            except IndexError:
                pass

    def _move_to_origin(self):
        translate_x = -self.min_x
        translate_y = -self.baseline
        transform = transforms.Transform("translate(%s, %s)" % (translate_x, translate_y))

        for node in self.node.iter(SVG_PATH_TAG):
            path = paths.Path(node.get("d"))
            path = path.transform(transform)
            node.set('d', str(path))
            node.attrib.pop('transform', None)

        # Move commands as well
        for node in self.node.iter(SVG_USE_TAG):
            oldx = units.convert_unit(node.get("x", 0), 'px', node.unit)
            oldy = units.convert_unit(node.get("y", 0), 'px', node.unit)
            x, y = transform.apply_to_point((oldx, oldy))
            node.set('x', x)
            node.set('y', y)