require 'prawn/measurement_extensions' class RotatedCell < Prawn::Table::Cell::Text def initialize(pdf, text, options = {}) options[:content] = text options[:valign] = :center options[:align] = :center options[:rotate_around] = :center @rotation = -options[:rotate] || 0 super(pdf, [0, pdf.cursor], options) end def tan_rotation Math.tan(Math::PI * @rotation / 180) end def skew (height + (border_top_width / 2.0) + (border_bottom_width / 2.0)) / tan_rotation end def styled_width_of(_text) options = @text_options.reject { |k| k == :style } with_font { (@pdf.height_of(@content, options) + padding_top + padding_bottom) / tan_rotation } end def natural_content_height options = @text_options.reject { |k| k == :style } with_font { (@pdf.width_of(@content, options) + padding_top + padding_bottom) * tan_rotation } end def draw_borders(point) @pdf.mask(:line_width, :stroke_color) do x, y = point from = [[x - skew, y + (border_top_width / 2.0)], to = [x, y - height - (border_bottom_width / 2.0)]] @pdf.line_width = @border_widths[3] @pdf.stroke_color = @border_colors[3] @pdf.stroke_line(from, to) @pdf.undash end end def draw_content with_font do with_text_color do text_box(width: spanned_content_width + FPTolerance + skew, height: spanned_content_height + FPTolerance, at: [1 - skew, @pdf.cursor]).render end end end end class RenderPdf < Prawn::Document include ActionView::Helpers::NumberHelper include ApplicationHelper TOP_MARGIN = 36 BOTTOM_MARGIN = 23 HEADER_SPACE = 9 FOOTER_SPACE = 3 HEADER_FONT_SIZE = 16 FOOTER_FONT_SIZE = 8 DEFAULT_FONT = 'OpenSans' def initialize(options = {}) options[:font_size] ||= FoodsoftConfig[:pdf_font_size].try(:to_f) || 12 options[:page_size] ||= FoodsoftConfig[:pdf_page_size] || 'A4' options[:skip_page_creation] = true @options = options @first_page = true no_footer = @options&.[](:no_footer) ? true : false super(options) # Use ttf for better utf-8 compability font_families.update( 'OpenSans' => { bold: font_path('OpenSans-Bold.ttf'), italic: font_path('OpenSans-Italic.ttf'), bold_italic: font_path('OpenSans-BoldItalic.ttf'), normal: font_path('OpenSans-Regular.ttf') } ) header = options[:title] || title footer = I18n.l(Time.now, format: :long) unless no_footer header_size = 0 header_size = height_of(header, size: HEADER_FONT_SIZE, font: DEFAULT_FONT) + HEADER_SPACE if header footer_size = no_footer ? 0 : height_of(footer, size: FOOTER_FONT_SIZE, font: DEFAULT_FONT) + FOOTER_SPACE start_new_page(top_margin: TOP_MARGIN + header_size, bottom_margin: BOTTOM_MARGIN + footer_size) font DEFAULT_FONT repeat :all, dynamic: true do bounding_box [bounds.left, bounds.top + header_size], width: bounds.width, height: header_size do text header, size: HEADER_FONT_SIZE, align: :center, overflow: :shrink_to_fit if header end unless no_footer font_size FOOTER_FONT_SIZE do bounding_box [bounds.left, bounds.bottom - FOOTER_SPACE], width: bounds.width, height: footer_size do text footer, align: :left, valign: :bottom end bounding_box [bounds.left, bounds.bottom - FOOTER_SPACE], width: bounds.width, height: footer_size do text I18n.t('lib.render_pdf.page', number: page_number, count: page_count), align: :right, valign: :bottom end end end end end def title nil end def to_pdf body # Add content, which is defined in subclasses render # Render pdf end # @todo avoid underscore instead of unicode whitespace in pdf :/ def number_to_currency(number, options = {}) super(number, options).gsub("\u202f", ' ') if number end def font_size(points = nil, &block) points *= @options[:font_size] / 12 if points super(points, &block) end # add pagebreak or vertical whitespace, depending on configuration def down_or_page(space = 10) if @first_page @first_page = false return end if pdf_add_page_breaks? start_new_page else move_down space end end protected def fontsize(size) size end # return whether pagebreak or vertical whitespace is used for breaks def pdf_add_page_breaks?(docid = nil) docid ||= self.class.name.underscore cfg = FoodsoftConfig[:pdf_add_page_breaks] case cfg when Array cfg.index(docid.to_s).any? when Hash cfg[docid.to_s] else cfg end end def font_path(name) Rails.root.join('vendor', 'assets', 'fonts', name) end end