Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support native Prawn template #27

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion .ruby-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.7.1
2.7.6
21 changes: 18 additions & 3 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,25 +1,40 @@
PATH
remote: .
specs:
qr-bills (1.0.7)
qr-bills (1.0.9)
i18n (>= 1.8.3, < 2)
prawn (>= 1, < 3)
prawn-svg
rqrcode (>= 2.1, < 3)

GEM
remote: https://rubygems.org/
specs:
byebug (11.1.3)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why was this removed?

addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
chunky_png (1.4.0)
coderay (1.1.3)
concurrent-ruby (1.1.10)
css_parser (1.11.0)
addressable
diff-lcs (1.5.0)
i18n (1.10.0)
concurrent-ruby (~> 1.0)
method_source (1.0.0)
pdf-core (0.9.0)
prawn (2.4.0)
pdf-core (~> 0.9.0)
ttfunk (~> 1.7)
prawn-svg (0.32.0)
css_parser (~> 1.6)
prawn (>= 0.11.1, < 3)
rexml (~> 3.2)
pry (0.14.1)
coderay (~> 1.1)
method_source (~> 1.0)
public_suffix (4.0.7)
rake (13.0.6)
rexml (3.2.5)
rqrcode (2.1.1)
chunky_png (~> 1.0)
rqrcode_core (~> 1.0)
Expand All @@ -37,12 +52,12 @@ GEM
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.11.0)
rspec-support (3.11.0)
ttfunk (1.7.0)

PLATFORMS
ruby

DEPENDENCIES
byebug
pry
qr-bills!
rake (~> 13.0)
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,15 @@ QRBills.create_creditor_reference("MTR81UUWZYO48NY55NP3")
```ruby
params[:output_params][:format] = "html"
# OR
params[:output_params][:format] = "prawn"
# OR
params[:output_params][:format] = "png"
# OR
params[:output_params][:format] = "svg"
```

