code: def analyze_page(page):
      """Compute MediaBox and rectangles on page that are free to receive text.
  
      Notes:
          Assume a BORDER around the page, make 2 columns of the resulting
          sub-rectangle and extract the rectangles of all images on page.
          For demo purposes, the image rectangles are taken as "NO-GO areas"
          on the page when writing text with the Story.
          The function returns free areas for each of the columns.
  
      Returns:
          (page.number, mediabox, CELLS), where CELLS is a list of free cells.
      """
      prect = page.rect  # page rectangle - will be our MEDIABOX later
      where = prect + (BORDER, BORDER, -BORDER, -BORDER)
      TABLE = fitz.make_table(where, rows=1, cols=COLS)
  
      # extract rectangles covered by images on this page
      IMG_RECTS = (
          sorted(  # image rects on page (sort top-left to bottom-right)
              [
                  fitz.Rect(item["bbox"])
                  for item in page.get_image_info()
              ],
              key=lambda b: (b.y1, b.x0),
          )
      )
  
      def free_cells(column):
          """Return free areas in this column."""
          free_stripes = []  # y-value pairs wrapping a free area stripe
          # intersecting images: block complete intersecting column stripe
          col_imgs = [
              (b.y0, b.y1) for b in IMG_RECTS if abs(b & column) > 0
          ]
          s_y0 = column.y0  # top y-value of column
          for y0, y1 in col_imgs:  # an image stripe
              if (
                  y0 > s_y0 + FONTSIZE
              ):  # image starts below last free btm value
                  free_stripes.append(
                      (s_y0, y0)
                  )  # store as free stripe
              s_y0 = y1  # start of next free stripe
  
          if (
              s_y0 + FONTSIZE < column.y1
          ):  # enough room to column bottom
              free_stripes.append((s_y0, column.y1))
  
          if free_stripes == []:  # covers "no image in this column"
              free_stripes.append((column.y0, column.y1))
  
          # make available cells of this column
          CELLS = [
              fitz.Rect(column.x0, y0, column.x1, y1)
              for (y0, y1) in free_stripes
          ]
          return CELLS
  
      # collection of available Story rectangles on page
      CELLS = []
      for i in range(COLS):
          CELLS.extend(free_cells(TABLE[0][i]))
  
      return page.number, prect, CELLS
  
imports: import io
  import os
  import zipfile
  import fitz
label_desc: Use image node_id 1 for calling a global function with example usage: analyze_page(page) and returns: page,  prect,  CELLS, CELLS