code: def draw_grid_annotations(
      im, width, height, hor_texts, ver_texts, margin=0
  ):
      color_active = ImageColor.getcolor(
          opts.grid_text_active_color, "RGB"
      )
      color_inactive = ImageColor.getcolor(
          opts.grid_text_inactive_color, "RGB"
      )
      color_background = ImageColor.getcolor(
          opts.grid_background_color, "RGB"
      )
  
      def wrap(drawing, text, font, line_length):
          lines = [""]
          for word in text.split():
              line = f"{lines[-1]} {word}".strip()
              if drawing.textlength(line, font=font) <= line_length:
                  lines[-1] = line
              else:
                  lines.append(word)
          return lines
  
      def draw_texts(
          drawing, draw_x, draw_y, lines, initial_fnt, initial_fontsize
      ):
          for line in lines:
              fnt = initial_fnt
              fontsize = initial_fontsize
              while (
                  drawing.multiline_textsize(line.text, font=fnt)[0]
                  > line.allowed_width
                  and fontsize > 0
              ):
                  fontsize -= 1
                  fnt = get_font(fontsize)
              drawing.multiline_text(
                  (draw_x, draw_y + line.size[1] / 2),
                  line.text,
                  font=fnt,
                  fill=color_active
                  if line.is_active
                  else color_inactive,
                  anchor="mm",
                  align="center",
              )
  
              if not line.is_active:
                  drawing.line(
                      (
                          draw_x - line.size[0] // 2,
                          draw_y + line.size[1] // 2,
                          draw_x + line.size[0] // 2,
                          draw_y + line.size[1] // 2,
                      ),
                      fill=color_inactive,
                      width=4,
                  )
  
              draw_y += line.size[1] + line_spacing
  
      fontsize = (width + height) // 25
      line_spacing = fontsize // 2
  
      fnt = get_font(fontsize)
  
      pad_left = (
          0
          if sum(
              [
                  sum([len(line.text) for line in lines])
                  for lines in ver_texts
              ]
          )
          == 0
          else width * 3 // 4
      )
  
      cols = im.width // width
      rows = im.height // height
  
      assert cols == len(
          hor_texts
      ), f"bad number of horizontal texts: {len(hor_texts)}; must be {cols}"
      assert rows == len(
          ver_texts
      ), f"bad number of vertical texts: {len(ver_texts)}; must be {rows}"
  
      calc_img = Image.new("RGB", (1, 1), color_background)
      calc_d = ImageDraw.Draw(calc_img)
  
      for texts, allowed_width in zip(
          hor_texts + ver_texts,
          [width] * len(hor_texts) + [pad_left] * len(ver_texts),
      ):
          items = [] + texts
          texts.clear()
  
          for line in items:
              wrapped = wrap(calc_d, line.text, fnt, allowed_width)
              texts += [
                  GridAnnotation(x, line.is_active) for x in wrapped
              ]
  
          for line in texts:
              bbox = calc_d.multiline_textbbox(
                  (0, 0), line.text, font=fnt
              )
              line.size = (bbox[2] - bbox[0], bbox[3] - bbox[1])
              line.allowed_width = allowed_width
  
      hor_text_heights = [
          sum([line.size[1] + line_spacing for line in lines])
          - line_spacing
          for lines in hor_texts
      ]
      ver_text_heights = [
          sum([line.size[1] + line_spacing for line in lines])
          - line_spacing * len(lines)
          for lines in ver_texts
      ]
  
      pad_top = (
          0
          if sum(hor_text_heights) == 0
          else max(hor_text_heights) + line_spacing * 2
      )
  
      result = Image.new(
          "RGB",
          (
              im.width + pad_left + margin * (cols - 1),
              im.height + pad_top + margin * (rows - 1),
          ),
          color_background,
      )
  
      for row in range(rows):
          for col in range(cols):
              cell = im.crop(
                  (
                      width * col,
                      height * row,
                      width * (col + 1),
                      height * (row + 1),
                  )
              )
              result.paste(
                  cell,
                  (
                      pad_left + (width + margin) * col,
                      pad_top + (height + margin) * row,
                  ),
              )
  
      d = ImageDraw.Draw(result)
  
      for col in range(cols):
          x = pad_left + (width + margin) * col + width / 2
          y = pad_top / 2 - hor_text_heights[col] / 2
  
          draw_texts(d, x, y, hor_texts[col], fnt, fontsize)
  
      for row in range(rows):
          x = pad_left / 2
          y = (
              pad_top
              + (height + margin) * row
              + height / 2
              - ver_text_heights[row] / 2
          )
  
          draw_texts(d, x, y, ver_texts[row], fnt, fontsize)
  
      return result
  
imports: from __future__ import annotations
  import datetime
  import pytz
  import io
  import math
  import os
  from collections import namedtuple
  import re
  import numpy
  import piexif
  import piexif.helper
  from PIL import Image, ImageFont, ImageDraw, ImageColor, PngImagePlugin
  import string
  import json
  import hashlib
  from modules import sd_samplers, shared, script_callbacks, errors
  from modules.paths_internal import roboto_ttf_file
  from modules.shared import opts
label_desc: Use image node_id 5 for calling a global function with example usage: draw_grid_annotations(im, width, height, hor_texts, ver_texts, margin) and returns: result, lines