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
|
# Authors: see git history
#
# Copyright (c) 2010 Authors
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
import os
import inkex
from ..svg.tags import (INKSCAPE_GROUPMODE, INKSCAPE_LABEL, SVG_GROUP_TAG,
SVG_PATH_TAG, SVG_USE_TAG)
from ..update import update_inkstitch_document
from .glyph import Glyph
class FontVariant(object):
"""Represents a single variant of a font.
Each font may have multiple variants for left-to-right, right-to-left,
etc. Each variant has a set of Glyphs, one per character.
A FontVariant instance can be accessed as a dict by using a unicode
character as a key.
Properties:
path -- the path to the directory containing this font
variant -- the font variant, specified using one of the constants below
glyphs -- a dict of Glyphs, with the glyphs' unicode characters as keys.
"""
# We use unicode characters rather than English strings for font file names
# in order to be more approachable for languages other than English.
LEFT_TO_RIGHT = "→"
RIGHT_TO_LEFT = "←"
TOP_TO_BOTTOM = "↓"
BOTTOM_TO_TOP = "↑"
VARIANT_TYPES = (LEFT_TO_RIGHT, RIGHT_TO_LEFT, TOP_TO_BOTTOM, BOTTOM_TO_TOP)
@classmethod
def reversed_variant(cls, variant):
if variant == cls.LEFT_TO_RIGHT:
return cls.RIGHT_TO_LEFT
elif variant == cls.RIGHT_TO_LEFT:
return cls.LEFT_TO_RIGHT
elif variant == cls.TOP_TO_BOTTOM:
return cls.BOTTOM_TO_TOP
elif variant == cls.BOTTOM_TO_TOP:
return cls.TOP_TO_BOTTOM
else:
return None
def __init__(self, font_path, variant, default_glyph=None):
# If the font variant file does not exist, this constructor will
# raise an exception. The caller should catch it and decide
# what to do.
self.path = font_path
self.variant = variant
self.default_glyph = default_glyph
self.glyphs = {}
self._load_glyphs()
def _load_glyphs(self):
variant_file_paths = self._get_variant_file_paths()
for svg_path in variant_file_paths:
document = inkex.load_svg(svg_path)
update_inkstitch_document(document)
svg = document.getroot()
svg = self._apply_transforms(svg)
glyph_layers = svg.xpath(".//svg:g[starts-with(@inkscape:label, 'GlyphLayer-')]", namespaces=inkex.NSS)
for layer in glyph_layers:
self._clean_group(layer)
layer.attrib[INKSCAPE_LABEL] = layer.attrib[INKSCAPE_LABEL].replace("GlyphLayer-", "", 1)
glyph_name = layer.attrib[INKSCAPE_LABEL]
try:
self.glyphs[glyph_name] = Glyph(layer)
except (AttributeError, ValueError):
pass
def _get_variant_file_paths(self):
file_paths = []
direct_path = os.path.join(self.path, "%s.svg" % self.variant)
if os.path.isfile(direct_path):
file_paths.append(direct_path)
elif os.path.isdir(os.path.join(self.path, "%s" % self.variant)):
path = os.path.join(self.path, "%s" % self.variant)
file_paths.extend([os.path.join(path, svg) for svg in os.listdir(path) if svg.endswith('.svg')])
return file_paths
def _clean_group(self, group):
# We'll repurpose the layer as a container group labelled with the
# glyph.
del group.attrib[INKSCAPE_GROUPMODE]
# The layer may be marked invisible, so we'll clear the 'display'
# style and presentation attribute.
group.style.pop('display', None)
group.attrib.pop('display', None)
def _apply_transforms(self, svg):
# apply transforms to paths and use tags
for element in svg.iterdescendants((SVG_PATH_TAG, SVG_USE_TAG)):
transform = element.composed_transform()
if element.tag == SVG_PATH_TAG:
path = element.path.transform(transform)
element.set_path(path)
element.attrib.pop("transform", None)
if element.tag == SVG_USE_TAG:
oldx = element.get('x', 0)
oldy = element.get('y', 0)
newx, newy = transform.apply_to_point((oldx, oldy))
element.set('x', newx)
element.set('y', newy)
element.attrib.pop("transform", None)
# remove transforms after they have been applied
for group in svg.iterdescendants(SVG_GROUP_TAG):
group.attrib.pop('transform', None)
return svg
def __getitem__(self, character):
if character in self.glyphs:
return self.glyphs[character]
else:
return self.glyphs.get(self.default_glyph, None)
def __contains__(self, character):
return character in self.glyphs
|