Different fonts in a text box



  • Hello, everyone!

    Do you have any suggestion about the best way to change a font type inside a textbox?

    I mean, imagine I have a text from a variable and I'd like to put part of it in bold or italic, for instance.

    size('A4')
    t = '''
    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.
    '''
    t2 = FormattedString(t, font="Arial", fontSize = 20)
    while len(t2):
        newPage()
        font("Arial", 20)
        fill(0)
        t2 = textBox(t2, (10, 10, width() - 20, height() - 20))
    

    Is it possible, for example, to put "sit amet" in arial-bold or "condimentum" in arial-italic?

    Thank you!



  • 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



  • Yes, this helps me a lot @gferreira!

    Although it was more or less the idea I had in mind, you've clarified me it so well (and you've clean my code)!

    Nevertheless, my question was the begining of my intention (I dos nit explain you)...I mean...if I have a big text, and I need to define that part of this text is like a title1, another part is like normal text, after could come a title2, again a normal text (maybe with some words in bold or italic), maybe another title like title2, again normal text and so on...is there a clean way to format the text a posteriori?

    Or maybe I should look for another solution outside Drawbot?

    I'd like avoid using a text editor because I'd like to add Drawbot drawing properties to the document...

    Thanks a lot again!



  • 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



  • Hello, @gferreira!

    You have opened me a new world of possibilities! Thank you!

    In fact, I have some more doubts that I had considered with impossible solution after searching and trying for days and days but now I dare to ask you... (please, tell if I should mark someway that this topic is solved and if I should open a new one...or if they are too many questions!)

    • Is there any way to find out the page number where a piece of text (or a word, or a title ...) can be found? I mean, how to get that the title 'Some title here' is in page number 2, for instance.

    • Is there any way to find out the line number (in reference to the text box) where a piece of text can be found? I mean, for example, if the piece of text "gravida in mi., is in the line 1 of 20 of the textbox (that means that is in the first line of a page...

    Believe me if I say that I've been "playing" a lot with the size of the text (T.size()[1]) and the size of the textbox that I've defined, and pagecount and pages ... I guess everything would be based on mathematical calculations (text size, textbox size ...) but I can't find out if it is possible...

    Thank you very much, again!


  • admin

    pageCount() returning the current page count -->https://www.drawbot.com/content/canvas/pages.html#drawBot.pageCount

    textBoxCharacterBounds (I see its not added to the docs yet..) returning a list of typesetted bounding boxes with characters and a subsetted formattedString

    help(textBoxCharacterBounds)  # :)
    

    hope this helps with your puzzle!



  • 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!



  • Thanks you, @frederik!

    I didn't know about textBoxCharacterBounds!
    So far I have not been able to get all its potential because it does not seem to return whole lines.

    print(textBoxCharacterBounds(txt, (x, y, w, h),'justified')[0])
    

    This code does not return only the first line, but a bit more..so I can't confirm the line where I can find a word...but I'll continue exploring!

    Thanks again!



  • Hello, @gferreira!

    I had thought about working word for word ... I didn’t know if it was a good idea ... but it’s really interesting what you coded! I wouldn't have found it alone! thanks!

    I guess this way the text would never be justified ... but it will be very useful for me!

    thanks!