Update the appearance of an annotation.

def update(self, fontsize=0.0, text_color=None, border_color=None, fill_color=None, rotate=-1):
        """Update the appearance of an annotation."""

        if self.type[0] == ANNOT_WIDGET:
            print("Use updateWidget method for form fields.")
            return False

        val = _fitz.Annot_update(self, fontsize, text_color, border_color, fill_color, rotate)

        """
        The following code fixes shortcomings of MuPDF's "pdf_update_annot"
        function. Currently these are:
        1. Opacity (all annots). MuPDF ignores this proprty. This requires
           to add an ExtGState (extended graphics state) object in the
           C code as well.
        2. Dashing (all annots). MuPDF ignores this proprty.
        3. Colors and font size for FreeText annotations.
        4. Line end icons also for POLYGON and POLY_LINE annotations.
           MuPDF only honors them for LINE annotations.
        5. Always perform a "clean" for the annot, because MuPDF does not
           enclose their syntax in a string pair "q ... Q", which may cause
           Adobe and other readers not to display the annot.

        """
        if not val is True:  # skip if something went wrong
            return val

        def color_string(cs, code):
            """Return valid PDF color operator for a given color sequence.
            """
            if cs is None: return ""
            if hasattr(cs, "__float__") or len(cs) == 1:
                app = " g\n" if code == "f" else " G\n"
            elif len(cs) == 3:
                app = " rg\n" if code == "f" else " RG\n"
            else:
                app = " k\n" if code == "f" else " K\n"
            if hasattr(cs, "__len__"):
                col = " ".join(map(str, cs)) + app
            else:
                col = "%g" % cs + app
            return bytes(col, "utf8") if not fitz_py2 else col

        type   = self.type[0]               # get the annot type
        dt     = self.border["dashes"]      # get the dashes spec
        bwidth = self.border["width"]       # get border line width
        stroke = self.colors["stroke"]      # get the stroke color
        fill   = self.colors["fill"]        # get the fill color
        rect   = None                       # used if we change the rect here
        bfill  = color_string(fill, "f")
        p_ctm  = self.parent._getTransformation() # page transformation matrix
        imat   = ~p_ctm                     # inverse page transf. matrix

        line_end_le, line_end_ri = 0, 0     # line end codes
        if self.lineEnds:
            line_end_le, line_end_ri = self.lineEnds

        ap     = self._getAP()              # get the annot operator source
        ap_updated = False                  # assume we did nothing

        if type == ANNOT_FREETEXT:
            CheckColor(fill_color)
            CheckColor(border_color)
            CheckColor(text_color)

            ap_tab = ap.splitlines()        # split AP stream into lines
            idx_BT = ap_tab.index(b"BT")    # line no. of text start
        # to avoid effort, we rely on a fixed format generated by MuPDF for
        # this annot type: line 0 = fill color, line 5 border color, etc.
            if fill_color is not None:
                ap_tab[0] = color_string(fill_color, "f")
                ap_updated = True
            else:
                ap_tab[0] = ap_tab[1] = ap_tab[2] = b""
                ap_updated = True

            if idx_BT == 7:
                if bwidth > 0:
                    if border_color is not None:
                        ap_tab[4] = color_string(border_color, "s")
                        ap_updated = True
                else: # for zero border width suppress border
                    ap_tab[3] = b"0 w"
                    ap_tab[4] = ap_tab[5] = ap_tab[6] = b""
                    ap_updated = True

            if text_color is not None:
                ap_tab[idx_BT + 1] = color_string(text_color, "f")
                ap_updated = True

            if fontsize > 0.0:
                x = ap_tab[idx_BT + 2].split()
                x[1] = b"%g" % fontsize
                ap_tab[idx_BT + 2] = b" ".join(x)
                ap_updated = True

            if ap_updated:
                ap = b"\n".join(ap_tab)         # updated AP stream

        if bfill != "":
            if type == ANNOT_POLYGON:
                ap = ap[:-1] + bfill + b"b" # close, fill, and stroke
                ap_updated = True
            elif type == ANNOT_POLYLINE:
                ap = ap[:-1] + bfill + b"B" # fill and stroke
                ap_updated = True

        # Dashes not handled by MuPDF, so we do it here.
        if dt:
            dash = "[" + " ".join(map(str, dt)) + "] d\n"
            ap = dash.encode("utf-8") + ap
        # reset dashing - only applies for LINE annots with line ends given
            ap = ap.replace(b"\nS\n", b"\nS\n[] d\n", 1)
            ap_updated = True

        # Opacity not handled by MuPDF, so we do it here. The /ExtGState object
        # "Alp0" referenced here has already been added by our C code.
        if 0 <= self.opacity < 1:
            ap = b"/Alp0 gs\n" + ap
            ap_updated = True

        #----------------------------------------------------------------------
        # the following handles line end symbols for 'Polygon' and 'Polyline
        #----------------------------------------------------------------------
        if max(line_end_le, line_end_ri) > 0 and type in (ANNOT_POLYGON, ANNOT_POLYLINE):

            le_funcs = (None, TOOLS._le_square, TOOLS._le_circle,
                        TOOLS._le_diamond, TOOLS._le_openarrow,
                        TOOLS._le_closedarrow, TOOLS._le_butt,
                        TOOLS._le_ropenarrow, TOOLS._le_rclosedarrow,
                        TOOLS._le_slash)
            le_funcs_range = range(1, len(le_funcs))
            d = 4 * max(1, self.border["width"])
            rect = self.rect + (-d, -d, d, d)
            ap_updated = True
            points = self.vertices
            ap = b"q\n" + ap + b"\nQ\n"
            if line_end_le in le_funcs_range:
                p1 = Point(points[0]) * imat
                p2 = Point(points[1]) * imat
                left = le_funcs[line_end_le](self, p1, p2, False)
                ap += bytes(left, "utf8") if not fitz_py2 else left
            if line_end_ri in le_funcs_range:
                p1 = Point(points[-2]) * imat
                p2 = Point(points[-1]) * imat
                left = le_funcs[line_end_ri](self, p1, p2, True)
                ap += bytes(left, "utf8") if not fitz_py2 else left

        if ap_updated:
            if rect:                        # rect modified here?
                self.setRect(rect)
                self._setAP(ap, rect = 1)
            else:
                self._setAP(ap, rect = 0)

        # always perform a clean to wrap stream by "q" / "Q"
        self._cleanContents()

        return val