Insert an image in a rectangle on the current page.

    Notes:
        Exactly one of filename, pixmap or stream must be provided.
    Args:
        rect: (rect-like) where to place the source image
        filename: (str) name of an image file
        pixmap: (obj) a Pixmap object
        stream: (bytes) an image in memory
        rotate: (int) degrees (multiple of 90)
        keep_proportion: (bool) whether to maintain aspect ratio
        overlay: (bool) put in foreground

def insertImage(page, rect, filename=None, pixmap=None, stream=None, rotate=0,
                keep_proportion = True,
                overlay=True):
    """Insert an image in a rectangle on the current page.

    Notes:
        Exactly one of filename, pixmap or stream must be provided.
    Args:
        rect: (rect-like) where to place the source image
        filename: (str) name of an image file
        pixmap: (obj) a Pixmap object
        stream: (bytes) an image in memory
        rotate: (int) degrees (multiple of 90)
        keep_proportion: (bool) whether to maintain aspect ratio
        overlay: (bool) put in foreground
    """

    def calc_matrix(fw, fh, tr, rotate=0):
        """ Calculate transformation matrix for image insertion.

        Notes:
            The image will preserve its aspect ratio if and only if arguments
            fw, fh are both equal to 1.
        Args:
            fw, fh: width / height ratio factors of image - floats in (0,1].
                At least one of them (corresponding to the longer side) is equal to 1.
            tr: target rect in PDF coordinates
            rotate: rotation angle in degrees
        Returns:
            Transformation matrix.
        """
        # center point of target rect
        tmp = Point((tr.x1 + tr.x0) / 2., (tr.y1 + tr.y0) / 2.)

        rot = Matrix(rotate)  # rotation matrix

        # matrix m moves image center to (0, 0), then rotates
        m = Matrix(1, 0, 0, 1, -0.5, -0.5) * rot

        #sr1 = sr * m  # resulting image rect

        # --------------------------------------------------------------------
        # calculate the scale matrix
        # --------------------------------------------------------------------
        small = min(fw, fh)  # factor of the smaller side

        if rotate not in (0, 180):
            fw, fh = fh, fw  # width / height exchange their roles

        if fw < 1: # portrait
            if (float(tr.width) / fw) > (float(tr.height) / fh):
                w = tr.height * small
                h = tr.height
            else:
                w = tr.width
                h = tr.width / small

        elif fw != fh:  # landscape
            if (float(tr.width) / fw) > (float(tr.height) / fh):
                w = tr.height / small
                h = tr.height
            else:
                w = tr.width
                h = tr.width * small

        else: # (treated as) equal sided
            w = tr.width
            h = tr.height

        m *= Matrix(w, h)  # concat scale matrix

        m *= Matrix(1, 0, 0, 1, tmp.x, tmp.y)  # concat move to target center

        return m
    # -------------------------------------------------------------------------

    CheckParent(page)
    doc = page.parent
    if not doc.isPDF:
        raise ValueError("not a PDF")
    if bool(filename) + bool(stream) + bool(pixmap) != 1:
        raise ValueError("need exactly one of filename, pixmap, stream")

    if filename and not os.path.exists(filename):
        raise FileNotFoundError("No such file: '%s'" % filename)
    elif stream and type(stream) not in (bytes, bytearray, io.BytesIO):
        raise ValueError("stream must be bytes-like or BytesIO")
    elif pixmap and type(pixmap) is not Pixmap:
        raise ValueError("pixmap must be a Pixmap")

    while rotate < 0:
        rotate += 360
    while rotate > 360:
        rotate -= 360
    if rotate not in (0, 90, 180, 270):
        raise ValueError("bad rotate value")

    r = page.rect & rect
    if r.isEmpty or r.isInfinite:
        raise ValueError("rect must be finite and not empty")
    _imgpointer = None

    if keep_proportion is True:  # for this we need the image dimension
        if pixmap:  # this is the easy case
            w = pixmap.width
            h = pixmap.height
        elif stream:  # use tool to access the information
            # we also pass through the generated fz_image address
            img_size = TOOLS.image_size(stream, keep_image=True)
            w, h = img_size[:2]
            stream = None  # make sure this arg is NOT used
            _imgpointer = img_size[-1]  # pointer to fz_image
        else:  # worst case, we need to read the file ourselves
            img = open(filename, "rb")
            stream = img.read()
            img_size = TOOLS.image_size(stream, keep_image=True)
            w, h = img_size[:2]
            _imgpointer = img_size[-1]  # pointer to fz_image
            stream = None  # make sure this arg is NOT used
            filename = None  # make sure this arg is NOT used
            img.close()  # close image file

        maxf = max(w, h).__float__()
        fw = w / maxf
        fh = h / maxf
    else:
        fw = fh = 1.0

    clip = r * ~page._getTransformation()  # target rect in PDF coordinates

    matrix = calc_matrix(fw, fh, clip, rotate=rotate)

    ilst = [i[7] for i in doc.getPageImageList(page.number)]
    n = "fzImg"
    i = 0
    _imgname = n + "0"
    while _imgname in ilst:
        i += 1
        _imgname = n + str(i)

    page._insertImage(
            filename=filename,  # image in file
            pixmap=pixmap,  # image in pixmap
            stream=stream,  # image in memory
            matrix=matrix,  # generated matrix
            overlay=overlay,
            _imgname=_imgname,  # generated PDF resource name
            _imgpointer=_imgpointer,  # address of fz_image
        )