Navigation

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

    Gustavo Ferreira

    @gferreira

    38
    Reputation
    109
    Posts
    764
    Profile views
    4
    Followers
    0
    Following
    Joined Last Online
    Website hipertipo.com

    gferreira Follow
    drafts admin

    Best posts made by 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: Add fontVariation to text drawn in bezierPath?

      hi. you can use a FormattedString:

      txt = FormattedString()
      txt.append("hello world", font='Skia', fontSize=150, fontVariations=dict(wdth=0.8, wght=1.3))
      
      B = BezierPath()
      B.text(txt, (100, 100))
      
      drawPath(B)
      
      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: 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: 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: RFont subclass not generating "otf"

      hello @RicardGarcia,

      for a simple interpolation between two masters you can use font.interpolate(factor, font1, font2) – see this example script.

      hope this helps!

      posted in General Discussion
      gferreira
      gferreira
    • RE: Changing a list derived from a reference list also changes the reference list?

      hello @MauriceMeilleur,

      assigning a list to a new variable does not create a new list, just a new reference to the same list. (this also applies to dictionaries; both are mutable object types.)

      there are different ways to create a copy of a list:

      A = [1, 2, 3]
      B = A.copy() # or list(A) # or A[:]
      B.append(4)
      print(A)
      print(B)
      

      cheers!

      posted in General Discussion
      gferreira
      gferreira
    • RE: Delete previously made pages

      hello @ryan,

      I think newDrawing() is what you’re looking for.

      cheers!

      posted in General Discussion
      gferreira
      gferreira
    • RE: Problems with cv2? Alternative for resizing saved images on the fly?

      hello @MauriceMeilleur,

      you can set a different resolution when saving images in DrawBot. this can produce a different size in pixels than the document size (in points):

      size(100, 100)
      rect(10, 10, 80, 80)
      imgPath = '~/Desktop/malevich.png'
      saveImage(imgPath, imageResolution=144) # 200% = 72dpi * 2
      print(imageSize(imgPath))
      # (200, 200)
      

      you can also try using PIL/Pillow.

      cheers!

      posted in General Discussion
      gferreira
      gferreira

    Latest 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