lib/frameit/editor.rb in frameit-2.2.1 vs lib/frameit/editor.rb in frameit-2.2.2

- old
+ new

@@ -31,275 +31,277 @@ def prepare_image @image = MiniMagick::Image.open(screenshot.path) end - private - def store_result - output_path = screenshot.path.gsub('.png', '_framed.png').gsub('.PNG', '_framed.png') - image.format "png" - image.write output_path - Helper.log.info "Added frame: '#{File.expand_path(output_path)}'".green - end - # puts the screenshot into the frame - def put_into_frame - @image = frame.composite(image, "png") do |c| - c.compose "Over" - c.geometry offset['offset'] - end - end + def store_result + output_path = screenshot.path.gsub('.png', '_framed.png').gsub('.PNG', '_framed.png') + image.format "png" + image.write output_path + Helper.log.info "Added frame: '#{File.expand_path(output_path)}'".green + end - def offset - return @offset_information if @offset_information - - @offset_information = fetch_config['offset'] || Offsets.image_offset(screenshot) - - if @offset_information and (@offset_information['offset'] or @offset_information['offset']) - return @offset_information - end - raise "Could not find offset_information for '#{screenshot}'" + # puts the screenshot into the frame + def put_into_frame + @image = frame.composite(image, "png") do |c| + c.compose "Over" + c.geometry offset['offset'] end + end - ######################################################################################### - # Everything below is related to title, background, etc. and is not used in the easy mode - ######################################################################################### + def offset + return @offset_information if @offset_information - # this is used to correct the 1:1 offset information - # the offset information is stored to work for the template images - # since we resize the template images to have higher quality screenshots - # we need to modify the offset information by a certain factor - def modify_offset(multiplicator) - # Format: "+133+50" - hash = offset['offset'] - x = hash.split("+")[1].to_f * multiplicator - y = hash.split("+")[2].to_f * multiplicator - new_offset = "+#{x.round}+#{y.round}" - @offset_information['offset'] = new_offset - end + @offset_information = fetch_config['offset'] || Offsets.image_offset(screenshot) - # Do we add a background and title as well? - def should_add_title? - return (fetch_config['background'] and (fetch_config['title'] or fetch_config['keyword'])) + if @offset_information and (@offset_information['offset'] or @offset_information['offset']) + return @offset_information end + raise "Could not find offset_information for '#{screenshot}'" + end - # more complex mode: background, frame and title - def complex_framing - background = generate_background - - if self.frame # we have no frame on le mac - resize_frame! - @image = put_into_frame + ######################################################################################### + # Everything below is related to title, background, etc. and is not used in the easy mode + ######################################################################################### - # Decrease the size of the framed screenshot to fit into the defined padding + background - frame_width = background.width - frame_padding * 2 - image.resize "#{frame_width}x" - end + # this is used to correct the 1:1 offset information + # the offset information is stored to work for the template images + # since we resize the template images to have higher quality screenshots + # we need to modify the offset information by a certain factor + def modify_offset(multiplicator) + # Format: "+133+50" + hash = offset['offset'] + x = hash.split("+")[1].to_f * multiplicator + y = hash.split("+")[2].to_f * multiplicator + new_offset = "+#{x.round}+#{y.round}" + @offset_information['offset'] = new_offset + end - @image = put_device_into_background(background) + # Do we add a background and title as well? + def should_add_title? + return (fetch_config['background'] and (fetch_config['title'] or fetch_config['keyword'])) + end - if fetch_config['title'] - @image = add_title - end + # more complex mode: background, frame and title + def complex_framing + background = generate_background - image - end + if self.frame # we have no frame on le mac + resize_frame! + @image = put_into_frame - # Padding around the frames - def frame_padding - multi = 1.0 - multi = 1.7 if self.screenshot.is_triple_density? - return fetch_config['padding'] * multi + # Decrease the size of the framed screenshot to fit into the defined padding + background + frame_width = background.width - frame_padding * 2 + image.resize "#{frame_width}x" end - # Returns a correctly sized background image - def generate_background - background = MiniMagick::Image.open(fetch_config['background']) + @image = put_device_into_background(background) - if background.height != screenshot.size[1] - background.resize "#{screenshot.size[0]}x#{screenshot.size[1]}!" # `!` says it should ignore the ratio - end - background + if fetch_config['title'] + @image = add_title end - def put_device_into_background(background) - left_space = (background.width / 2.0 - image.width / 2.0).round - bottom_space = -(image.height / 10).round # to be just a bit below the image bottom - bottom_space -= 40 if screenshot.is_portrait? # even more for portrait mode + image + end - if screenshot.is_mini? - # Such small devices need special treatment - bottom_space -= 50 if screenshot.is_portrait? - bottom_space += 65 unless screenshot.is_portrait? - end + # Padding around the frames + def frame_padding + multi = 1.0 + multi = 1.7 if self.screenshot.triple_density? + return fetch_config['padding'] * multi + end - self.top_space_above_device = background.height - image.height - bottom_space + # Returns a correctly sized background image + def generate_background + background = MiniMagick::Image.open(fetch_config['background']) - @image = background.composite(image, "png") do |c| - c.compose "Over" - c.geometry "+#{left_space}+#{top_space_above_device}" - end + if background.height != screenshot.size[1] + background.resize "#{screenshot.size[0]}x#{screenshot.size[1]}!" # `!` says it should ignore the ratio + end + background + end - return image + def put_device_into_background(background) + left_space = (background.width / 2.0 - image.width / 2.0).round + bottom_space = -(image.height / 10).round # to be just a bit below the image bottom + bottom_space -= 40 if screenshot.portrait? # even more for portrait mode + + if screenshot.mini? + # Such small devices need special treatment + bottom_space -= 50 if screenshot.portrait? + bottom_space += 65 unless screenshot.portrait? end - # Resize the frame as it's too low quality by default - def resize_frame! - multiplicator = (screenshot.size[0].to_f / offset['width'].to_f) # by how much do we have to change this? - new_frame_width = multiplicator * frame.width # the new width for the frame - frame.resize "#{new_frame_width.round}x" # resize it to the calculated witdth - modify_offset(multiplicator) # modify the offset to properly insert the screenshot into the frame later + self.top_space_above_device = background.height - image.height - bottom_space + + @image = background.composite(image, "png") do |c| + c.compose "Over" + c.geometry "+#{left_space}+#{top_space_above_device}" end - # Add the title above the device - def add_title - title_images = build_title_images(image.width) - keyword = title_images[:keyword] - title = title_images[:title] + return image + end - # sum_width: the width of both labels together including the space inbetween - # is used to calculate the ratio - sum_width = title.width - sum_width += keyword.width + keyword_padding if keyword + # Resize the frame as it's too low quality by default + def resize_frame! + multiplicator = (screenshot.size[0].to_f / offset['width'].to_f) # by how much do we have to change this? + new_frame_width = multiplicator * frame.width # the new width for the frame + frame.resize "#{new_frame_width.round}x" # resize it to the calculated witdth + modify_offset(multiplicator) # modify the offset to properly insert the screenshot into the frame later + end - # Resize the 2 labels if necessary - smaller = 1.0 # default - ratio = (sum_width + keyword_padding * 2) / image.width.to_f - if ratio > 1.0 - # too large - resizing now - smaller = (1.0 / ratio) + # Add the title above the device + # rubocop:disable Metrics/AbcSize + def add_title + title_images = build_title_images(image.width) + keyword = title_images[:keyword] + title = title_images[:title] - Helper.log.debug "Text for image #{self.screenshot.path} is quite long, reducing font size by #{(ratio - 1.0).round(2)}" if $verbose + # sum_width: the width of both labels together including the space inbetween + # is used to calculate the ratio + sum_width = title.width + sum_width += keyword.width + keyword_padding if keyword - title.resize"#{(smaller * title.width).round}x" - keyword.resize"#{(smaller * keyword.width).round}x" if keyword - sum_width *= smaller - end + # Resize the 2 labels if necessary + smaller = 1.0 # default + ratio = (sum_width + keyword_padding * 2) / image.width.to_f + if ratio > 1.0 + # too large - resizing now + smaller = (1.0 / ratio) - top_space = (top_space_above_device / 2.0 - (actual_font_size / 2.0 * smaller)).round # centered - left_space = (image.width / 2.0 - sum_width / 2.0).round + Helper.log.debug "Text for image #{self.screenshot.path} is quite long, reducing font size by #{(ratio - 1.0).round(2)}" if $verbose - # First, put the keyword on top of the screenshot, if we have one - if keyword - @image = image.composite(keyword, "png") do |c| - c.compose "Over" - c.geometry "+#{left_space}+#{top_space}" - end + title.resize "#{(smaller * title.width).round}x" + keyword.resize "#{(smaller * keyword.width).round}x" if keyword + sum_width *= smaller + end - left_space += keyword.width + (keyword_padding * smaller) - end - - # Then, put the title on top of the screenshot next to the keyword - @image = image.composite(title, "png") do |c| + top_space = (top_space_above_device / 2.0 - (actual_font_size / 2.0 * smaller)).round # centered + left_space = (image.width / 2.0 - sum_width / 2.0).round + + # First, put the keyword on top of the screenshot, if we have one + if keyword + @image = image.composite(keyword, "png") do |c| c.compose "Over" c.geometry "+#{left_space}+#{top_space}" end - image - end - def actual_font_size - [top_space_above_device / 3.0, @image.width / 30.0].max.round + left_space += keyword.width + (keyword_padding * smaller) end - # The space between the keyword and the title - def keyword_padding - (actual_font_size / 2.0).round + # Then, put the title on top of the screenshot next to the keyword + @image = image.composite(title, "png") do |c| + c.compose "Over" + c.geometry "+#{left_space}+#{top_space}" end + image + end + # rubocop:enable Metrics/AbcSize - # This will build 2 individual images with the title, which will then be added to the real image - def build_title_images(max_width) - words = [:keyword, :title].keep_if{ |a| fetch_text(a) } # optional keyword/title - results = {} - words.each do |key| - # Create empty background - empty_path = File.join(Helper.gem_path('frameit'), "lib/assets/empty.png") - title_image = MiniMagick::Image.open(empty_path) - image_height = actual_font_size * 2 # gets trimmed afterwards anyway, and on the iPad the `y` would get cut - title_image.combine_options do |i| - # * 2.0 as the text might be larger than the actual image. We're trimming afterwards anyway - i.resize "#{max_width * 2.0}x#{image_height}!" # `!` says it should ignore the ratio - end + def actual_font_size + [top_space_above_device / 3.0, @image.width / 30.0].max.round + end - current_font = font(key) - text = fetch_text(key) - Helper.log.debug "Using #{current_font} as font the #{key} of #{screenshot.path}" if $verbose and current_font - Helper.log.debug "Adding text '#{fetch_text(key)}'"if $verbose + # The space between the keyword and the title + def keyword_padding + (actual_font_size / 2.0).round + end - # Add the actual title - title_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 '#{fetch_text(key)}'" - i.fill fetch_config[key.to_s]['color'] - end - title_image.trim # remove white space - - results[key] = title_image + # This will build 2 individual images with the title, which will then be added to the real image + def build_title_images(max_width) + words = [:keyword, :title].keep_if { |a| fetch_text(a) } # optional keyword/title + results = {} + words.each do |key| + # Create empty background + empty_path = File.join(Helper.gem_path('frameit'), "lib/assets/empty.png") + title_image = MiniMagick::Image.open(empty_path) + image_height = actual_font_size * 2 # gets trimmed afterwards anyway, and on the iPad the `y` would get cut + title_image.combine_options do |i| + # * 2.0 as the text might be larger than the actual image. We're trimming afterwards anyway + i.resize "#{max_width * 2.0}x#{image_height}!" # `!` says it should ignore the ratio end - results - end - # Loads the config (colors, background, texts, etc.) - # Don't use this method to access the actual text and use `fetch_texts` instead - def fetch_config - return @config if @config + current_font = font(key) + text = fetch_text(key) + Helper.log.debug "Using #{current_font} as font the #{key} of #{screenshot.path}" if $verbose and current_font + Helper.log.debug "Adding text '#{text}'" if $verbose - config_path = File.join(File.expand_path("..", screenshot.path), "Framefile.json") - config_path = File.join(File.expand_path("../..", screenshot.path), "Framefile.json") unless File.exists?config_path - file = ConfigParser.new.load(config_path) - return {} unless file # no config file at all - @config = file.fetch_value(screenshot.path) + # Add the actual title + title_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.fill fetch_config[key.to_s]['color'] + end + title_image.trim # remove white space + + results[key] = title_image end + results + end - # Fetches the title + keyword for this particular screenshot - def fetch_text(type) - raise "Valid parameters :keyword, :title" unless [:keyword, :title].include?type + # Loads the config (colors, background, texts, etc.) + # Don't use this method to access the actual text and use `fetch_texts` instead + def fetch_config + return @config if @config - # Try to get it from a keyword.strings or title.strings file - strings_path = File.join(File.expand_path("..", screenshot.path), "#{type.to_s}.strings") - if File.exists?strings_path - parsed = StringsParser.parse(strings_path) - result = parsed.find { |k, v| screenshot.path.include?k } - return result.last if result - end + config_path = File.join(File.expand_path("..", screenshot.path), "Framefile.json") + config_path = File.join(File.expand_path("../..", screenshot.path), "Framefile.json") unless File.exist? config_path + file = ConfigParser.new.load(config_path) + return {} unless file # no config file at all + @config = file.fetch_value(screenshot.path) + end - # No string files, fallback to Framefile config - result = fetch_config[type.to_s]['text'] - Helper.log.debug "Falling back to default text as there was nothing specified in the .strings file" if $verbose + # Fetches the title + keyword for this particular screenshot + def fetch_text(type) + raise "Valid parameters :keyword, :title" unless [:keyword, :title].include? type - if !result and type == :title - # title is mandatory - raise "Could not get title for screenshot #{screenshot.path}. Please provide one in your Framefile.json".red - end + # Try to get it from a keyword.strings or title.strings file + strings_path = File.join(File.expand_path("..", screenshot.path), "#{type}.strings") + if File.exist? strings_path + parsed = StringsParser.parse(strings_path) + result = parsed.find { |k, v| screenshot.path.include? k } + return result.last if result + end - return result + # No string files, fallback to Framefile config + result = fetch_config[type.to_s]['text'] + Helper.log.debug "Falling back to default text as there was nothing specified in the .strings file" if $verbose + + if !result and type == :title + # title is mandatory + raise "Could not get title for screenshot #{screenshot.path}. Please provide one in your Framefile.json".red end - # The font we want to use - def font(key) - single_font = fetch_config[key.to_s]['font'] - return single_font if single_font + return result + end - fonts = fetch_config[key.to_s]['fonts'] - if fonts - fonts.each do |font| - if font['supported'] - font['supported'].each do |language| - if screenshot.path.include?language - return font["font"] - end + # The font we want to use + def font(key) + single_font = fetch_config[key.to_s]['font'] + return single_font if single_font + + fonts = fetch_config[key.to_s]['fonts'] + if fonts + fonts.each do |font| + if font['supported'] + font['supported'].each do |language| + if screenshot.path.include? language + return font["font"] end - else - # No `supported` array, this will always be true - Helper.log.debug "Found a font with no list of supported languages, using this now" if $verbose - return font["font"] end + else + # No `supported` array, this will always be true + Helper.log.debug "Found a font with no list of supported languages, using this now" if $verbose + return font["font"] end end - - Helper.log.debug "No custom font specified for #{screenshot}, using the default one" if $verbose - return nil end + + Helper.log.debug "No custom font specified for #{screenshot}, using the default one" if $verbose + return nil + end end end