* `html` returns a full qr-bill as a html-template string, uses `params[:qrcode_format]` for the qrcode format which supports `png` and `svg`. Defaults to `png`.
* `prawn` returns a full qr-bill in pure Ruby for inclusion in a [Prawn PDF template](https://github.com/prawnpdf/prawn), uses `params[:qrcode_format]` for the qrcode format which supports `svg` ONLY.
* `png` returns the qrcode of the qr-bill as a ChunkyPNG::Image object.
* `svg` returns the qrcode of the qr-bill as a svg string.

Expand Down Expand Up @@ -124,6 +127,10 @@ params[:bill_params][:additionally_information] = "pagamento riparazione
# generate the QR Bill
bill = QRBills.generate(params)

# if params[:output_params][:format] == "prawn" pass `pdf` to QRBills.generate to get a Prawn::Document object
bill = QRBills.generate(params, pdf)


# bill format is given in the params, default is html
# bill has the following format:
# bill = {
Expand Down
5 changes: 4 additions & 1 deletion lib/qr-bills.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
require 'qr-bills/qr-exceptions'
require 'qr-bills/qr-params'
require 'qr-bills/qr-html-layout'
require 'qr-bills/qr-prawn-layout'
require 'qr-bills/qr-creditor-reference'

module QRBills
def self.generate(qr_params)
def self.generate(qr_params, pdf = nil)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the PR! I would find better to pass an additional params directly in the qr_params variable, you can add a new parameter there called [:output][:prawn][:pdf], so we can leave all prawn objects under that place. Or eventually we can create a new section for the inputs like "[:format_inputs][:prawn][:pdf]"

raise ArgumentError, "#{QRExceptions::INVALID_PARAMETERS}: bill type param not set" unless qr_params.has_key?(:bill_type)
raise ArgumentError, "#{QRExceptions::INVALID_PARAMETERS}: validation failed" unless QRParams.valid?(qr_params)

Expand All @@ -19,6 +20,8 @@ def self.generate(qr_params)
output = case qr_params[:output_params][:format]
when 'html'
QRHTMLLayout.create(qr_params)
when 'prawn'
QRPRAWNLayout.create(qr_params, pdf)
else
QRGenerator.create(qr_params, qr_params[:qrcode_filepath])
end
Expand Down
148 changes: 148 additions & 0 deletions lib/qr-bills/qr-prawn-layout.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
require 'qr-bills/qr-generator'
require 'prawn'
require 'prawn-svg'
require "prawn/measurement_extensions"

module QRPRAWNLayout
attr_reader :document, :type

def self.create(params, pdf = nil)
qrcode = QRGenerator.create(params, params[:qrcode_filepath])
params[:qrcode_filepath] = convert_qrcode_to_data_url(qrcode)
prawn_layout(params, pdf)
end

def self.convert_qrcode_to_data_url(qrcode)
# Stolen from sprockets
# https://github.com/rails/sprockets/blob/0f3e0e93dabafa8f3027e8036e40fd08902688c8/lib/sprockets/context.rb#L295-L303
data = CGI.escape(qrcode)
data.gsub!('%3D', '=')
data.gsub!('%3A', ':')
data.gsub!('%2F', '/')
data.gsub!('%27', "'")
data.tr!('+', ' ')

"data:image/svg+xml;charset=utf-8,#{data}"
end

def self.prawn_layout(params, pdf)
pdf.canvas do
I18n.with_locale(params[:bill_params][:language]) do
pdf.bounding_box([0, 105.mm], width: 210.mm, height: 105.mm) do
y_pos = pdf.cursor

# Receipt Panel
pdf.bounding_box([5.mm, y_pos], width: 52.mm, height: 105.mm) do
pdf.move_down 5.mm

pdf.text I18n.t("qrbills.receipt").capitalize, size: 11.pt, style: :bold
pdf.move_down 4.mm

pdf.text "#{I18n.t("qrbills.account").capitalize} / #{I18n.t("qrbills.payable_to").capitalize}", size: 6.pt, style: :bold
pdf.text "#{params[:bill_params][:creditor][:iban]}", size: 8.pt
pdf.text "#{render_address(params[:bill_params][:creditor][:address])}", size: 8.pt, inline_format: true

if !params[:bill_params][:reference].nil? && !params[:bill_params][:reference].empty?
pdf.move_down 4.mm
pdf.text I18n.t("qrbills.reference").capitalize, size: 6.pt, style: :bold
pdf.text "#{params[:bill_params][:reference]}", size: 8.pt
end
pdf.move_down 4.mm

pdf.text I18n.t("qrbills.payable_by").capitalize, size: 6.pt, style: :bold
pdf.text "#{render_address(params[:bill_params][:debtor][:address])}", size: 8.pt, inline_format: true
pdf.move_down 8.mm

bounding_box_cursor = pdf.cursor
pdf.bounding_box([0, bounding_box_cursor], width: 20.mm) do
pdf.text I18n.t("qrbills.currency").capitalize, size: 6.pt, style: :bold
pdf.text "#{params[:bill_params][:currency]}", size: 8.pt
end

pdf.bounding_box([20.mm, bounding_box_cursor], width: 20.mm) do
pdf.text I18n.t("qrbills.amount").capitalize, size: 6.pt, style: :bold
pdf.text "#{format('%.2f', params[:bill_params][:amount])}", size: 8.pt
end

pdf.move_down 6.mm
pdf.text I18n.t("qrbills.acceptance_point"), align: :right, size: 6.pt, style: :bold
end

# Payment Panel - QR code sub-section
pdf.bounding_box([67.mm, y_pos], width: 51.mm, height: 90.mm) do
pdf.move_down 5.mm

pdf.text I18n.t("qrbills.payment_part").capitalize, size: 11.pt, style: :bold
pdf.move_down 5.mm

pdf.svg QRGenerator.build_svg(params[:bill_params]), width: 46.mm, height: 46.mm
pdf.move_down 5.mm

payment_currency_bounding_box_cursor = pdf.cursor
pdf.bounding_box([0, payment_currency_bounding_box_cursor], width: 20.mm) do
pdf.text I18n.t("qrbills.currency").capitalize, size: 8.pt, style: :bold
pdf.text "#{params[:bill_params][:currency]}", size: 10.pt
end

pdf.bounding_box([20.mm, payment_currency_bounding_box_cursor], width: 20.mm) do
pdf.text I18n.t("qrbills.amount").capitalize, size: 8.pt, style: :bold
pdf.text "#{format('%.2f', params[:bill_params][:amount])}", size: 10.pt
end
end

# Payment Panel - Account / Payable to sub-section
pdf. bounding_box([118.mm, y_pos], width: 92.mm, height: 90.mm) do
pdf.move_down 5.mm

pdf.text "#{I18n.t("qrbills.account").capitalize} / #{I18n.t("qrbills.payable_to").capitalize}", size: 8.pt, style: :bold
pdf.text "#{params[:bill_params][:creditor][:iban]}", size: 10.pt
pdf.text "#{render_address(params[:bill_params][:creditor][:address])}", size: 10.pt, inline_format: true

if !params[:bill_params][:reference].nil? && !params[:bill_params][:reference].empty?
pdf.move_down 4.mm
pdf.text "#{I18n.t("qrbills.reference").capitalize}", size: 8.pt, style: :bold
pdf.text "#{params[:bill_params][:reference]}", size: 10.pt
end

if !params[:bill_params][:additionally_information].nil? && !params[:bill_params][:additionally_information].empty?
pdf.move_down 4.mm
pdf.text "#{I18n.t("qrbills.additional_information").capitalize}", size: 8.pt, style: :bold
pdf.text "#{params[:bill_params][:additionally_information]}", size: 10.pt
end
pdf.move_down 4.mm

pdf.text "#{I18n.t("qrbills.payable_by").capitalize}", size: 8.pt, style: :bold
pdf.text "#{render_address(params[:bill_params][:debtor][:address])}", size: 10.pt, inline_format: true
end

# Payment Panel - Further information sub-section
pdf.bounding_box([67.mm, y_pos - 85.mm], width: 138.mm, height: 10.mm) do
if !params[:bill_params][:bill_information_coded].nil? && !params[:bill_params][:bill_information_coded].empty?
pdf.text "<b>#{I18n.t("qrbills.name").capitalize}</b> AV1: #{params[:bill_params][:bill_information_coded]}", size: 7.pt, inline_format: true
end

if !params[:bill_params][:alternative_scheme_parameters].nil? && !params[:bill_params][:alternative_scheme_parameters].empty?
pdf.text "<b>#{I18n.t("qrbills.name").capitalize}</b> AV2: #{params[:bill_params][:alternative_scheme_parameters]}", size: 7.pt, inline_format: true
end
end

pdf.stroke_color("808080")
pdf.dash(2, space: 2)
pdf.stroke_vertical_line 0, 105.mm, at: 62.mm
pdf.stroke_horizontal_line 0, 210.mm, at: 105.mm
pdf.undash

end
end
end
end

def self.render_address(address)
case address[:type]
when 'S'
format("%s<br>%s %s<br>%s %s<br>", address[:name], address[:line1], address[:line2], address[:postal_code], address[:town])
when 'K'
format("%s<br>%s<br>%s<br>", address[:name], address[:line1], address[:line2])
end
end
end
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for this, I would need to test it accordingly

6 changes: 4 additions & 2 deletions qr-bills.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ Gem::Specification.new do |s|
"source_code_uri" => "https://github.com/damoiser/qr-bills",
"wiki_uri" => "https://github.com/damoiser/qr-bills"
}
s.required_ruby_version = ">= 2.7.1"
s.required_ruby_version = ">= 2.7.4"
s.add_runtime_dependency("i18n", ">= 1.8.3", "< 2")
s.add_runtime_dependency("rqrcode", ">= 2.1", "< 3")
s.add_runtime_dependency("prawn", ">= 1", "< 3")
s.add_runtime_dependency("prawn-svg")

