python-lottie  0.6.11+devcecd248
A framework to work with lottie files and telegram animated stickers (tgs)
font.py
Go to the documentation of this file.
1 import os
2 import sys
3 import subprocess
4 import fontTools.pens.basePen
5 import fontTools.ttLib
6 import fontTools.t1Lib
7 from fontTools.pens.boundsPen import ControlBoundsPen
8 import enum
9 import math
10 from xml.etree import ElementTree
11 from ..nvector import NVector
12 from ..objects.bezier import Bezier, BezierPoint
13 from ..objects.shapes import Path, Group, Fill, Stroke
14 from ..objects.text import TextJustify
15 from ..objects.base import LottieProp, CustomObject
16 from ..objects.layers import ShapeLayer
17 
18 
19 class BezierPen(fontTools.pens.basePen.BasePen):
20  def __init__(self, glyphSet, offset=NVector(0, 0)):
21  super().__init__(glyphSet)
22  self.beziers = []
23  self.current = Bezier()
24  self.offset = offset
25 
26  def _point(self, pt):
27  return self.offset + NVector(pt[0], -pt[1])
28 
29  def _moveTo(self, pt):
30  self._endPath()
31 
32  def _endPath(self):
33  if len(self.current.points):
34  self.beziers.append(self.current)
35  self.current = Bezier()
36 
37  def _closePath(self):
38  self.current.close()
39  self._endPath()
40 
41  def _lineTo(self, pt):
42  if len(self.current.points) == 0:
43  self.current.points.append(self._point(self._getCurrentPoint()))
44 
45  self.current.points.append(self._point(pt))
46 
47  def _curveToOne(self, pt1, pt2, pt3):
48  if len(self.current.points) == 0:
49  cp = self._point(self._getCurrentPoint())
50  self.current.points.append(
52  cp,
53  None,
54  self._point(pt1) - cp
55  )
56 
57  )
58  else:
59  self.current.points[-1].out_tangent = self._point(pt1) - self.current.points[-1].vertex
60 
61  dest = self._point(pt3)
62  self.current.points.append(
64  dest,
65  self._point(pt2) - dest,
66  None,
67  )
68  )
69 
70 
71 class SystemFont:
72  def __init__(self, family):
73  self.family = family
74  self.files = {}
75  self.styles = set()
76  self._renderers = {}
77 
78  def add_file(self, styles, file):
79  self.styles |= set(styles)
80  key = self._key(styles)
81  self.files.setdefault(key, file)
82 
83  def filename(self, styles):
84  return self.files[self._key(styles)]
85 
86  def _key(self, styles):
87  if isinstance(styles, str):
88  return (styles,)
89  return tuple(sorted(styles))
90 
91  def __getitem__(self, styles):
92  key = self._key(styles)
93  if key in self._renderers:
94  return self._renderers[key]
95  fr = RawFontRenderer(self.files[key])
96  self._renderers[key] = fr
97  return fr
98 
99  def __repr__(self):
100  return "<SystemFont %s>" % self.family
101 
102 
103 class FontQuery:
104  """!
105  @see https://www.freedesktop.org/software/fontconfig/fontconfig-user.html#AEN21
106  https://manpages.ubuntu.com/manpages/cosmic/man1/fc-pattern.1.html
107  """
108  def __init__(self, str=""):
109  self._query = {}
110  if isinstance(str, FontQuery):
111  self._query = str._query.copy()
112  elif str:
113  chunks = str.split(":")
114  family = chunks.pop(0)
115  self._query = dict(
116  chunk.split("=")
117  for chunk in chunks
118  if chunk
119  )
120  self.family(family)
121 
122  def family(self, name):
123  self._query["family"] = name
124  return self
125 
126  def weight(self, weight):
127  self._query["weight"] = weight
128  return self
129 
130  def css_weight(self, weight):
131  """!
132  Weight from CSS weight value.
133 
134  Weight is different between CSS and fontconfig
135  This creates some interpolations to ensure known values are translated properly
136  @see https://www.freedesktop.org/software/fontconfig/fontconfig-user.html#AEN178
137  https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight#Common_weight_name_mapping
138  """
139  if weight < 200:
140  v = max(0, weight - 100) / 100 * 40
141  elif weight < 500:
142  v = -weight**3 / 200000 + weight**2 * 11/2000 - weight * 17/10 + 200
143  elif weight < 700:
144  v = -weight**2 * 3/1000 + weight * 41/10 - 1200
145  else:
146  v = (weight - 700) / 200 * 10 + 200
147  return self.weight(int(round(v)))
148 
149  def style(self, *styles):
150  self._query["style"] = " ".join(styles)
151  return self
152 
153  def charset(self, *hex_ranges):
154  self._query["charset"] = " ".join(hex_ranges)
155  return self
156 
157  def char(self, char):
158  return self.charset("%x" % ord(char))
159 
160  def custom(self, property, value):
161  self._query[property] = value
162  return self
163 
164  def clone(self):
165  return FontQuery(self)
166 
167  def __getitem__(self, key):
168  return self._query.get(key, "")
169 
170  def __contains__(self, item):
171  return item in self._query
172 
173  def get(self, key, default=None):
174  return self._query.get(key, default)
175 
176  def __str__(self):
177  return self._query.get("family", "") + ":" + ":".join(
178  "%s=%s" % (p, v)
179  for p, v in self._query.items()
180  if p != "family"
181  )
182 
183  def __repr__(self):
184  return "<FontQuery %r>" % str(self)
185 
186  def weight_to_css(self):
187  x = int(self["weight"])
188  if x < 40:
189  v = x / 40 * 100 + 100
190  elif x < 100:
191  v = x**3/300 - x**2 * 11/15 + x*167/3 - 3200/3
192  elif x < 200:
193  v = (2050 - 10 * math.sqrt(5) * math.sqrt(1205 - 6 * x)) / 3
194  else:
195  v = (x - 200) * 200 / 10 + 700
196  return int(round(v))
197 
198 
200  def __init__(self):
201  self.fonts = None
202 
203  def _lazy_load(self):
204  if self.fonts is None:
205  self.load()
206 
207  def load(self):
208  self.fonts = {}
209  self.load_fc_list()
210 
211  def cmd(self, *a):
212  p = subprocess.Popen(a, stdout=subprocess.PIPE)
213  out, err = p.communicate()
214  out = out.decode("utf-8").strip()
215  return out, p.returncode
216 
217  def load_fc_list(self):
218  out, returncode = self.cmd("fc-list", r'--format=%{file}\t%{family[0]}\t%{style[0]}\n')
219  if returncode == 0:
220  for line in out.splitlines():
221  file, family, styles = line.split("\t")
222  self._get(family).add_file(styles.split(" "), file)
223 
224  def best(self, query):
225  """!
226  Returns the renderer best matching the name
227  """
228  out, returncode = self.cmd("fc-match", r"--format=%{family}\t%{style}", str(query))
229  if returncode == 0:
230  return self._font_from_match(out)
231 
232  def _font_from_match(self, out):
233  fam, style = out.split("\t")
234  fam = fam.split(",")[0]
235  style = style.split(",")[0].split()
236  return self[fam][style]
237 
238  def all(self, query):
239  """!
240  Yields all the renderers matching a query
241  """
242  out, returncode = self.cmd("fc-match", "-s", r"--format=%{family}\t%{style}\n", str(query))
243  if returncode == 0:
244  for line in out.splitlines():
245  try:
246  yield self._font_from_match(line)
247  except (fontTools.ttLib.TTLibError, fontTools.t1Lib.T1Error):
248  pass
249 
250  def default(self):
251  """!
252  Returns the default fornt renderer
253  """
254  return self.best()
255 
256  def _get(self, family):
257  self._lazy_load()
258  if family in self.fonts:
259  return self.fonts[family]
260  font = SystemFont(family)
261  self.fonts[family] = font
262  return font
263 
264  def __getitem__(self, key):
265  self._lazy_load()
266  return self.fonts[key]
267 
268  def __iter__(self):
269  self._lazy_load()
270  return iter(self.fonts.values())
271 
272  def keys(self):
273  self._lazy_load()
274  return self.fonts.keys()
275 
276  def __contains__(self, item):
277  self._lazy_load()
278  return item in self.fonts
279 
280 
281 ## Dictionary of system fonts
283 
284 
286  if "GPOS" not in font:
287  return {}
288 
289  gpos_table = font["GPOS"].table
290 
291  unique_kern_lookups = set()
292  for item in gpos_table.FeatureList.FeatureRecord:
293  if item.FeatureTag == "kern":
294  feature = item.Feature
295  unique_kern_lookups |= set(feature.LookupListIndex)
296 
297  kerning_pairs = {}
298  for kern_lookup_index in sorted(unique_kern_lookups):
299  lookup = gpos_table.LookupList.Lookup[kern_lookup_index]
300  if lookup.LookupType in {2, 9}:
301  for pairPos in lookup.SubTable:
302  if pairPos.LookupType == 9: # extension table
303  if pairPos.ExtensionLookupType == 8: # contextual
304  continue
305  elif pairPos.ExtensionLookupType == 2:
306  pairPos = pairPos.ExtSubTable
307 
308  if pairPos.Format != 1:
309  continue
310 
311  firstGlyphsList = pairPos.Coverage.glyphs
312  for ps_index, _ in enumerate(pairPos.PairSet):
313  for pairValueRecordItem in pairPos.PairSet[ps_index].PairValueRecord:
314  secondGlyph = pairValueRecordItem.SecondGlyph
315  valueFormat = pairPos.ValueFormat1
316 
317  if valueFormat == 5: # RTL kerning
318  kernValue = "<%d 0 %d 0>" % (
319  pairValueRecordItem.Value1.XPlacement,
320  pairValueRecordItem.Value1.XAdvance)
321  elif valueFormat == 0: # RTL pair with value <0 0 0 0>
322  kernValue = "<0 0 0 0>"
323  elif valueFormat == 4: # LTR kerning
324  kernValue = pairValueRecordItem.Value1.XAdvance
325  else:
326  print(
327  "\tValueFormat1 = %d" % valueFormat,
328  file=sys.stdout)
329  continue # skip the rest
330 
331  kerning_pairs[(firstGlyphsList[ps_index], secondGlyph)] = kernValue
332  return kerning_pairs
333 
334 
336  def __init__(self, glyph, lsb, aw, xmin, xmax):
337  self.glyph = glyph
338  self.lsb = lsb
339  self.advance = aw
340  self.xmin = xmin
341  self.xmax = xmax
342  self.width = xmax - xmin
343  self.advance = xmax
344 
345  def draw(self, pen):
346  return self.glyph.draw(pen)
347 
348 
349 class Font:
350  def __init__(self, wrapped):
351  self.wrapped = wrapped
352  if isinstance(self.wrapped, fontTools.ttLib.TTFont):
353  self.cmap = self.wrapped.getBestCmap() or {}
354  else:
355  self.cmap = {}
356 
358 
359  @classmethod
360  def open(cls, filename):
361  try:
362  f = fontTools.ttLib.TTFont(filename)
363  except fontTools.ttLib.TTLibError:
364  f = fontTools.t1Lib.T1Font(filename)
365  f.parse()
366 
367  return cls(f)
368 
369  def getGlyphSet(self):
370  return self.wrapped.getGlyphSet()
371 
372  def getBestCmap(self):
373  return {}
374 
375  def glyph_name(self, codepoint):
376  if isinstance(codepoint, str):
377  if len(codepoint) != 1:
378  return ""
379  codepoint = ord(codepoint)
380 
381  if codepoint in self.cmap:
382  return self.cmap[codepoint]
383 
384  return self.calculated_glyph_name(codepoint)
385 
386  @staticmethod
387  def calculated_glyph_name(codepoint):
388  from fontTools import agl # Adobe Glyph List
389  if codepoint in agl.UV2AGL:
390  return agl.UV2AGL[codepoint]
391  elif codepoint <= 0xFFFF:
392  return "uni%04X" % codepoint
393  else:
394  return "u%X" % codepoint
395 
396  def scale(self):
397  if isinstance(self.wrapped, fontTools.ttLib.TTFont):
398  return 1 / self.wrapped["head"].unitsPerEm
399  elif isinstance(self.wrapped, fontTools.t1Lib.T1Font):
400  return self.wrapped["FontMatrix"][0]
401 
402  def yMax(self):
403  if isinstance(self.wrapped, fontTools.ttLib.TTFont):
404  return self.wrapped["head"].yMax
405  elif isinstance(self.wrapped, fontTools.t1Lib.T1Font):
406  return self.wrapped["FontBBox"][3]
407 
408  def glyph(self, glyph_name):
409  if isinstance(self.wrapped, fontTools.ttLib.TTFont):
410  glyph = self.glyphset[glyph_name]
411 
412  xmin = getattr(glyph._glyph, "xMin", glyph.lsb)
413  xmax = getattr(glyph._glyph, "xMax", glyph.width)
414  return GlyphMetrics(glyph, glyph.lsb, glyph.width, xmin, xmax)
415  elif isinstance(self.wrapped, fontTools.t1Lib.T1Font):
416  glyph = self.glyphset[glyph_name]
417  bounds_pen = ControlBoundsPen(self.glyphset)
418  bounds = bounds_pen.bounds
419  glyph.draw(bounds_pen)
420  if not hasattr(glyph, "width"):
421  advance = bounds[2]
422  else:
423  advance = glyph.width
424  return GlyphMetrics(glyph, bounds[0], advance, bounds[0], bounds[2])
425 
426  def __contains__(self, key):
427  if isinstance(self.wrapped, fontTools.t1Lib.T1Font):
428  return key in self.wrapped.font
429  return key in self.wrapped
430 
431  def __getitem__(self, key):
432  return self.wrapped[key]
433 
434 
436  tab_width = 4
437 
438  @property
439  def font(self):
440  raise NotImplementedError
441 
442  def get_query(self):
443  raise NotImplementedError
444 
445  def kerning(self, c1, c2):
446  return 0
447 
448  def text_to_chars(self, text):
449  return text
450 
451  def _on_missing(self, char, size, pos, group):
452  """!
453  - Character as string
454  - Font size
455  - [in, out] Character position
456  - Group shape
457  """
458 
459  def glyph_name(self, ch):
460  return self.font.glyph_name(ch)
461 
462  def scale(self, size):
463  return size * self.font.scale()
464 
465  def line_height(self, size):
466  return self.font.yMax() * self.scale(size)
467 
468  def ex(self, size):
469  return self.font.glyph("x").advance * self.scale(size)
470 
471  def glyph_beziers(self, glyph, offset=NVector(0, 0)):
472  pen = BezierPen(self.font.glyphset, offset)
473  glyph.draw(pen)
474  return pen.beziers
475 
476  def glyph_shapes(self, glyph, offset=NVector(0, 0)):
477  beziers = self.glyph_beziers(glyph, offset)
478  return [
479  Path(bez)
480  for bez in beziers
481  ]
482 
483  def _on_character(self, ch, size, pos, scale, line, use_kerning, chars, i):
484  chname = self.glyph_name(ch)
485 
486  if chname in self.font.glyphset:
487  glyphdata = self.font.glyph(chname)
488  #pos.x += glyphdata.lsb * scale
489  glyph_shapes = self.glyph_shapes(glyphdata, pos / scale)
490 
491  if glyph_shapes:
492  if len(glyph_shapes) > 1:
493  glyph_shape_group = line.add_shape(Group())
494  glyph_shape = glyph_shape_group
495  else:
496  glyph_shape_group = line
497  glyph_shape = glyph_shapes[0]
498 
499  for sh in glyph_shapes:
500  sh.shape.value.scale(scale)
501  glyph_shape_group.add_shape(sh)
502 
503  glyph_shape.name = ch
504 
505  kerning = 0
506  if use_kerning and i < len(chars) - 1:
507  nextcname = chars[i+1]
508  kerning = self.kerning(chname, nextcname)
509 
510  pos.x += (glyphdata.advance + kerning) * scale
511  return True
512  return False
513 
514  def render(self, text, size, pos=None, use_kerning=True, start_x=None):
515  """!
516  Renders some text
517 
518  @param text String to render
519  @param size Font size (in pizels)
520  @param[in,out] pos Text position
521  @param use_kerning Whether to honour kerning info from the font file
522  @param start_x x-position of the start of a line
523 
524  @returns a Group shape, augmented with some extra attributes:
525  - line_height Line height
526  - next_x X position of the next character
527  """
528  scale = self.scale(size)
529  line_height = self.line_height(size)
530  group = Group()
531  group.name = text
532  if pos is None:
533  pos = NVector(0, 0)
534  start_x = pos.x if start_x is None else start_x
535  line = Group()
536  group.add_shape(line)
537  #group.transform.scale.value = NVector(100, 100) * scale
538 
539  chars = self.text_to_chars(text)
540  for i, ch in enumerate(chars):
541  if ch == "\n":
542  line.next_x = pos.x
543  pos.x = start_x
544  pos.y += line_height
545  line = Group()
546  group.add_shape(line)
547  continue
548  elif ch == "\t":
549  chname = self.glyph_name(ch)
550  if chname in self.font.glyphset:
551  width = self.font.glyph(chname).advance
552  else:
553  width = self.ex(size)
554  pos.x += width * scale * self.tab_width
555  continue
556 
557  self._on_character(ch, size, pos, scale, line, use_kerning, chars, i)
558 
559  group.line_height = line_height
560  group.next_x = line.next_x = pos.x
561  return group
562 
563 
565  def __init__(self, filename):
566  self.filename = filename
567  self._font = Font.open(filename)
568  self._kerning = None
569 
570  @property
571  def font(self):
572  return self._font
573 
574  def kerning(self, c1, c2):
575  if self._kerning is None:
576  self._kerning = collect_kerning_pairs(self.font)
577  return self._kerning.get((c1, c2), 0)
578 
579  def __repr__(self):
580  return "<FontRenderer %r>" % self.filename
581 
582  def get_query(self):
583  return self.filename
584 
585 
587  def __init__(self, query, max_attempts=10):
588  self.query = FontQuery(query)
589  self._best = None
590  self._bq = None
591  self._fallback = {}
592  self.max_attempts = max_attempts
593 
594  @property
595  def font(self):
596  return self.best.font
597 
598  def get_query(self):
599  return self.query
600 
601  def ex(self, size):
602  best = self.best
603  if "x" not in self.font.glyphset:
604  best = fonts.best(self.query.clone().char("x"))
605  return best.ex(size)
606 
607  @property
608  def best(self):
609  cq = str(self.query)
610  if self._best is None or self._bq != cq:
611  self._best = fonts.best(self.query)
612  self._bq = cq
613  return self._best
614 
615  def fallback_renderer(self, char):
616  if char in self._fallback:
617  return self._fallback[char]
618 
619  if len(char) != 1:
620  return None
621 
622  codepoint = ord(char)
623  name = Font.calculated_glyph_name(codepoint)
624  for i, font in enumerate(fonts.all(self.query.clone().char(char))):
625  # For some reason fontconfig sometimes returns a font that doesn't
626  # actually contain the glyph
627  if name in font.font.glyphset or codepoint in font.font.cmap:
628  self._fallback[char] = font
629  return font
630 
631  if i > self.max_attempts:
632  self._fallback[char] = None
633  return None
634 
635  def _on_character(self, char, size, pos, scale, group, use_kerning, chars, i):
636  if self.best._on_character(char, size, pos, scale, group, use_kerning, chars, i):
637  return True
638 
639  font = self.fallback_renderer(char)
640  if not font:
641  return False
642 
643  child = font.render(char, size, pos)
644  if len(child.shapes) == 2:
645  group.add_shape(child.shapes[0])
646  else:
647  group.add_shape(child)
648 
649  def __repr__(self):
650  return "<FallbackFontRenderer %s>" % self.query
651 
652 
654  _split = None
655 
656  def __init__(self, wrapped, emoji_dir):
657  if not os.path.isdir(emoji_dir):
658  raise Exception("Not a valid directory: %s" % emoji_dir)
659  self.wrapped = wrapped
660  self.emoji_dir = emoji_dir
661  self._svgs = {}
662 
663  @property
664  def font(self):
665  return self.wrapped.font
666 
667  def _get_svg_filename(self, char):
668  basename = "-".join("%x" % ord(cp) for cp in char)
669  suffix = ".svg"
670 
671  filename = os.path.join(self.emoji_dir, basename + suffix)
672  if os.path.isfile(filename):
673  return basename, filename
674 
675  filename = os.path.join(self.emoji_dir, basename.upper() + suffix)
676  if os.path.isfile(filename):
677  return basename, filename
678 
679  if char and char[-1] == '\ufe0f':
680  return self._get_svg_filename(char[:-1])
681 
682  return None, None
683 
684  def _get_svg(self, char):
685  from ..parsers.svg import parse_svg_file
686 
687  if char in self._svgs:
688  return self._svgs[char]
689 
690  basename, filename = self._get_svg_filename(char)
691  if filename is None:
692  self._svgs[char] = None
693  return None
694 
695  svga = parse_svg_file(filename)
696  svgshape = Group()
697  svgshape.name = basename
698  for layer in svga.layers:
699  if isinstance(layer, ShapeLayer):
700  for shape in layer.shapes:
701  svgshape.add_shape(shape)
702 
703  self._svgs[char] = svgshape
704  svgshape._bbox = svgshape.bounding_box()
705  return svgshape
706 
707  def _on_character(self, char, size, pos, scale, group, use_kerning, chars, i):
708  svgshape = self._get_svg(char)
709  if svgshape:
710  target_height = self.line_height(size)
711  scale = target_height / svgshape._bbox.height
712  shape_group = Group()
713  shape_group = svgshape.clone()
714  shape_group.transform.scale.value *= scale
715  offset = NVector(
716  -svgshape._bbox.x1 + svgshape._bbox.width * 0.075,
717  -svgshape._bbox.y2 + svgshape._bbox.height * 0.1
718  )
719  shape_group.transform.position.value = pos + offset * scale
720  group.add_shape(shape_group)
721  pos.x += svgshape._bbox.width * scale
722  return True
723  return self.wrapped._on_character(char, size, pos, scale, group, use_kerning, chars, i)
724 
725  def get_query(self):
726  return self.wrapped.get_query()
727 
728  @staticmethod
729  def _get_splitter():
730  if EmojiRenderer._split is None:
731  try:
732  import grapheme
733  EmojiRenderer._split = grapheme.graphemes
734  except ImportError:
735  sys.stderr.write("Install `grapheme` for better Emoji support\n")
736  EmojiRenderer._split = lambda x: x
737  return EmojiRenderer._split
738 
739  @staticmethod
740  def emoji_split(string):
741  return EmojiRenderer._get_splitter()(string)
742 
743  def text_to_chars(self, string):
744  return list(self.emoji_split(string))
745 
746 
747 class FontStyle:
748  def __init__(self, query, size, justify=TextJustify.Left, position=None, use_kerning=True, emoji_svg=None):
749  self.emoji_svg = emoji_svg
750  self._set_query(query)
751  self.size = size
752  self.justify = justify
753  self.position = position.clone() if position else NVector(0, 0)
754  self.use_kerning = use_kerning
755 
756  def _set_query(self, query):
757  if isinstance(query, str) and os.path.isfile(query):
758  self._renderer = RawFontRenderer(query)
759  else:
760  self._renderer = FallbackFontRenderer(query)
761 
762  if self.emoji_svg:
763  self._renderer = EmojiRenderer(self._renderer, self.emoji_svg)
764 
765  @property
766  def query(self):
767  return self._renderer.get_query()
768 
769  @query.setter
770  def query(self, value):
771  if str(value) != str(self.query):
772  self._set_query(value)
773 
774  @property
775  def renderer(self):
776  return self._renderer
777 
778  def render(self, text, pos=NVector(0, 0)):
779  group = self._renderer.render(text, self.size, self.position+pos, self.use_kerning)
780  for subg in group.shapes[:-1]:
781  width = subg.next_x - self.position.x - pos.x
782  if self.justify == TextJustify.Center:
783  subg.transform.position.value.x -= width / 2
784  elif self.justify == TextJustify.Right:
785  subg.transform.position.value.x -= width
786  return group
787 
788  def clone(self):
789  return FontStyle(str(self.query), self.size, self.justify, NVector(*self.position), self.use_kerning)
790 
791  @property
792  def ex(self):
793  return self._renderer.ex(self.size)
794 
795  @property
796  def line_height(self):
797  return self._renderer.line_height(self.size)
798 
799 
800 def _propfac(a):
801  return property(lambda s: s._get(a), lambda s, v: s._set(a, v))
802 
803 
805  _props = [
806  LottieProp("query_string", "_query", str),
807  LottieProp("size", "_size", float),
808  LottieProp("justify", "_justify", TextJustify),
809  LottieProp("text", "_text", str),
810  LottieProp("position", "_position", NVector),
811  ]
812  wrapped_lottie = Group
813 
814  def __init__(self, text="", query="", size=64, justify=TextJustify.Left):
815  CustomObject.__init__(self)
816  if isinstance(query, FontStyle):
817  self.style = query
818  else:
819  self.style = FontStyle(query, size, justify)
820  self.text = text
821  self.hidden = None
822 
823  def _get(self, a):
824  return getattr(self.style, a)
825 
826  def _set(self, a, v):
827  return setattr(self.style, a, v)
828 
829  query = _propfac("query")
830  size = _propfac("size")
831  justify = _propfac("justify")
832  position = _propfac("position")
833 
834  @property
835  def query_string(self):
836  return str(self.query)
837 
838  @query_string.setter
839  def query_string(self, v):
840  self.query = v
841 
842  def _build_wrapped(self):
843  g = self.style.render(self.text)
844  self.line_height = g.line_height
845  return g
846 
847  def bounding_box(self, time=0):
848  return self.wrapped.bounding_box(time)
lottie.utils.font.FontRenderer._on_character
def _on_character(self, ch, size, pos, scale, line, use_kerning, chars, i)
Definition: font.py:483
lottie.objects.shapes.Group
ShapeElement that can contain other shapes.
Definition: shapes.py:430
lottie.utils.font.BezierPen.offset
offset
Definition: font.py:24
lottie.utils.font.GlyphMetrics.glyph
glyph
Definition: font.py:337
lottie.utils.font.FallbackFontRenderer
Definition: font.py:586
lottie.utils.font.SystemFont._key
def _key(self, styles)
Definition: font.py:86
lottie.utils.font.GlyphMetrics.width
width
Definition: font.py:342
lottie.utils.font.Font.scale
def scale(self)
Definition: font.py:396
lottie.utils.font.EmojiRenderer.wrapped
wrapped
Definition: font.py:659
lottie.utils.font._SystemFontList
Definition: font.py:199
lottie.utils.font.SystemFont.__repr__
def __repr__(self)
Definition: font.py:99
lottie.utils.font.FontQuery.weight_to_css
def weight_to_css(self)
Definition: font.py:186
lottie.utils.font.Font
Definition: font.py:349
lottie.utils.font.Font.__contains__
def __contains__(self, key)
Definition: font.py:426
lottie.utils.font.EmojiRenderer.__init__
def __init__(self, wrapped, emoji_dir)
Definition: font.py:656
lottie.utils.font.FallbackFontRenderer.font
def font(self)
Definition: font.py:595
lottie.utils.font.RawFontRenderer.filename
filename
Definition: font.py:566
lottie.utils.font.FallbackFontRenderer.best
def best(self)
Definition: font.py:608
lottie.utils.font.EmojiRenderer._svgs
_svgs
Definition: font.py:661
lottie.utils.font._SystemFontList._get
def _get(self, family)
Definition: font.py:256
lottie.utils.font.FontQuery.family
def family(self, name)
Definition: font.py:122
lottie.utils.font.RawFontRenderer.kerning
def kerning(self, c1, c2)
Definition: font.py:574
lottie.utils.font.Font.open
def open(cls, filename)
Definition: font.py:360
lottie.utils.font.Font.glyphset
glyphset
Definition: font.py:357
lottie.utils.font.FontShape
Definition: font.py:804
lottie.utils.font.EmojiRenderer.emoji_split
def emoji_split(string)
Definition: font.py:740
lottie.utils.font.FontRenderer.glyph_beziers
def glyph_beziers(self, glyph, offset=NVector(0, 0))
Definition: font.py:471
lottie.utils.font.collect_kerning_pairs
def collect_kerning_pairs(font)
Definition: font.py:285
lottie.utils.font.SystemFont._renderers
_renderers
Definition: font.py:76
lottie.utils.font.FontStyle.emoji_svg
emoji_svg
Definition: font.py:749
lottie.utils.font.SystemFont.styles
styles
Definition: font.py:75
lottie.utils.font.FontQuery.get
def get(self, key, default=None)
Definition: font.py:173
lottie.utils.font.Font.getBestCmap
def getBestCmap(self)
Definition: font.py:372
lottie.objects.bezier.Bezier
Single bezier curve.
Definition: bezier.py:123
lottie.utils.font.FallbackFontRenderer.__init__
def __init__(self, query, max_attempts=10)
Definition: font.py:587
lottie.utils.font.SystemFont.__init__
def __init__(self, family)
Definition: font.py:72
lottie.utils.font.BezierPen.__init__
def __init__(self, glyphSet, offset=NVector(0, 0))
Definition: font.py:20
lottie.utils.font.FontShape.hidden
hidden
Definition: font.py:821
lottie.utils.font.FontStyle.renderer
def renderer(self)
Definition: font.py:775
lottie.utils.font.RawFontRenderer.__repr__
def __repr__(self)
Definition: font.py:579
lottie.utils.font.FontShape.text
text
Definition: font.py:820
lottie.utils.font.FontQuery.clone
def clone(self)
Definition: font.py:164
lottie.utils.font.Font.glyph_name
def glyph_name(self, codepoint)
Definition: font.py:375
lottie.utils.font.BezierPen._point
def _point(self, pt)
Definition: font.py:26
lottie.utils.font.Font.glyph
def glyph(self, glyph_name)
Definition: font.py:408
lottie.utils.font.FontStyle.clone
def clone(self)
Definition: font.py:788
lottie.utils.font.FontStyle.size
size
Definition: font.py:751
lottie.utils.font.GlyphMetrics.xmax
xmax
Definition: font.py:341
lottie.utils.font.SystemFont.filename
def filename(self, styles)
Definition: font.py:83
lottie.utils.font.FallbackFontRenderer._best
_best
Definition: font.py:589
lottie.utils.font._SystemFontList.load
def load(self)
Definition: font.py:207
lottie.utils.font.SystemFont.add_file
def add_file(self, styles, file)
Definition: font.py:78
lottie.objects.shapes.Path
Animatable Bezier curve.
Definition: shapes.py:398
lottie.utils.font.FontStyle.__init__
def __init__(self, query, size, justify=TextJustify.Left, position=None, use_kerning=True, emoji_svg=None)
Definition: font.py:748
lottie.utils.font.Font.cmap
cmap
Definition: font.py:353
lottie.utils.font.EmojiRenderer._get_svg_filename
def _get_svg_filename(self, char)
Definition: font.py:667
lottie.utils.font.FontQuery.__init__
def __init__(self, str="")
Definition: font.py:108
lottie.utils.font.FontRenderer.tab_width
int tab_width
Definition: font.py:436
lottie.utils.font.EmojiRenderer.emoji_dir
emoji_dir
Definition: font.py:660
lottie.utils.font.GlyphMetrics.draw
def draw(self, pen)
Definition: font.py:345
lottie.utils.font.FontRenderer.glyph_shapes
def glyph_shapes(self, glyph, offset=NVector(0, 0))
Definition: font.py:476
lottie.utils.font.FontQuery.__getitem__
def __getitem__(self, key)
Definition: font.py:167
lottie.utils.font.FontRenderer.glyph_name
def glyph_name(self, ch)
Definition: font.py:459
lottie.utils.font.FontStyle.query
def query(self)
Definition: font.py:766
lottie.utils.font.FontQuery.__contains__
def __contains__(self, item)
Definition: font.py:170
lottie.parsers.svg.importer.parse_svg_file
def parse_svg_file(file, layer_frames=0, *args, **kwargs)
Definition: importer.py:1310
lottie.utils.font.FontStyle.justify
justify
Definition: font.py:752
lottie.utils.font.FallbackFontRenderer.ex
def ex(self, size)
Definition: font.py:601
lottie.utils.font.FontRenderer.text_to_chars
def text_to_chars(self, text)
Definition: font.py:448
lottie.utils.font.GlyphMetrics
Definition: font.py:335
lottie.utils.font.FontQuery
Definition: font.py:103
lottie.utils.font.FontQuery.__str__
def __str__(self)
Definition: font.py:176
lottie.utils.font._SystemFontList.best
def best(self, query)
Returns the renderer best matching the name.
Definition: font.py:224
lottie.utils.font.FontStyle
Definition: font.py:747
lottie.utils.font._SystemFontList.keys
def keys(self)
Definition: font.py:272
lottie.utils.font.FontRenderer.font
def font(self)
Definition: font.py:439
lottie.utils.font.FontStyle.position
position
Definition: font.py:753
lottie.objects.base.LottieProp
Lottie <-> Python property mapper.
Definition: base.py:88
lottie.utils.font.FontQuery.__repr__
def __repr__(self)
Definition: font.py:183
lottie.utils.font.RawFontRenderer.font
def font(self)
Definition: font.py:571
lottie.utils.font.SystemFont.family
family
Definition: font.py:73
lottie.utils.font.FontRenderer
Definition: font.py:435
lottie.objects.base.CustomObject
Allows extending the Lottie shapes with custom Python classes.
Definition: base.py:326
lottie.utils.font.FontShape.query_string
def query_string(self)
Definition: font.py:835
lottie.utils.font.GlyphMetrics.xmin
xmin
Definition: font.py:340
lottie.utils.font.RawFontRenderer
Definition: font.py:564
lottie.utils.font.EmojiRenderer.font
def font(self)
Definition: font.py:664
lottie.utils.font.FallbackFontRenderer.max_attempts
max_attempts
Definition: font.py:592
lottie.utils.font.FallbackFontRenderer._bq
_bq
Definition: font.py:590
lottie.utils.font.FontRenderer.ex
def ex(self, size)
Definition: font.py:468
lottie.utils.font.FontQuery.style
def style(self, *styles)
Definition: font.py:149
lottie.utils.font.FontStyle._set_query
def _set_query(self, query)
Definition: font.py:756
lottie.utils.font.FontRenderer.line_height
def line_height(self, size)
Definition: font.py:465
lottie.utils.font.SystemFont
Definition: font.py:71
lottie.utils.font.RawFontRenderer._kerning
_kerning
Definition: font.py:568
lottie.utils.font.FontRenderer.kerning
def kerning(self, c1, c2)
Definition: font.py:445
lottie.utils.font.FontQuery.css_weight
def css_weight(self, weight)
Weight from CSS weight value.
Definition: font.py:130
lottie.utils.font.FontStyle.use_kerning
use_kerning
Definition: font.py:754
lottie.utils.font.FontStyle._renderer
_renderer
Definition: font.py:758
lottie.utils.font.FontShape.style
style
Definition: font.py:817
lottie.utils.font._SystemFontList.cmd
def cmd(self, *a)
Definition: font.py:211
lottie.utils.font.FontQuery.char
def char(self, char)
Definition: font.py:157
lottie.utils.font._SystemFontList._lazy_load
def _lazy_load(self)
Definition: font.py:203
lottie.utils.font._SystemFontList.default
def default(self)
Returns the default fornt renderer.
Definition: font.py:250
lottie.utils.font.FallbackFontRenderer.query
query
Definition: font.py:588
lottie.utils.font.BezierPen.beziers
beziers
Definition: font.py:22
lottie.utils.font.FontQuery._query
_query
Definition: font.py:109
lottie.utils.font.FontQuery.custom
def custom(self, property, value)
Definition: font.py:160
lottie.utils.font.RawFontRenderer._font
_font
Definition: font.py:567
lottie.utils.font.FontStyle.render
def render(self, text, pos=NVector(0, 0))
Definition: font.py:778
lottie.utils.font.Font.calculated_glyph_name
def calculated_glyph_name(codepoint)
Definition: font.py:387
lottie.utils.font.FontRenderer.scale
def scale(self, size)
Definition: font.py:462
lottie.utils.font.Font.wrapped
wrapped
Definition: font.py:351
lottie.utils.font._SystemFontList._font_from_match
def _font_from_match(self, out)
Definition: font.py:232
lottie.utils.font.EmojiRenderer.text_to_chars
def text_to_chars(self, string)
Definition: font.py:743
lottie.utils.font.EmojiRenderer
Definition: font.py:653
lottie.utils.font.EmojiRenderer._get_svg
def _get_svg(self, char)
Definition: font.py:684
lottie.utils.font.FontShape.__init__
def __init__(self, text="", query="", size=64, justify=TextJustify.Left)
Definition: font.py:814
lottie.utils.font._SystemFontList.__iter__
def __iter__(self)
Definition: font.py:268
lottie.utils.font._SystemFontList.__getitem__
def __getitem__(self, key)
Definition: font.py:264
lottie.utils.font.SystemFont.files
files
Definition: font.py:74
lottie.utils.font.Font.__getitem__
def __getitem__(self, key)
Definition: font.py:431
lottie.utils.font.FallbackFontRenderer.get_query
def get_query(self)
Definition: font.py:598
lottie.utils.font.BezierPen._endPath
def _endPath(self)
Definition: font.py:32
lottie.utils.font._SystemFontList.all
def all(self, query)
Yields all the renderers matching a query.
Definition: font.py:238
lottie.utils.font.FontQuery.charset
def charset(self, *hex_ranges)
Definition: font.py:153
lottie.utils.font.RawFontRenderer.get_query
def get_query(self)
Definition: font.py:582
lottie.objects.bezier.BezierPoint
Definition: bezier.py:6
lottie.utils.font._SystemFontList.__init__
def __init__(self)
Definition: font.py:200
lottie.utils.font.FontStyle.line_height
def line_height(self)
Definition: font.py:796
lottie.utils.font.FontQuery.weight
def weight(self, weight)
Definition: font.py:126
lottie.utils.font.GlyphMetrics.__init__
def __init__(self, glyph, lsb, aw, xmin, xmax)
Definition: font.py:336
lottie.utils.font.FontShape.bounding_box
def bounding_box(self, time=0)
Definition: font.py:847
lottie.utils.font.RawFontRenderer.__init__
def __init__(self, filename)
Definition: font.py:565
lottie.utils.font.BezierPen
Definition: font.py:19
lottie.utils.font.FontShape.line_height
line_height
Definition: font.py:844
lottie.utils.font.GlyphMetrics.advance
advance
Definition: font.py:339
lottie.utils.font.FallbackFontRenderer.fallback_renderer
def fallback_renderer(self, char)
Definition: font.py:615
lottie.utils.font.FontRenderer.get_query
def get_query(self)
Definition: font.py:442
lottie.utils.font._SystemFontList.__contains__
def __contains__(self, item)
Definition: font.py:276
lottie.utils.font._SystemFontList.load_fc_list
def load_fc_list(self)
Definition: font.py:217
lottie.utils.font.Font.getGlyphSet
def getGlyphSet(self)
Definition: font.py:369
lottie.utils.font.EmojiRenderer.get_query
def get_query(self)
Definition: font.py:725
lottie.utils.font.BezierPen.current
current
Definition: font.py:23
lottie.utils.font.FontShape.query
query
Definition: font.py:829
lottie.utils.font.FallbackFontRenderer.__repr__
def __repr__(self)
Definition: font.py:649
lottie.utils.font.GlyphMetrics.lsb
lsb
Definition: font.py:338
lottie.utils.font.FontStyle.ex
def ex(self)
Definition: font.py:792
lottie.utils.font.FallbackFontRenderer._fallback
_fallback
Definition: font.py:591
lottie.utils.font.SystemFont.__getitem__
def __getitem__(self, styles)
Definition: font.py:91
lottie.utils.font.Font.__init__
def __init__(self, wrapped)
Definition: font.py:350
lottie.objects.base.CustomObject.wrapped
wrapped
Definition: base.py:333
lottie.utils.font._SystemFontList.fonts
fonts
Definition: font.py:201
lottie.utils.font.FontRenderer.render
def render(self, text, size, pos=None, use_kerning=True, start_x=None)
Renders some text.
Definition: font.py:514
lottie.utils.font.Font.yMax
def yMax(self)
Definition: font.py:402
lottie.nvector.NVector
Definition: nvector.py:9