Insert text into a given rectangle.

        Args:
            rect -- the textbox to fill
            buffer -- text to be inserted
            fontname -- a Base-14 font, font name or '/name'
            fontfile -- name of a font file
            fontsize -- font size
            color -- RGB stroke color triple
            fill -- RGB fill color triple
            render_mode -- text rendering control
            border_width -- thickness of glyph borders
            expandtabs -- handles tabulators with string function
            align -- left, center, right, justified
            rotate -- 0, 90, 180, or 270 degrees
            morph -- morph box with  a matrix and a pivotal point
        Returns:
            unused or deficit rectangle area (float)

def insertTextbox(self, rect, buffer,
                      fontname="helv",
                      fontfile=None,
                      fontsize=11,
                      set_simple=0,
                      encoding=0,
                      color=None,
                      fill=None,
                      expandtabs=1,
                      border_width=1,
                      align=0,
                      render_mode=0,
                      rotate=0,
                      morph=None):
        """ Insert text into a given rectangle.

        Args:
            rect -- the textbox to fill
            buffer -- text to be inserted
            fontname -- a Base-14 font, font name or '/name'
            fontfile -- name of a font file
            fontsize -- font size
            color -- RGB stroke color triple
            fill -- RGB fill color triple
            render_mode -- text rendering control
            border_width -- thickness of glyph borders
            expandtabs -- handles tabulators with string function
            align -- left, center, right, justified
            rotate -- 0, 90, 180, or 270 degrees
            morph -- morph box with  a matrix and a pivotal point
        Returns:
            unused or deficit rectangle area (float)
        """
        rect = Rect(rect)
        if rect.isEmpty or rect.isInfinite:
            raise ValueError("text box must be finite and not empty")

        color_str = ColorCode(color, "c")
        fill_str = ColorCode(fill, "f")
        if fill is None and render_mode == 0:    # ensure fill color for 0 Tr
            fill = color
            fill_str = ColorCode(color, "f")

        if rotate % 90 != 0:
            raise ValueError("rotate must be multiple of 90")

        rot = rotate
        while rot < 0: rot += 360
        rot = rot % 360
        
        # is buffer worth of dealing with?
        if not bool(buffer):
            return rect.height if rot in (0, 180) else rect.width

        cmp90 = "0 1 -1 0 0 0 cm\n"   # rotates counter-clockwise
        cmm90 = "0 -1 1 0 0 0 cm\n"   # rotates clockwise
        cm180 = "-1 0 0 -1 0 0 cm\n"  # rotates by 180 deg.
        height = self.height

        fname = fontname
        if fname.startswith("/"):
            fname = fname[1:]

        xref = self.page.insertFont(fontname=fname,
                                    fontfile=fontfile,
                                    encoding=encoding,
                                    set_simple=set_simple,
                                   )
        fontinfo = CheckFontInfo(self.doc, xref)

        fontdict = fontinfo[1]
        ordering = fontdict["ordering"]
        simple = fontdict["simple"]
        glyphs = fontdict["glyphs"]
        bfname = fontdict["name"]

        # create a list from buffer, split into its lines
        if type(buffer) in (list, tuple):
            t0 = "\n".join(buffer)
        else:
            t0 = buffer
            
        maxcode = max([ord(c) for c in t0])
        # replace invalid char codes for simple fonts
        if simple and maxcode > 255:
            t0 = "".join([c if ord(c)<256 else "?" for c in t0])

        t0 = t0.splitlines()
        
        glyphs = self.doc.getCharWidths(xref, maxcode + 1)
        if simple and bfname not in ("Symbol", "ZapfDingbats"):
            tj_glyphs = None
        else:
            tj_glyphs = glyphs


        #----------------------------------------------------------------------
        # calculate pixel length of a string
        #----------------------------------------------------------------------
        def pixlen(x):
            """Calculate pixel length of x."""
            if ordering < 0:
                return sum([glyphs[ord(c)][1] for c in x]) * fontsize
            else:
                return len(x) * fontsize
        #----------------------------------------------------------------------

        if ordering < 0:
            blen = glyphs[32][1] * fontsize       # pixel size of space character
        else:
            blen = fontsize

        text = ""                               # output buffer
        lheight = fontsize * 1.2                # line height
        if CheckMorph(morph):
            m1 = Matrix(1, 0, 0, 1, morph[0].x + self.x,
                             self.height - morph[0].y - self.y)
            mat = ~m1 * morph[1] * m1
            cm = "%g %g %g %g %g %g cm\n" % JM_TUPLE(mat)
        else:
            cm = ""
        
        #---------------------------------------------------------------------------
        # adjust for text orientation / rotation
        #---------------------------------------------------------------------------
        progr = 1                               # direction of line progress
        c_pnt = Point(0, fontsize)         # used for line progress
        if rot == 0:                            # normal orientation
            point = rect.tl + c_pnt             # line 1 is 'fontsize' below top
            pos = point.y + self.y              # y of first line
            maxwidth = rect.width               # pixels available in one line
            maxpos = rect.y1 + self.y           # lines must not be below this
            
        elif rot == 90:                         # rotate counter clockwise
            c_pnt = Point(fontsize, 0)     # progress in x-direction
            point = rect.bl + c_pnt             # line 1 'fontsize' away from left
            pos = point.x + self.x              # position of first line
            maxwidth = rect.height              # pixels available in one line
            maxpos = rect.x1 + self.x           # lines must not be right of this
            cm += cmp90
            
        elif rot == 180:                        # text upside down
            c_pnt = -Point(0, fontsize)    # progress upwards in y direction
            point = rect.br + c_pnt             # line 1 'fontsize' above bottom
            pos = point.y + self.y              # position of first line
            maxwidth = rect.width               # pixels available in one line
            progr = -1                          # subtract lheight for next line
            maxpos = rect.y0 + self.y           # lines must not be above this
            cm += cm180
            
        else:                                   # rotate clockwise (270 or -90)
            c_pnt = -Point(fontsize, 0)    # progress from right to left
            point = rect.tr + c_pnt             # line 1 'fontsize' left of right
            pos = point.x + self.x              # position of first line
            maxwidth = rect.height              # pixels available in one line
            progr = -1                          # subtract lheight for next line
            maxpos = rect.x0 + self.x           # lines must not left of this
            cm += cmm90
        
        #=======================================================================
        # line loop
        #=======================================================================
        just_tab = []                           # 'justify' indicators per line
        
        for i, line in enumerate(t0):
            line_t = line.expandtabs(expandtabs).split(" ")  # split into words
            lbuff = ""                          # init line buffer
            rest = maxwidth                     # available line pixels
            #===================================================================
            # word loop
            #===================================================================
            for word in line_t:
                pl_w = pixlen(word)             # pixel len of word
                if rest >= pl_w:                # will it fit on the line?
                    lbuff += word + " "         # yes, and append word
                    rest -= (pl_w + blen)       # update available line space
                    continue
                # word won't fit - output line (if not empty)
                if len(lbuff) > 0:
                    lbuff = lbuff.rstrip() + "\n"   # line full, append line break
                    text += lbuff                   # append to total text
                    pos += lheight * progr          # increase line position
                    just_tab.append(True)           # line is justify candidate
                    lbuff = ""                      # re-init line buffer
                rest = maxwidth                 # re-init avail. space
                if pl_w <= maxwidth:            # word shorter than 1 line?
                    lbuff = word + " "          # start the line with it
                    rest = maxwidth - pl_w - blen    # update free space
                    continue
                # long word: split across multiple lines - char by char ...
                if len(just_tab) > 0:
                    just_tab[-1] = False            # reset justify indicator
                for c in word:
                    if pixlen(lbuff) <= maxwidth - pixlen(c):
                        lbuff += c
                    else:                       # line full
                        lbuff += "\n"           # close line
                        text += lbuff           # append to text
                        pos += lheight * progr  # increase line position
                        just_tab.append(False)  # do not justify line
                        lbuff = c               # start new line with this char
                lbuff += " "                    # finish long word
                rest = maxwidth - pixlen(lbuff) # long word stored
                    
            if lbuff != "":                     # unprocessed line content?
                text += lbuff.rstrip()          # append to text
                just_tab.append(False)          # do not justify line
            if i < len(t0) - 1:                 # not the last line?
                text += "\n"                    # insert line break
                pos += lheight * progr          # increase line position
        
        more = (pos - maxpos) * progr           # difference to rect size limit
        
        if more > 1e-5:                         # landed too much outside rect
            return (-1) * more                  # return deficit, don't output
    
        more = abs(more)
        if more < 1e-5:
            more = 0                            # don't bother with epsilons
        nres = "\nq BT\n" + cm                # initialize output buffer
        templ = "1 0 0 1 %g %g Tm /%s %g Tf "
        # center, right, justify: output each line with its own specifics
        spacing = 0
        text_t = text.splitlines()              # split text in lines again
        for i, t in enumerate(text_t):
            pl = maxwidth - pixlen(t)           # length of empty line part
            pnt = point + c_pnt * (i * 1.2)     # text start of line
            if align == 1:                      # center: right shift by half width
                if rot in (0, 180):
                    pnt = pnt + Point(pl / 2, 0) * progr
                else:
                    pnt = pnt - Point(0, pl / 2) * progr
            elif align == 2:                    # right: right shift by full width
                if rot in (0, 180):
                    pnt = pnt + Point(pl, 0) * progr
                else:
                    pnt = pnt - Point(0, pl) * progr
            elif align == 3:                    # justify
                spaces = t.count(" ")           # number of spaces in line
                if spaces > 0 and just_tab[i]:  # if any, and we may justify
                    spacing = pl / spaces       # make every space this much larger
                else:
                    spacing = 0                 # keep normal space length
            top  = height - pnt.y - self.y
            left = pnt.x + self.x
            if rot == 90:
                left = height - pnt.y - self.y
                top  = -pnt.x - self.x
            elif rot == 270:
                left = -height + pnt.y + self.y
                top  = pnt.x + self.x
            elif rot == 180:
                left = -pnt.x - self.x
                top  = -height + pnt.y + self.y

            nres += templ % (left, top, fname, fontsize)
            if render_mode > 0:
                nres += "%i Tr " % render_mode
            if spacing != 0:
                nres += "%g Tw " % spacing
            if color is not None:
                nres += color_str
            if fill is not None:
                nres += fill_str
            if border_width != 1:
                nres += "%g w " % border_width
            nres += "%sTJ\n" % getTJstr(t, tj_glyphs, simple, ordering)

        nres += "ET Q\n"
        
        self.text_cont += nres
        self.updateRect(rect)
        return more