decorators

  • renderable

    class renderable:
        def __init__(self,
            rect=(1080, 1080),
            bg=None,
            fmt="png",
            name=None,
            rasterizer=None,
            prefix=None,
            suffix=None,
            dst=None,
            custom_folder=None,
            postfn=None,
            watch=[],
            watch_soft=[],
            watch_restart=[],
            solo=False,
            mute=False,
            rstate=False,
            preview_only=False,
            render_only=False,
            direct_draw=False,
            clip=False,
            composites=False,
            single_frame=True,
            interactable=False,
            cv2caps=None,
            render_bg=True,
            style="_default",
            viewBox=True,
            layer=False,
            cond=None,
            sort=0,
            hide=[],
            grid=None,
            xray=True,
            memory=None,
            reset_memory=None):
    

    Base class for any content renderable by Coldtype.

  • animation

    class animation:
        def __init__(self,
            rect=(1080, 1080),
            timeline:Timeline=10,
            show_frame=True,
            offset=0,
            overlay=True,
            audio=None,
            suffixer=None,
            clip_cursor=True,
            reset_to_zero=False,
            release=None,
            **kwargs
            ):
    

    Base class for any frame-wise animation animatable by Coldtype

classes

  • P

    class P:
        def __init__(self, *vals, **kwargs):
    

    P stands for Path (or Pen)

    • align

      def align(self,
          rect,
          x="mdx",
          y="mdy",
          th=None, # deprecated
          tv=None, # deprecated
          tx=1,
          ty=0,
          transformFrame=True,
          h=None,
          returnOffset=False
          ) 
      

      Align this pen to another rect, defaults to the center.

      • tx means true-x (i.e. will disregard any invisible ‘frame’ set on the pen (as in the case of glyphs returned from StSt/Glyphwise));
      • ty means true-y, which is the same but for the vertical dimension
    • ambit

      def ambit(self, th=None, tv=None, tx=0, ty=0, t=None) 
      

      Get the calculated rect boundary.

      • tx means (t)rue (x) (i.e. the true width/horizontal dimension (was previously th));
      • ty means (t)rue (y) (i.e. the true height/vertical dimension (was previously tv));

      Passing either ignores a non-bounds-derived frame in either dimension

    • bounds

      def bounds(self):
          """Calculate the exact bounds of this shape, using a BoundPen"""
          b = Rect(0, 0, 0, 0)
      
          if self.val_present():
              try:
                  cbp = BoundsPen(None)
                  self._val.replay(cbp)
                  mnx, mny, mxx, mxy = cbp.bounds
                  b = Rect((mnx, mny, mxx - mnx, mxy - mny))
              except:
                  pass
      
          if len(self._els) > 0:
              bs = []
              for el in self._els:
                  eb = el.bounds()
                  if eb and eb.nonzero():
                      bs.append(eb)
      
              if len(bs) > 0:
                  b = bs[0]
                  for eb in bs[1:]:
                      b = b.union(eb)
      
          return b
      

      Calculate the exact bounds of this shape, using a BoundPen

    • catmull

      def catmull(self, points, close=False) 
      

      Run a catmull spline through a series of points

    • difference

      def difference(self, otherPen=None) 
      

      Calculate and return the difference of this shape and another.

    • dots

      def dots(self, radius=4, square=False):
          """(Necessary?) Create circles at moveTo commands"""
          dp = type(self)()
          for t, pts in self.v.value:
              if t == "moveTo":
                  x, y = pts[0]
                  if square:
                      dp.rect(Rect((x-radius, y-radius, radius, radius)))
                  else:
                      dp.oval(Rect((x-radius, y-radius, radius, radius)))
          self.v.value = dp.v.value
          return self
      

      (Necessary?) Create circles at moveTo commands

    • down

      def down(self):
          return self.pen()
      

      down one level of hierarchy — unimplemented by Runon

    • explode

      def explode(self):
          """Convert all contours to individual paths"""
          for el in self._els:
              el.explode()
      
          if self.val_present():
              rp = RecordingPen()
              ep = ExplodingPen(rp)
              self.replay(ep)
      
              for p in ep._pens:
                  el = type(self)()
                  el._val.value = p
                  el._attrs = deepcopy(self._attrs)
                  self.append(el)
      
              self._val = RecordingPen()
      
          return self
      

      Convert all contours to individual paths

    • f

      def f(self, *value) 
      

      Get/set a (f)ill

    • flatten

      def flatten(self, length=10, segmentLines=True) 
      

      Runs a fontTools FlattenPen on this pen

    • glyph

      def glyph(self, glyph, glyphSet=None, layerComponents=False) 
      

      Play a glyph (like from defcon) into this pen.

    • gridlayer

      def gridlayer(self, nx, ny=None, track=0, lead=0) 
      

      Spread nx copies and then stack ny copies, w/ optional tracking & leading

    • gridlines

      def gridlines(self, rect, x=20, y=None, absolute=False) 
      

      Construct a grid in the pen using x and (optionally) y subdivisions

    • hull

      def hull(self, points) 
      

      Same as .line but calls closePath instead of endPath`

    • img

      def img(self, src=None, rect=Rect(0, 0, 500, 500), pattern=False, opacity=1.0):
          """Get/set an image fill"""
          if src:
              from coldtype.img.datimage import DATImage
              if isinstance(src, DATImage):
                  return self.attr(image=dict(src=src.src, rect=rect, pattern=pattern, opacity=opacity))
              return self.attr(image=dict(src=src, rect=rect, pattern=pattern, opacity=opacity))
          else:
              return self.attr(field="image")
      

      Get/set an image fill

    • intersection

      def intersection(self, otherPen=None) 
      

      Calculate and return the intersection of this shape and another.

    • lead

      def lead(self, leading) 
      

      Vertical spacing

    • length

      def length(self, t=1):
      
          """Get the length of the curve for time `t`"""
          cc = CurveCutter(self)
          start = 0
          tv = t * cc.calcCurveLength()
          return tv
      

      Get the length of the curve for time t

    • line

      def line(self, points, moveTo=True, endPath=True) 
      

      Syntactic sugar for moveTo+lineTo(…)+endPath; can have any number of points

    • normalize_attr_value

      def normalize_attr_value(self, k, v):
          if k == "fill" and not isinstance(v, Color):
              return normalize_color(v)
          else:
              return super().normalize_attr_value(k, v)
      

      subclass hook

    • outline

      def outline(self,
          offset=1,
          drawInner=True,
          drawOuter=True,
          cap="square",
          miterLimit=None,
          closeOpenPaths=True
          ) 
      

      AKA expandStroke

    • oval

      def oval(self, rect) 
      

      Oval primitive

    • pen

      def pen(self):
          """collapse and combine into a single vector"""
          if len(self) == 0:
              return self
      
          frame = self.ambit()
          self.collapse()
      
          for el in self._els:
              el._val.replay(self._val)
              #self._val.record(el._val)
      
          self._attrs = {**self._els[0]._attrs, **self._attrs}
      
          self.data(frame=frame)
          self._els = []
          return self
      

      collapse and combine into a single vector

    • point_t

      def point_t(self, t=0.5):
      
          """Get point value for time `t`"""
          cc = CurveCutter(self)
          start = 0
          tv = t * cc.calcCurveLength()
          p, tangent = cc.subsegmentPoint(start=0, end=tv)
          return p, tangent
      

      Get point value for time t

    • printable_data

      def printable_data(self):
          out = {}
          exclude = ["_last_align_rect", "_notebook_shown"]
          for k, v in self._data.items():
              if k not in exclude:
                  out[k] = v
          return out
      

      subclass hook for repr

    • printable_val

      def printable_val(self):
          if self.val_present():
              return f"{len(self._val.value)}mvs"
      

      subclass hook for repr

    • record

      def record(self, pen) 
      

      Play a pen into this pen, meaning that pen will be added to this one’s value.

    • rect

      def rect(self, rect) 
      

      Rectangle primitive — moveTo/lineTo/lineTo/lineTo/closePath

    • removeOverlap

      def removeOverlap(self):
          """Remove overlaps within this shape and return itself."""
          return self._pathop(otherPen=None, operation=BooleanOp.Simplify)
      

      Remove overlaps within this shape and return itself.

    • reverse

      def reverse(self, recursive=False, winding=True):
          """Reverse elements; if pen value present, reverse the winding direction of the pen."""
          if winding and self.val_present():
              if self.unended():
                  self.closePath()
              dp = RecordingPen()
              rp = ReverseContourPen(dp)
              self.replay(rp)
              self._val.value = dp.value
              return self
      
          return super().reverse(recursive=recursive, winding=winding)
      

      Reverse elements; if pen value present, reverse the winding direction of the pen.

    • reverseDifference

      def reverseDifference(self, otherPen=None) 
      

      Calculate and return the reverseDifference of this shape and another.

    • reversePens

      def reversePens(self):
          """for backwards compatibility"""
          return self.reverse(recursive=False)
      

      for backwards compatibility

    • rotate

      def rotate(self, degrees, point=None, th=None, tv=None, tx=1, ty=1, **kwargs) 
      

      Rotate this shape by a degree (in 360-scale, counterclockwise).

    • roughen

      def roughen(self, amplitude=10, threshold=10, ignore_ends=False, seed=None) 
      

      Randomizes points in skeleton

    • round

      def round(self):
          """Round the values of this pen to integer values."""
          return self.round_to(1)
      

      Round the values of this pen to integer values.

    • round_to

      def round_to(self, rounding) 
      

      Round the values of this pen to nearest multiple of rounding.

    • roundedRect

      def roundedRect(self, rect, hr, vr=None, scale=True) 
      

      Rounded rectangle primitive

    • s

      def s(self, *value) 
      

      Get/set a (s)troke

    • scale

      def scale(self, scaleX, scaleY=None, point=None, th=None, tv=None, tx=1, ty=0, **kwargs) 
      

      Scale this shape by a percentage amount (1-scale).

    • scaleToHeight

      def scaleToHeight(self, h, shrink_only=False) 
      

      Scale this shape horizontally

    • scaleToRect

      def scaleToRect(self, rect, preserveAspect=True, shrink_only=False, tx=1, ty=0) 
      

      Scale this shape into a Rect.

    • scaleToWidth

      def scaleToWidth(self, w, shrink_only=False) 
      

      Scale this shape horizontally

    • sf

      def sf(self, value=None) 
      

      strokeFirst

    • sm

      def sm(self, value=None) 
      

      strokeMiter

    • spread

      def spread(self, tracking=0, tx=0, zero=False) 
      

      Horizontal distribution of elements

    • stack

      def stack(self, leading=0, ty=0, zero=False) 
      

      Vertical distribution of elements

    • strokeFirst

      def strokeFirst(self, value=None) 
      

      For a rendering engine that has to stroke and fill in two separate passes, perform the stroke before the fill (akin to an .understroke but without the duplication overhead)

    • strokeMiter

      def strokeMiter(self, value=None) 
      

      For a rendering engine that can specify stroke-miter

    • style

      def style(self, style="_default"):
          """for backwards compatibility with defaults and grouped-stroke-properties"""
          st = {**super().style(style)}
          return self.groupedStyle(st)
      

      for backwards compatibility with defaults and grouped-stroke-properties

    • subsegment

      def subsegment(self, start=0, end=1) 
      

      Return a subsegment of the pen based on t values start and end

    • sw

      def sw(self, value) 
      

      Get/set a (s)troke (w)idth

    • toGlyph

      def toGlyph(self, name=None, width=None, allow_blank=False):
      
          """
          Create a glyph (like from `defcon`) using this pen’s value.
          *Warning*: if path is unended, closedPath will be called
          """
          from defcon import Glyph
          if not allow_blank:
              if self.unended():
                  self.closePath()
          bounds = self.bounds()
          glyph = Glyph()
          glyph.name = name
          glyph.width = width or bounds.w
          try:
              sp = glyph.getPen()
              self.replay(sp)
          except AssertionError:
              if not allow_blank:
                  print(">>>blank glyph:", glyph.name)
          return glyph
      

      Create a glyph (like from defcon) using this pen’s value. Warning: if path is unended, closedPath will be called

    • track

      def track(self, t, v=False) 
      

      Track-out/distribute elements

    • track_to_rect

      def track_to_rect(self, rect, pullToEdges=False, r=0) 
      

      Distribute pens evenly within a frame

    • track_with_width

      def track_with_width(self, t) 
      

      Track-out/distribute elements

    • transform

      def transform(self, transform, transformFrame=True) 
      

      Perform an arbitrary transformation on the pen, using the fontTools Transform class.

    • translate

      def translate(self, x, y=None, transformFrame=True) 
      

      Translate this shape by x and y (pixel values).

    • union

      def union(self, otherPen=None) 
      

      Calculate and return the union of this shape and another.

    • val_present

      def val_present(self):
          return self._val is not None and len(self._val.value) > 0
      

      subclass hook

    • xor

      def xor(self, otherPen=None) 
      

      Calculate and return the XOR of this shape and another.

  • Style

    class Style:
        def __init__(self,
            font:Union[Font, str]=None,
            font_size:int=12,
            tracking=0,
            trackingMode=1,
            kern_pairs=dict(),
            space=None,
            baselineShift=0,
            xShift=None,
            rotate=0,
            reverse=False,
            removeOverlap=False,
            q2c=False,
            lang=None,
            narrower=None,
            fallback=None,
            palette=0,
            capHeight=None,
            ascender=None,
            descender=None,
            metrics="c",
            data={},
            layer=None,
            liga=True,
            kern=True,
            fill=rgb(0, 0.5, 1),
            stroke=None,
            strokeWidth=0,
            variations=dict(),
            variationLimits=dict(),
            trackingLimit=0,
            scaleVariations=True,
            rollVariations=False,
            mods=None,
            features=dict(),
            increments=dict(),
            varyFontSize=False,
            preventHwid=False,
            fitHeight=None,
            meta=dict(),
            no_shapes=False,
            show_frames=False,
            load_font=True, # should we attempt to load the font?
            tag=None, # way to differentiate in __eq__
            _stst=False,
            **kwargs
            ):
    

    Class for configuring font properties

    Keyword arguments

    • font: can either be a coldtype.text.Font object, a pathlib.Path, or a plain string path
    • font_size: standard point-based font-size, expressed as integer
    • tracking (aka tu): set the tracking, by default in font-source-point-size aka as if the font-size was always 1000; this means tracking is by default done relatively rather than absolutely (aka the relative tracking will not change when you change the font_size)
    • trackingMode: set to 0 to set tracking in a classic font_size-based (defaults to 1, as described just above)
    • space: set this to override the width of the standard space character (useful when setting text on a curve and the space is getting collapsed)
    • baselineShift (aka bs): if an integer, shifts glyphs by that amount in y axis; if a list, shifts glyphs at corresponding index in list by that amount in y axis
    • xShift (aka xs): if an integer, shifts glyphs by that amount in x axis; if a list, shifts glyphs at corresponding index in list by that amount in x axis
    • rotate: rotate glyphs by degree
    • reverse (aka r): reverse the order of the glyphs, so that the left-most glyph is first in when vectorized via .pens()
    • removeOverlaps (aka ro): automatically use skia-pathops to remove overlaps from the glyphs (useful when using variable ttf fonts)
    • lang: set language directly, to access language-specific alternate characters/rules

    Shorthand kwargs

    • kp for kern_pairs — a dict of glyphName->[left,right] values in font-space
    • tl for trackingLimit
    • bs for baselineShift
    • ch for capHeight — a number in font-space; not specified, read from font; specified as ‘x’, capHeight is set to xHeight as read from font
    • mod

      def mod(self, **kwargs):
          """Modify this style object to create a new
          one; kwargs can have all of the same kwargs as
          the standard `Style` constructor"""
          keyed = dict(**self.input, **self.input["kwargs"])
          del keyed["kwargs"]
          del keyed["self"]
          keyed.update(kwargs)
          return Style(**keyed)
      

      Modify this style object to create a new one; kwargs can have all of the same kwargs as the standard Style constructor

  • Rect

    class Rect:
        def __init__(self, *rect):
    

    Representation of a rectangle as (x, y, w, h), indexable

    Constructor handles multiple formats, including:

    • x, y, w, h
    • [x, y, w, h]
    • w, h (x and y default to 0, 0)

    Rect objects can be splat’d where lists are expected as individual arguments (as in drawBot), i.e. rect(*my_rect), or can be passed directly to functions expected a list representation of a rectangle.

    • FromCenter

      def FromCenter(center, w, h=None) 
      

      Create a rect given a center point and a width and height (optional, height will default to width if not specified”)

    • FromMnMnMxMx

      def FromMnMnMxMx(extents) 
      

      Create a rectangle from xmin, ymin, xmax, ymax

    • divide

      def divide(self, amount, edge, forcePixel=False) 
      

      Dividing

      Derived from the behavior of the classic Cocoa function CGRectDivide, which takes a rectangle and breaks it into two pieces, based on a pixel amount and an edge.

      A quick example: assume you have a rectangle, r, defined as such:

      r = Rect(0, 0, 300, 100)

      If you want to break that into a left-hand rectangle that’s 100 pixels wide and a right-hand rectangle that’s 200 pixels wide, you could either say:

      left, right = r.divide(100, "mnx")

      or you could say

      right, left = r.divide(200, "mxx")

      where mxx is the rightmost edge, and mnx is the leftmost edge.

      Centering

      A special use-case is if you want to break a rectangle into three rectangles, based on the center “edge”, you can do something like this:

      left, center, right = r.divide(200, "mdx")

      This will result in three rectangles, always left-to-right, where left is 50px wide, then center is 200px wide, then right is also 50px wide — anything not in the center will be evenly distributed between left and right, or top-and-bottom in the case of a Y edge.

    • grid

      def grid(self, columns=2, rows=2) 
      

      Construct a grid

    • inset

      def inset(self, dx, dy=None) 
      

      Creates padding in the amount of dx and dy. Also does expansion with negative values, or both at once

    • interp

      def interp(self, v, other) 
      

      Interpolate with another rect

    • ipos

      def ipos(self, pt, defaults=(0.5, 0.5), clamp=True) 
      

      Get scaled 0-1 bounded (optional) value from a point in a rectangle

    • mnmnmxmx

      def mnmnmxmx(self) 
      

      Return extents of rectangle as list

    • nonzero

      def nonzero(self) 
      

      is this rect not just all zeros?

    • origin

      def origin(self) 
      

      (x, y) as tuple

    • point

      def point(self, eh, ev=Edge.MinX) 
      

      Get a Point at a given compass direction, chosen from

      • C
      • W
      • NW
      • N
      • NE
      • E
      • SE
      • S
      • SW
    • rect

      def rect(self) 
      

      x,y,w,h in list

    • round

      def round(self) 
      

      round the values in the rectangle to the nearest integer

    • square

      def square(self) 
      

      take a square from the center of this rect

    • subdivide

      def subdivide(self, amount, edge) 
      

      Like divide, but here you specify the number of equal pieces you want (like columns or rows), and then what edge to start at, i.e.

      .. code:: python

      r = Rect(0, 0, 500, 100)
      r.subdivide(5, "mxx")
      => [Rect([400.0, 0, 100.0, 100]), Rect([300.0, 0, 100.0, 100]), Rect([200.0, 0, 100.0, 100]), Rect([100.0, 0, 100.0, 100]), Rect([0, 0, 100.0, 100])]
      

      will get you five 100-px wide rectangles, right-to-left

      (N.B. Does not support center edges, as that makes no sense)

    • subdivide_with_leading

      def subdivide_with_leading(self, count, leading, edge, forcePixel=True) 
      

      Same as subdivide, but inserts leading between each subdivision

    • subdivide_with_leadings

      def subdivide_with_leadings(self, count, leadings, edge, forcePixel=True) 
      

      Same as subdivide_with_leadings, but inserts leading between each subdivision, indexing the size of the leading from a list of leadings

    • subtract

      def subtract(self, amount, edge) 
      

      The opposite of take, this will remove and not return a piece of the given amount from the given edge.

      Let’s say you have a 100px-wide square and you want to drop 10px from the right-hand side, you would do:

      Rect(100, 100).subtract(10, Edge.MaxX), which leaves you with Rect([0, 0, 90, 100])

    • take

      def take(self, amount, edge, forcePixel=False) 
      

      Like divide, but here it just returns the “first” rect from a divide call, not all the resulting pieces, i.e. you can “take” 200px from the center of a rectangle by doing this Rect(0, 0, 300, 100).take(200, "mdx") which will result in Rect([50, 0, 200, 100])

    • wh

      def wh(self) 
      

      the width and height as a tuple

    • xy

      def xy(self) 
      

      equivalent to origin

    • zero

      def zero(self) 
      

      disregard origin and set it to (0,0)

functions

  • StSt

    def StSt(text,
        font,
        font_size=24,
        rect=Rect(1080, 1080),
        strip=False,
        multiline=False,
        lead=True,
        #xa="mdx",
        **kwargs) 
    

    Set a line of text with a single Style object, passed either with it’s constituent parts (i.e. kwargs) or as an actual Style object.

    Examples:

    @renderable()
    def stst_1(r):
        # here the styling arguments are "flat"
        return (StSt("COLDTYPE", Font.ColdObvi(), 100, wdth=0)
            .align(r))
    
    @renderable()
    def stst_2(r):
        # here the styling arguments are encapsulated in the Style object
        return (StSt("COLDTYPE", Style(Font.ColdObvi(), 100, wdth=1))
            .align(r))
    
  • Glyphwise

    def Glyphwise(st:str
        , styler:Callable[[GlyphwiseGlyph], Style]
        , start:int=0
        , line:int=0
        , multiline=False) 
    

    Build text by applying unique style to each glyph.

    Style is determined by a styler function (usually a lambda) that is given a GlyphwiseGlyph containing information about the glyph and its position (index, etc.); styler function must return a Style object to be used for styling

    Examples:

    @renderable()
    def glyphwise(r):
        return (Glyphwise("COLDTYPE", lambda x:
            Style(Font.ColdObvi(), 200, wdth=x.e))
            .align(r))