-
-
Save Siyfion/d92dc707a7f359edff70 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'open-uri' | |
require 'prawn' | |
require 'prawn/measurement_extensions' | |
require_relative 'logging' | |
class RDIRenderer | |
include Singleton, Logging | |
@pdf = nil | |
@nutri_object = nil | |
@associated_product = nil | |
@data_object = nil | |
@min_font_sizes = {} | |
def render_rdi(pdf, nutri_object, associated_product) | |
@pdf = pdf | |
@nutri_object = nutri_object | |
@associated_product = associated_product | |
@min_font_sizes = {title: 100, value: 100, percentage: 100} # Set some high default values | |
# Get the binding's ACTUAL value from the product data | |
bound_attribute = @associated_product['fields'].find { |field| field['binding'] == @nutri_object['binding'] } | |
# There is no data in the product that will render this item, so ignore it | |
return if bound_attribute.nil? | |
# Don't render anything, as it requires data | |
return if bound_attribute['data'].nil? | |
# Check that the data is an object | |
return unless bound_attribute['data'].is_a?(Hash) | |
@data_object = bound_attribute['data'] | |
# Check for valid data before rendering | |
return unless check_for_valid_content | |
@x_position = @nutri_object['settings']['x'].to_f | |
@y_position = @nutri_object['settings']['y'].to_f | |
@rt = @nutri_object['settings']['rt'].to_f | |
@width = @nutri_object['settings']['width'].to_f | |
@height = @nutri_object['settings']['height'].to_f | |
# Populate the color properties with the relevant values | |
get_field_colors | |
@pdf.rotate(-@rt, :origin => [@x_position + (@width / 2.0), -(@y_position + (@height / 2.0))]) do | |
# Work out the current ratio of width to height | |
aspect_ratio = @width / @height | |
# Check the proportions of this control match | |
# the expected (they probably wont!) | |
if aspect_ratio > 2.37 | |
# The control is too SHORT | |
new_width = @height * 2.37 | |
# Adjust the X value to compensate | |
@x_position += (@width - new_width) / 2 | |
# Save the new width | |
@width = new_width | |
elsif aspect_ratio < 2.37 | |
# The control is too WIDE | |
new_height = @width / 2.37 | |
# Adjust the Y value to compensate | |
@y_position += (@height - new_height) / 2 | |
# Save the new height | |
@height = new_height | |
end | |
@pdf.translate(@x_position, -@y_position) do | |
# Render the bottom text and take note of the font size used, | |
# as we need to use the same font size for the top | |
box = Prawn::Text::Formatted::Box.new( | |
[{text: 'of your reference intake'}], | |
document: @pdf, | |
at: [@width / 4, -((@height / 100) * 84.55)], | |
width: @width / 2, | |
height: (@height / 100) * 15.45, | |
overflow: :shrink_to_fit, | |
align: :center, | |
valign: :center, | |
min_font_size: 1, | |
disable_wrap_by_char: true | |
) | |
box.render # Render the text box | |
heading_font_size = box.instance_eval { @font_size } | |
# Set the font size for the top heading | |
@pdf.font_size heading_font_size | |
# Top text | |
@pdf.text_box( | |
"Each #{@data_object['portionText']} contains", | |
at: [@width / 4, 0], | |
width: @width / 2, | |
height: (@height / 100) * 15.45, | |
overflow: :shrink_to_fit, | |
align: :center, | |
valign: :center, | |
min_font_size: 1, | |
disable_wrap_by_char: true | |
) | |
bound_attribute['data']['values'].each_pair do |key, value| | |
@pdf.translate((@width / 5) * value[:index], -((@height / 100) * 84.55)) do | |
if key == 'energy' | |
value_text = "#{bound_attribute['data']['values'][key][:value]['kJ']} kJ\n#{bound_attribute['data']['values'][key][:value]['kcal']}kcal" | |
else | |
value_text = "#{bound_attribute['data']['values'][key][:value]}g" | |
end | |
render_lozenge({}, @width / 5, (@height / 100) * 69.1, value[:color], key.capitalize, value_text, value[:percentage]) | |
end | |
end | |
bound_attribute['data']['values'].each_pair do |key, value| | |
@pdf.translate((@width / 5) * value[:index], -((@height / 100) * 84.55)) do | |
if key == 'energy' | |
value_text = "#{bound_attribute['data']['values'][key][:value]['kJ']} kJ\n#{bound_attribute['data']['values'][key][:value]['kcal']}kcal" | |
else | |
value_text = "#{bound_attribute['data']['values'][key][:value]}g" | |
end | |
render_lozenge(@min_font_sizes, @width / 5, (@height / 100) * 69.1, value[:color], key.capitalize, value_text, value[:percentage]) | |
end | |
end | |
end | |
end | |
end | |
def check_for_valid_content | |
# Check for values that are invalid | |
return false if @data_object['portionSize'] == 0 | |
all_zero = true | |
@data_object['values'].each do |key, value| | |
if value.is_a?(Hash) | |
if value['kJ'] != 0 || value['kcal'] != 0 | |
all_zero = false | |
break | |
end | |
else | |
if value != 0 | |
all_zero = false | |
break | |
end | |
end | |
end | |
# If all the values are zero, then don't draw it! | |
all_zero ? false : true | |
end | |
def render_lozenge (font_sizes, width, height, color, title, value, percentage) | |
# Only render the outline if it's a "real" render | |
unless font_sizes.empty? | |
# Draw the color polygon | |
@pdf.move_to 0, (height / 10) * 3.33 | |
@pdf.line_to width, (height / 10) * 3.33 | |
@pdf.line_to width, (height / 10) * 1.5 | |
@pdf.curve_to [0, (height / 10) * 1.5], :bounds => [[width - ((height / 10) * 1.5), 0], [(height / 10) * 1.5, 0]] | |
@pdf.line_to 0, (height / 10) * 3.33 | |
@pdf.fill_color color | |
@pdf.fill | |
@pdf.fill_color '000000' | |
# Draw the outlines | |
@pdf.stroke do | |
# Set the join style to rounded to make it nice and smooth | |
@pdf.join_style = :round | |
# Draw the main outline | |
@pdf.move_to 0, (height / 10) * 1.5 | |
@pdf.curve_to [width, (height / 10) * 1.5], :bounds => [[(height / 10) * 1.5, 0], [width - ((height / 10) * 1.5), 0]] | |
@pdf.line_to width, (height / 10) * 8.5 | |
@pdf.curve_to [0, (height / 10) * 8.5], :bounds => [[width - ((height / 10) * 1.5), height], [(height / 10) * 1.5, height]] | |
@pdf.line_to 0, (height / 10) * 1.5 | |
# Draw the divider line | |
@pdf.move_to 0, (height / 10) * 3.33 | |
@pdf.line_to width, (height / 10) * 3.33 | |
end | |
end | |
@pdf.font_size 40 | |
logger.info("font_sizes: #{font_sizes}\n@min_font_sizes: #{@min_font_sizes}") | |
# Title text | |
box = Prawn::Text::Formatted::Box.new( | |
get_font_value(title, font_sizes[:title]), | |
document: @pdf, | |
at: [(width / 100) * 5, (height / 10) * 8.75], | |
width: (width / 100) * 90, | |
height: (height / 10) * 1.3, | |
overflow: :shrink_to_fit, | |
align: :center, | |
valign: :center, | |
min_font_size: 1, | |
disable_wrap_by_char: true | |
) | |
font_sizes[:title].nil? ? box.render(dry_run: true) : box.render | |
if box.instance_eval { @font_size } < @min_font_sizes[:title] | |
@min_font_sizes[:title] = box.instance_eval { @font_size } | |
end | |
# Value text | |
box = Prawn::Text::Formatted::Box.new( | |
get_font_value(value, font_sizes[:value]), | |
document: @pdf, | |
at: [(width / 100) * 5, (height / 10) * 7.15], | |
width: (width / 100) * 90, | |
height: (height / 10) * 3.55, | |
overflow: :shrink_to_fit, | |
style: :bold, | |
align: :center, | |
valign: :center, | |
min_font_size: 1, | |
disable_wrap_by_char: true | |
) | |
font_sizes[:value].nil? ? box.render(dry_run: true) : box.render | |
if box.instance_eval { @font_size } < @min_font_sizes[:value] && value.lines.count == 1 | |
@min_font_sizes[:value] = box.instance_eval { @font_size } | |
end | |
# Percentage text | |
box = Prawn::Text::Formatted::Box.new( | |
get_font_value(percentage, font_sizes[:percentage]), | |
document: @pdf, | |
at: [(width / 100) * 10, (height / 10) * 3.05], | |
width: (width / 100) * 80, | |
height: (height / 10) * 2, | |
overflow: :shrink_to_fit, | |
style: :bold, | |
align: :center, | |
valign: :center, | |
min_font_size: 1, | |
disable_wrap_by_char: true | |
) | |
font_sizes[:percentage].nil? ? box.render(dry_run: true) : box.render | |
if box.instance_eval { @font_size } < @min_font_sizes[:percentage] | |
@min_font_sizes[:percentage] = box.instance_eval { @font_size } | |
end | |
end | |
def get_field_colors | |
if @data_object['portionSize'].is_a?(String) | |
@data_object['portionSize'] = @data_object['portionSize'].to_f | |
end | |
@data_object['values'].each do |key, value| | |
# Convert any strings to floats | |
if value.is_a?(String) | |
value = value.to_f | |
end | |
if value.is_a?(Hash) | |
if value['kcal'].is_a?(String) | |
value['kcal'] = value['kcal'].to_f | |
end | |
if value['kJ'].is_a?(String) | |
value['kJ'] = value['kJ'].to_f | |
end | |
adjusted_value = value['kJ'] / (@data_object['portionSize'] / 100.0) | |
else | |
adjusted_value = value / (@data_object['portionSize'] / 100.0) | |
end | |
case key | |
when 'energy' | |
@data_object['values'][key] = {value: value, color: 'FFFFFF'} | |
@data_object['values'][key][:percentage] = "#{((value['kJ'] / 8400.0) * 100).round}%" | |
@data_object['values'][key][:index] = 0 | |
when 'fat' | |
if @data_object['type'] === 'food' | |
if adjusted_value > 17.5 || value > 21 | |
@data_object['values'][key] = {value: value, color: 'e16b5f'} | |
elsif adjusted_value > 3 && adjusted_value <= 17.5 | |
@data_object['values'][key] = {value: value, color: 'f7ca87'} | |
elsif adjusted_value <= 3.0 | |
@data_object['values'][key] = {value: value, color: 'a7cd7e'} | |
end | |
else | |
if adjusted_value > 8.75 || value > 10.5 | |
@data_object['values'][key] = {value: value, color: 'e16b5f'} | |
elsif adjusted_value > 1.5 && adjusted_value <= 8.75 | |
@data_object['values'][key] = {value: value, color: 'f7ca87'} | |
elsif adjusted_value <= 1.5 | |
@data_object['values'][key] = {value: value, color: 'a7cd7e'} | |
end | |
end | |
@data_object['values'][key][:percentage] = "#{((value / 70.0) * 100).round}%" | |
@data_object['values'][key][:index] = 1 | |
when 'saturates' | |
if @data_object['type'] === 'food' | |
if adjusted_value > 5 || value > 6 | |
@data_object['values'][key] = {value: value, color: 'e16b5f'} | |
elsif adjusted_value > 1.5 && adjusted_value <= 5 | |
@data_object['values'][key] = {value: value, color: 'f7ca87'} | |
elsif adjusted_value <= 1.5 | |
@data_object['values'][key] = {value: value, color: 'a7cd7e'} | |
end | |
else | |
if adjusted_value > 2.5 || value > 3 | |
@data_object['values'][key] = {value: value, color: 'e16b5f'} | |
elsif adjusted_value > 0.75 && adjusted_value <= 2.5 | |
@data_object['values'][key] = {value: value, color: 'f7ca87'} | |
elsif adjusted_value <= 0.75 | |
@data_object['values'][key] = {value: value, color: 'a7cd7e'} | |
end | |
end | |
@data_object['values'][key][:percentage] = "#{((value / 20.0) * 100).round}%" | |
@data_object['values'][key][:index] = 2 | |
when 'sugars' | |
if @data_object['type'] === 'food' | |
if adjusted_value > 22.5 || value > 27 | |
@data_object['values'][key] = {value: value, color: 'e16b5f'} | |
elsif adjusted_value > 5 && adjusted_value <= 22.5 | |
@data_object['values'][key] = {value: value, color: 'f7ca87'} | |
elsif adjusted_value <= 5 | |
@data_object['values'][key] = {value: value, color: 'a7cd7e'} | |
end | |
else | |
if adjusted_value > 11.25 || value > 13.5 | |
@data_object['values'][key] = {value: value, color: 'e16b5f'} | |
elsif adjusted_value > 2.5 && adjusted_value <= 11.25 | |
@data_object['values'][key] = {value: value, color: 'f7ca87'} | |
elsif adjusted_value <= 2.5 | |
@data_object['values'][key] = {value: value, color: 'a7cd7e'} | |
end | |
end | |
@data_object['values'][key][:percentage] = "#{((value / 90.0) * 100).round}%" | |
@data_object['values'][key][:index] = 3 | |
when 'salt' | |
if @data_object['type'] === 'food' | |
if adjusted_value > 1.5 || value > 1.8 | |
@data_object['values'][key] = {value: value, color: 'e16b5f'} | |
elsif adjusted_value > 0.3 && adjusted_value <= 1.5 | |
@data_object['values'][key] = {value: value, color: 'f7ca87'} | |
elsif adjusted_value <= 0.3 | |
@data_object['values'][key] = {value: value, color: 'a7cd7e'} | |
end | |
else | |
if adjusted_value > 0.75 || value > 0.9 | |
@data_object['values'][key] = {value: value, color: 'e16b5f'} | |
elsif adjusted_value > 0.3 && adjusted_value <= 0.75 | |
@data_object['values'][key] = {value: value, color: 'f7ca87'} | |
elsif adjusted_value <= 0.3 | |
@data_object['values'][key] = {value: value, color: 'a7cd7e'} | |
end | |
end | |
@data_object['values'][key][:percentage] = "#{((value / 6.0) * 100).round}%" | |
@data_object['values'][key][:index] = 4 | |
else | |
logger.info("Field type #{key} was not a recognised type!") | |
end | |
end | |
end | |
def get_font_value(text, font_size) | |
(font_size.nil? || text.lines.count > 1) ? [{text: text}] : [{text: text, size: font_size}] | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment