def build_text_images(max_width, max_height, stack_title)
      words = [:keyword, :title].keep_if { |a| fetch_text(a) } # optional keyword/title
      results = {}
      trim_boxes = {}
      top_vertical_trim_offset = Float::INFINITY # Init at a large value, as the code will search for a minimal value.
      bottom_vertical_trim_offset = 0
      words.each do |key|
        # Create empty background
        empty_path = File.join(Frameit::ROOT, "lib/assets/empty.png")
        text_image = MiniMagick::Image.open(empty_path)
        image_height = max_height # gets trimmed afterwards anyway, and on the iPad the `y` would get cut
        text_image.combine_options do |i|
          # Oversize as the text might be larger than the actual image. We're trimming afterwards anyway
          i.resize("#{max_width * 5.0}x#{image_height}!") # `!` says it should ignore the ratio
        end

        current_font = font(key)
        text = fetch_text(key)
        UI.verbose("Using #{current_font} as font the #{key} of #{screenshot.path}") if current_font
        UI.verbose("Adding text '#{text}'")

        text.gsub!('\n', "\n")
        text.gsub!(/(?<!\\)(')/) { |s| "\\#{s}" } # escape unescaped apostrophes with a backslash

        interline_spacing = fetch_config['interline_spacing']

        # Add the actual title
        text_image.combine_options do |i|
          i.font(current_font) if current_font
          i.gravity("Center")
          i.pointsize(actual_font_size)
          i.draw("text 0,0 '#{text}'")
          i.interline_spacing(interline_spacing) if interline_spacing
          i.fill(fetch_config[key.to_s]['color'])
        end

        results[key] = text_image

        # Natively trimming the image with .trim will result in the loss of the common baseline between the text in all images when side-by-side (e.g. stack_title is false).
        # Hence retrieve the calculated trim bounding box without actually trimming:
        calculated_trim_box = text_image.identify do |b|
          b.format("%@") # CALCULATED: trim bounding box (without actually trimming), see: http://www.imagemagick.org/script/escape.php
        end

        # Create a Trimbox object from the MiniMagick .identify string with syntax "<width>x<height>+<offset_x>+<offset_y>":
        trim_box = Frameit::Trimbox.new(calculated_trim_box)

        # Get the minimum top offset of the trim box:
        if trim_box.offset_y < top_vertical_trim_offset
          top_vertical_trim_offset = trim_box.offset_y
        end

        # Get the maximum bottom offset of the trim box, this is the top offset + height:
        if (trim_box.offset_y + trim_box.height) > bottom_vertical_trim_offset
          bottom_vertical_trim_offset = trim_box.offset_y + trim_box.height
        end

        # Store for the crop action:
        trim_boxes[key] = trim_box
      end

      # Crop text images:
      words.each do |key|
        # Get matching trim box:
        trim_box = trim_boxes[key]

        # For side-by-side text images (e.g. stack_title is false) adjust the trim box based on top_vertical_trim_offset and bottom_vertical_trim_offset to maintain the text baseline:
        unless stack_title
          # Determine the trim area by maintaining the same vertical top offset based on the smallest value from all trim boxes (top_vertical_trim_offset).
          # When the vertical top offset is larger than the smallest vertical top offset, the trim box needs to be adjusted:
          if trim_box.offset_y > top_vertical_trim_offset
            # Increase the height of the trim box with the difference in vertical top offset:
            trim_box.height += trim_box.offset_y - top_vertical_trim_offset
            # Change the vertical top offset to match that of the others:
            trim_box.offset_y = top_vertical_trim_offset

            UI.verbose("Trim box for key \"#{key}\" is adjusted to align top: #{trim_box}\n")
          end

          # Check if the height needs to be adjusted to reach the bottom offset:
          if (trim_box.offset_y + trim_box.height) < bottom_vertical_trim_offset
            # Set the height of the trim box to the difference between vertical bottom and top offset:
            trim_box.height = bottom_vertical_trim_offset - trim_box.offset_y

            UI.verbose("Trim box for key \"#{key}\" is adjusted to align bottom: #{trim_box}\n")
          end
        end

        # Crop image with (adjusted) trim box parameters in MiniMagick string format:
        results[key].crop(trim_box.string_format)
      end

      results
    end
def build_text_images(max_width, max_height, stack_title)
      words = [:keyword, :title].keep_if { |a| fetch_text(a) } # optional keyword/title
      results = {}
      trim_boxes = {}
      top_vertical_trim_offset = Float::INFINITY # Init at a large value, as the code will search for a minimal value.
      bottom_vertical_trim_offset = 0
      words.each do |key|
        # Create empty background
        empty_path = File.join(Frameit::ROOT, "lib/assets/empty.png")
        text_image = MiniMagick::Image.open(empty_path)
        image_height = max_height # gets trimmed afterwards anyway, and on the iPad the `y` would get cut
        text_image.combine_options do |i|
          # Oversize as the text might be larger than the actual image. We're trimming afterwards anyway
          i.resize("#{max_width * 5.0}x#{image_height}!") # `!` says it should ignore the ratio
        end

        current_font = font(key)
        text = fetch_text(key)
        UI.verbose("Using #{current_font} as font the #{key} of #{screenshot.path}") if current_font
        UI.verbose("Adding text '#{text}'")

        text.gsub!('\n', "\n")
        text.gsub!(/(?<!\\)(')/) { |s| "\\#{s}" } # escape unescaped apostrophes with a backslash

        interline_spacing = fetch_config['interline_spacing']

        # Add the actual title
        text_image.combine_options do |i|
          i.font(current_font) if current_font
          i.gravity("Center")
          i.pointsize(actual_font_size)
          i.draw("text 0,0 '#{text}'")
          i.interline_spacing(interline_spacing) if interline_spacing
          i.fill(fetch_config[key.to_s]['color'])
        end

        results[key] = text_image

        # Natively trimming the image with .trim will result in the loss of the common baseline between the text in all images when side-by-side (e.g. stack_title is false).
        # Hence retrieve the calculated trim bounding box without actually trimming:
        calculated_trim_box = text_image.identify do |b|
          b.format("%@") # CALCULATED: trim bounding box (without actually trimming), see: http://www.imagemagick.org/script/escape.php
        end

        # Create a Trimbox object from the MiniMagick .identify string with syntax "<width>x<height>+<offset_x>+<offset_y>":
        trim_box = Frameit::Trimbox.new(calculated_trim_box)

        # Get the minimum top offset of the trim box:
        if trim_box.offset_y < top_vertical_trim_offset
          top_vertical_trim_offset = trim_box.offset_y
        end

        # Get the maximum bottom offset of the trim box, this is the top offset + height:
        if (trim_box.offset_y + trim_box.height) > bottom_vertical_trim_offset
          bottom_vertical_trim_offset = trim_box.offset_y + trim_box.height
        end

        # Store for the crop action:
        trim_boxes[key] = trim_box
      end

      # Crop text images:
      words.each do |key|
        # Get matching trim box:
        trim_box = trim_boxes[key]

        # For side-by-side text images (e.g. stack_title is false) adjust the trim box based on top_vertical_trim_offset and bottom_vertical_trim_offset to maintain the text baseline:
        unless stack_title
          # Determine the trim area by maintaining the same vertical top offset based on the smallest value from all trim boxes (top_vertical_trim_offset).
          # When the vertical top offset is larger than the smallest vertical top offset, the trim box needs to be adjusted:
          if trim_box.offset_y > top_vertical_trim_offset
            # Increase the height of the trim box with the difference in vertical top offset:
            trim_box.height += trim_box.offset_y - top_vertical_trim_offset
            # Change the vertical top offset to match that of the others:
            trim_box.offset_y = top_vertical_trim_offset

            UI.verbose("Trim box for key \"#{key}\" is adjusted to align top: #{trim_box}\n")
          end

          # Check if the height needs to be adjusted to reach the bottom offset:
          if (trim_box.offset_y + trim_box.height) < bottom_vertical_trim_offset
            # Set the height of the trim box to the difference between vertical bottom and top offset:
            trim_box.height = bottom_vertical_trim_offset - trim_box.offset_y

            UI.verbose("Trim box for key \"#{key}\" is adjusted to align bottom: #{trim_box}\n")
          end
        end

        # Crop image with (adjusted) trim box parameters in MiniMagick string format:
        results[key].crop(trim_box.string_format)
      end

      results
    end
This will build up to 2 individual images with the title and optional keyword, which will then be added to the real image