s.add_development_dependency("rspec", "~> 3.9")
s.add_development_dependency("rake", "~> 13.0")
s.add_development_dependency("pry")
s.add_development_dependency("byebug")
end
3 changes: 3 additions & 0 deletions spec/qr-params_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,18 @@

it "fails if currency type is empty" do
@params[:bill_params][:currency] = ""
@params[:bill_type] = QRParams::QR_BILL_WITH_QR_REFERENCE
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this and the following rows are been added?

expect{QRParams.base_params_valid?(@params)}.to raise_error(ArgumentError, "QR-bill invalid parameters: currency cannot be blank")
end

it "fails if currency is nil" do
@params[:bill_params][:currency] = nil
@params[:bill_type] = QRParams::QR_BILL_WITH_QR_REFERENCE
expect{QRParams.base_params_valid?(@params)}.to raise_error(ArgumentError, "QR-bill invalid parameters: currency cannot be blank")
end

it "succeeds if the previous params are correctly set" do
@params[:bill_type] = QRParams::QR_BILL_WITH_QR_REFERENCE
expect{QRParams.base_params_valid?(@params)}.not_to raise_error
expect(QRParams.base_params_valid?(@params)).to be_truthy
end
Expand Down
80 changes: 80 additions & 0 deletions spec/qr-prawn-layout_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
require 'i18n'
require 'fileutils'
require 'qr-bills/qr-prawn-layout'
require 'qr-bills/qr-params'

