Navigation

    • Register
    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Users
    • Groups
    • Solved
    • Unsolved
    • Search
    1. Home
    2. gferreira
    3. Posts
    • Profile
    • Following
    • Followers
    • Topics
    • Posts
    • Best
    • Groups

    Posts made by gferreira

    • RE: Accessing opsz fontVariation

      hello @craig,

      it looks like variable optical sizes are selected automatically based on the point size. you can then use scale() to make text bigger or smaller:

      opsz.png

      ttfPath = 'SourceSerif4Variable-Roman.ttf'
      txt = 'Handgloves'
      
      font(ttfPath)
      translate(20, 100)
      
      with savedState():
          scale(40)
          fontSize(6)
          text(txt, (0, 0))
      
      translate(0, 300)
      with savedState():
          scale(13)
          fontSize(19)
          text(txt, (0, 0))
      
      translate(0, 300)
      with savedState():
          scale(4)
          fontSize(72)
          text(txt, (0, 0))
      

      hope this helps! cheers

      posted in Bugs
      gferreira
      gferreira
    • RE: Variable Font Animation Jiggle

      hello @paul,

      I’ve adapted your script to use the Skia font, and was able to reproduce the issue. I think it‘s related to hinting and text rasterization on low resolutions.

      you can reduce the jiggle effect by converting the text to curves – that is, setting it with BezierPath instead of text().

      the script below generates two movies for comparison, one with each approach.

      w = 1080
      h = 1080
      customFont = 'Skia'
      nframes = 500
      fps = 30
      
      var = listFontVariations(customFont)
      varMin = var['wght']['minValue']
      varMax = var['wght']['maxValue']
      
      for mode in range(2):
          for i in range(nframes):
              newPage(w, h)
              frameDuration(1.0/fps)
              fill(1)
              rect(0, 0, w, h)
              fill(0)    
              wt = varMin + i * ((varMax - varMin) / nframes)
      
              # mode 0: text as text
              if mode == 0:
                  font(customFont, 200)
                  fontVariations(wght=wt)
                  text("Hamburg", (w/2, h/2-100), align="center")
              
              # mode 1: text as bezierPath
              else:
                  txt = FormattedString()
                  txt.append("Hamburg", font=customFont, fontSize=200, fontVariations=dict(wght=wt))
                  B = BezierPath()
                  B.text(txt, (w/2, h/2-100), align="center")
                  drawPath(B)
          
          saveImage(f"animation-{mode}.mp4")
          newDrawing()
      

      hope this helps! cheers

      posted in Bugs
      gferreira
      gferreira
    • RE: QRCodeGenerator(size, message)

      hello @eduairet,

      when working with QRCodeGenerator:

      • data must be in bytes
      • size must be a tuple

      this works:

      path = bytes('http://www.drawbot.com/', 'utf-8')
      
      w, h = 31, 31
      
      img = ImageObject()
      img.QRCodeGenerator((w, h), path, 'Q')
      
      size(w, h)
      image(img, (0, 0))
      

      the third parameter (L, M, Q or H) determines the correction level, see the Core Image docs for more info.

      hope this helps! cheers

      ps. the resulting image is tiny (1 pixel per bit). you can scale it by reading the pixel values and redrawing it with shapes:

      path = bytes('http://www.drawbot.com/', 'utf-8')
      
      w, h = 31, 31
      s = 10
      
      img = ImageObject()
      img.QRCodeGenerator((w, h), path, 'Q')
      
      size(w*s, h*s)
      for x in range(w):
          for y in range(h):
              c = imagePixelColor(img, (x, y))
              fill(*c)
              rect(x*s, y*s, s, s)
      
      posted in General Discussion
      gferreira
      gferreira
    • RE: Markdown syntax for FormattedString?

      hello @djrrb,

      I think this error was introduced in markdown 3.3 (new htmlparser).

      I don’t know how to fix it, but you can install an earlier version of markdown which still works:

      pip install markdown==3.2.2
      

      cheers

      posted in Feature Requests
      gferreira
      gferreira
    • RE: Boustrophedonic typesetting

      @imik glad to hear that. cheers!!

      posted in Code snippets
      gferreira
      gferreira
    • RE: Reading pixel values (rgb or brightness) on coordinations

      hello @snaders,

      yes, you can read image pixel values with imagePixelColor(). it’s a bit slow though, use it with caution… good luck!

      posted in General Discussion
      gferreira
      gferreira
    • Boustrophedonic typesetting

      boustrophedonic.png

      txt = "Boustrophedon is a type of bi-directional text, mostly seen in ancient manuscripts and other inscriptions. Alternate lines of writing are flipped, or reversed, with reversed letters. Rather than going left-to-right as in modern European languages, or right-to-left as in Arabic and Hebrew, alternate lines in boustrophedon must be read in opposite directions. Also, the individual characters are reversed, or mirrored. It was a common way of writing in stone in ancient Greece.".upper()
      
      
      def boustrophedonicTextBox(txt, box, drawFrame=False):
      
          x, y, w, h = box
          L = fontLineHeight()
      
          save()
          translate(x, y-L)
      
          T = ''
          lineLength = 0
          backward = False
          words = txt.split()
          lineCount = 0
          overflow = ''
      
          for i, word in enumerate(words):
      
              # add word to current line
              if lineLength + textSize(word)[0] <= w:
                  T += word + ' '
                  lineLength += textSize(word + ' ')[0]
      
              # break to next line
              else:
                  # no more space for new line
                  if not (lineCount+1) * L < h:
                      overflow = T + ' '.join(words[i:])
                      break
      
                  # draw current line
                  with savedState():
                      # flip direction
                      if backward:
                          scale(-1, 1)
                      text(T, (0, 0), align='center')
      
                  # begin new line
                  translate(0, -L)
                  backward = not backward
                  lineCount += 1
                  T = word + ' '
                  lineLength = textSize(word + ' ')[0]
      
              # draw last line
              if i == len(words)-1:
                  lineCount += 1
                  with savedState():
                      if backward:
                          scale(-1, 1)
                      text(T, (0, 0), align='center')
      
          restore()
      
          if drawFrame:
              with savedState():
                  fill(None)
                  stroke(1, 0, 0)
                  rect(x - w/2, y, w, -h)
      
          return overflow
      
      
      font('Skia')
      fontSize(36)
      lineHeight(66)
      txt = boustrophedonicTextBox(txt, (width()/2, height()-100, width()-80, 800), drawFrame=False)
      
      print(txt)
      
      posted in Code snippets
      gferreira
      gferreira
    • RE: Different fonts in a text box

      hello @mït,

      I think I understand what you mean – it’s not so easy because, once a piece of text is drawn into the page, you can’t really ‘search’ for it anymore.

      it can be done if you lay out the text yourself: word by word, line by line, page by page. the example script below is based on this one.

      Screen Shot 2020-09-21 at 12.51.01.png

      TXT = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
      FONT = 'Menlo-Bold'
      FONTSIZE = 36
      LINEHEIGHT = 120
      WIDTH = 570
      X, Y = 150, 900
      HIGHLIGHT = 'voluptate'
      PAGEHIGHLIGHT = None
      
      # start with an empty string, add words one by one
      T = FormattedString(font=FONT, fontSize=FONTSIZE, lineHeight=FONTSIZE*1.2)
      
      # set text properties globally - needed for correct measurements with `textSize`
      font(FONT)
      fontSize(FONTSIZE)
      
      # variables to keep track of the current line
      lineLength = 0
      lineNumber = 1
      lineY = Y
      
      # iterate over words in text
      for word in TXT.split():
      
          # if word fits in the current line, add word to line
          if lineLength + textSize(word)[0] <= WIDTH:
              T += word + ' '
              lineLength += textSize(word + ' ')[0]
      
          # word does not fit in current line, make a new line
          else:        
      
              # if next line does not fit in current page, start a new page
              if lineY - LINEHEIGHT < 0:
                  newPage()
                  font(FONT)
                  fontSize(FONTSIZE)
                  lineY = Y
                  
              # draw the current line
              text(T, (X, lineY))
      
              # check if highlight word was in this line and page
              if HIGHLIGHT in str(T):
                  PAGEHIGHLIGHT = pageCount()
                  color = (1, 0, 0)
              else: 
                  color = (0.7,)
              fill(*color)
              text(str(lineNumber), (X-50, lineY), align='right')
      
              # make a fresh new line, add word to line
              T = FormattedString(font=FONT, fontSize=FONTSIZE, lineHeight=FONTSIZE*1.2)
              T += word + ' '
      
              # update variables for new line
              lineLength = textSize(word + ' ')[0]
              lineNumber += 1
              lineY -= LINEHEIGHT
      
      # draw page numbers, check if highlight word in page
      for i, page in enumerate(pages()):
          with page:
              color = (1, 0, 0) if (i+1) == PAGEHIGHLIGHT else (0.7,)
              fill(*color)
              text(str(i+1), (width()-50, Y), align='right')
      

      hope this makes sense!

      posted in General Discussion
      gferreira
      gferreira
    • RE: Different fonts in a text box

      hello @mït,

      not sure if we can call it clean 🙂 but here’s a way to apply formatting to a FormattedString a posteriori using a simple find & replace:

      txt = '''Some title here
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla tempus justo in finibus condimentum. Proin vitae mauris ac nibh elementum consectetur. Aenean justo mi, cursus vel placerat vitae, gravida in mi.
      '''
      
      def applyFormatting(formattedString, findText, styleDict):
      
          start = str(formattedString).find(findText)
          end = start + len(findText)
          
          before = formattedString[:start]
          after  = formattedString[end:]
      
          replaceText = formattedString.copy()
          replaceText.clear()
          replaceText.append(findText, **styleDict)
      
          return before + replaceText + after
      
      T = FormattedString(txt, fontSize=36, font="Arial")
      
      T = applyFormatting(T, 'Some title here', dict(fontSize=72, font='Times New Roman', paragraphBottomSpacing=20))
      T = applyFormatting(T, 'sit amet', dict(font='Arial Bold', fill=(1, 0, 0),))
      T = applyFormatting(T, 'condimentum', dict(font='Arial Italic', fill=(0, 1, 0),))
      
      while len(T):
          newPage('A4')
          T = textBox(T, (20, 20, width() - 40, height() - 40))
      

      adding support for markdown or html markup to FormattedString would be super useful!

      hope this helps

      posted in General Discussion
      gferreira
      gferreira
    • RE: Different fonts in a text box

      hello @mït,

      you can change text properties in the FormattedString, before passing it to textBox.

      like this:

      T = FormattedString(fontSize=42)
      T.font("Arial")
      T.append('Lorem ipsum dolor ')
      
      T.font("Arial Bold")
      T.append('sit amet, ')
      
      T.font("Arial")
      T.append('consectetur adipiscing elit. Nulla tempus justo in finibus ')
      
      T.font("Arial Italic")
      T.append('condimentum. ')
      
      T.font("Arial")
      T.append('Proin vitae mauris ac nibh elementum consectetur. Aenean justo mi, cursus vel placerat vitae, gravida in mi.')
      
      while len(T):
          newPage('A4')
          T = textBox(T, (10, 10, width() - 20, height() - 20))
      

      hope this helps! cheers

      posted in General Discussion
      gferreira
      gferreira
    • RE: access a specific frame from a gif

      hello @traviskochel,

      you can use image() inside an ImageObject to get a specific page, and then apply the filters using the image object. like this:

      path = 'myFolder/myDocument.pdf'
      
      img = ImageObject()
      
      with img:
          w, h = imageSize(path)
          size(w, h)
          image(path, (0, 0), pageNumber=4)
          
      img.falseColor(color0=(1,0,0,1))
      image(img, (0, 0))
      

      cheers!

      posted in General Discussion
      gferreira
      gferreira
    • RE: access a specific frame from a gif

      hello @eduairet,

      you can use the pageNumber argument in image or imageSize to get a given frame in a multipage image (gif or pdf). important: page numbering starts at 1.

      see Inserting a PDF

      path = '~/Desktop/image.gif'
      pageCount = numberOfPages(path)
      
      for pageNumber in range(1, pageCount+1):
          w, h = imageSize(path, pageNumber=pageNumber)
          newPage(w, h)
          image(path, (0, 0), pageNumber=pageNumber)
      

      cheers!

      posted in General Discussion
      gferreira
      gferreira
    • RE: Set Rfont to some font and access its glyph methods

      @michelangelo textSize() uses the current graphics state to measure some text, so in order get the advance widths for your font, you need to set it first:

      FONTNAME = 'someFont'
      font(FONTNAME) # <-- set the font before measuring
      for char in TEXT:
          src.text(char, (0, 0), font=FONTNAME, fontSize=FONTSIZE)
          drawPath(src)
          advance = textSize(char)[0] # measure using the current font
      

      cheers!

      posted in Code snippets
      gferreira
      gferreira
    • RE: Set Rfont to some font and access its glyph methods

      How does drawBot know when to use the kerning pairs for [AA], [AW]?

      in your example Helvetica is an installed font in binary format, you can use it to set text etc. while RFont and RGlyph are normally used to edit source fonts in UFO format.

      if you want to access and manipulate glyphs of an installed font, you can do it using the BezierPath object:

      abcd.png

      from fontPens.flattenPen import FlattenPen
      
      TEXT = 'abcd'
      FONTSIZE = 300
      
      size(800, 300)
      translate(40, 40)
      fontSize(FONTSIZE)
      
      for char in TEXT:
      
          # get bezier shape for character
          src = BezierPath()
          src.text(char, (0, 0), fontSize=FONTSIZE)
      
          # modify shape using a pen
          dst = BezierPath()
          pen = FlattenPen(dst, approximateSegmentLength=60)
          src.drawToPen(pen)
      
          # draw original shape
          stroke(None)
          fill(0.8)
          drawPath(src)
      
          # draw modified shape
          fill(None)
          stroke(1, 0, 0)
          drawPath(dst)
      
          # get advance width
          advance = textSize(char)[0]
      
          # move to next character
          translate(advance, 0)
      

      this example uses a pen to transform the glyph, but you can also do it using your own functions.

      hope this makes sense!

      posted in Code snippets
      gferreira
      gferreira
    • RE: Set Rfont to some font and access its glyph methods

      hi @michelangelo, just a small note:

      myFont.newGlyph(char)

      RFont.newGlyph() takes a glyph name, not a unicode character.

      cheers!

      posted in Code snippets
      gferreira
      gferreira
    • RE: Stroke alignment in shape

      hello @guidoferreyra,

      ovals and rects are also available as methods of BezierPath, so you can use that same solution to draw the stroke inwards.

      to draw the stroke outwards, you can draw the shape with a doubled stroke first, and then draw it again on top using only the fill (so the internal half of the stroke is covered).

      here’s an updated example showing all 3 “stroke modes”:

      stroke-modes.png

      STROKEWIDTH = 20
      
      B = BezierPath()
      B.text('a', (0, 0), fontSize=1200)
      # B.rect(0, 0, 500, 500)
      # B.oval(0, 0, 500, 500)
      
      size(2200, 1000)
      translate(50, 100)
      fontSize(72)
      
      for option in ['inside', 'center', 'outside']:
      
          text(option, (100, 780))
          save()        
          stroke(1, 0, 0)
      
          if option == 'inside':
              strokeWidth(STROKEWIDTH * 2)
              with savedState():
                  clipPath(B)
                  drawPath(B)
      
          elif option == 'outside':
              strokeWidth(STROKEWIDTH * 2)
              drawPath(B)
              stroke(None)
              drawPath(B)
      
          else:
              strokeWidth(STROKEWIDTH)
              drawPath(B)
      
          restore()
          translate(700, 0)
      

      hope this helps! cheers

      posted in General Discussion
      gferreira
      gferreira
    • RE: Tutorial request: how to animate a variable font

      hello @yoshigoshi,

      the axis is selected by index in line 11:

      axis = list(variations.keys())[0] # 0 = first
      

      the width axis is the second one in this case, so you can get it with [1].

      cheers!

      posted in Tutorials
      gferreira
      gferreira
    • RE: Oval() point placement

      hello @ryan,

      I dont’t know why the extremes are angled…

      here’s a way to get the desired result by modifying oval:

      def twist(func, angle):
          def wrapper(x, y, w, h):
              with savedState():
                  translate(x + w/2, y + h/2)
                  rotate(angle)
                  func(-w/2, -h/2, w, h)
          return wrapper
      
      oval = twist(oval, 45)
      

      when working with BezierPath, it’s possible to rotate the shape:

      B = BezierPath()
      B.oval(x, y, w, h)
      B.rotate(45, (x + w/2, y + h/2))
      

      hope this helps!

      posted in General Discussion
      gferreira
      gferreira
    • RE: Manipulating points

      @michelangelo you can use DrawBot inside RoboFont with the DrawBot extension

      posted in General Discussion
      gferreira
      gferreira
    • RE: Delay after drawing

      hello @monomonnik,

      …is it possible to only save as gif, and not draw the canvas in the Drawbot app?

      yes: you can use DrawBot as a Python module, without the interface – this can speed things up considerably.

      see also posts with the tag animation – there are some discussions about performance optimization.

      good luck!

      posted in General Discussion
      gferreira
      gferreira