Skip to content

Instantly share code, notes, and snippets.

@Siyfion
Last active August 29, 2015 14:22
Show Gist options
  • Save Siyfion/d92dc707a7f359edff70 to your computer and use it in GitHub Desktop.
Save Siyfion/d92dc707a7f359edff70 to your computer and use it in GitHub Desktop.
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