RSpec.configure do |config|
config.before(:each) do
@pdf = Prawn::Document.new(:page_size => 'A4')
@params = QRParams.get_qr_params
@params[:fonts][:eot] = "../web/assets/fonts/LiberationSans-Regular.eot"
@params[:fonts][:woff] = "../web/assets/fonts/LiberationSans-Regular.woff"
@params[:fonts][:ttf] = "../web/assets/fonts/LiberationSans-Regular.ttf"
@params[:fonts][:svg] = "../web/assets/fonts/LiberationSans-Regular.svg"
@params[:locales][:path] = "config/locales/"
@params[:qrcode_format] = 'svg'
@params[:bill_params][:creditor][:iban] = "CH9300762011623852957"
@params[:bill_params][:creditor][:address][:type] = "S"
@params[:bill_params][:creditor][:address][:name] = "Compagnia di assicurazione forma & scalciante"
@params[:bill_params][:creditor][:address][:line1] = "Via cantonale"
@params[:bill_params][:creditor][:address][:line2] = "24"
@params[:bill_params][:creditor][:address][:postal_code] = "3000"
@params[:bill_params][:creditor][:address][:town] = "Lugano"
@params[:bill_params][:creditor][:address][:country] = "CH"
@params[:bill_params][:amount] = 12345.15
@params[:bill_params][:currency] = "CHF"
@params[:bill_params][:debtor][:address][:type] = "S"
@params[:bill_params][:debtor][:address][:name] = "Foobar Barfoot"
@params[:bill_params][:debtor][:address][:line1] = "Via cantonale"
@params[:bill_params][:debtor][:address][:line2] = "25"
@params[:bill_params][:debtor][:address][:postal_code] = "3001"
@params[:bill_params][:debtor][:address][:town] = "Comano"
@params[:bill_params][:debtor][:address][:country] = "CH"
@params[:bill_params][:reference] = "RF89MTR81UUWZYO48NY55NP3"
@params[:bill_params][:reference_type] = "SCOR"
@params[:bill_params][:additionally_information] = "pagamento riparazione monopattino"

I18n.load_path << File.join(@params[:locales][:path], "qrbills.it.yml")
I18n.load_path << File.join(@params[:locales][:path], "qrbills.en.yml")
I18n.load_path << File.join(@params[:locales][:path], "qrbills.de.yml")
I18n.load_path << File.join(@params[:locales][:path], "qrbills.fr.yml")
I18n.default_locale = :it
end
end

RSpec.describe "QRPRAWNLayout" do
before do
FileUtils.mkdir_p "#{Dir.pwd}/tmp/"
File.delete filepath if File.exist?(filepath)
end

let(:filepath) { "#{Dir.pwd}/tmp/prawn-layout.pdf" }

describe "layout generation" do
before do
@params[:qrcode_format] = 'svg'
end

it "successfully generates prawn/ruby layout + qr code" do
expect{QRPRAWNLayout.create(@params, @pdf)}.not_to raise_error
end

it "generates svg qrcode" do
expect(@params[:qrcode_filepath]).to_not include("data:image/svg+xml;")

prawn_output = QRPRAWNLayout.create(@params, @pdf)
IO.binwrite(filepath, prawn_output)
expect(File.exist?(filepath)).to be_truthy

expect(@params[:qrcode_filepath]).to include("data:image/svg+xml;")
end

it "does not overwrite locale" do
@params[:bill_params][:language] = :de

QRPRAWNLayout.create(@params, @pdf)

expect(I18n.locale).to be :it
end
end
end