From 60f35a4aa72eb6022e48f14d3327f377b27bacd8 Mon Sep 17 00:00:00 2001 From: 800a7b32 <1313697+800a7b32@users.noreply.github.com> Date: Wed, 1 Jun 2022 16:58:02 +0000 Subject: [PATCH 01/14] Update documentation to guide development --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index d0759a1..51bb8a5 100644 --- a/README.md +++ b/README.md @@ -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 `png` and `svg`. Defaults to `png`. * `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. From f12e6dbb56a1af4f98c037871ae639fca0f08989 Mon Sep 17 00:00:00 2001 From: 800a7b32 <1313697+800a7b32@users.noreply.github.com> Date: Wed, 1 Jun 2022 16:59:13 +0000 Subject: [PATCH 02/14] bump ruby version --- .ruby-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ruby-version b/.ruby-version index 860487c..49cdd66 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.7.1 +2.7.6 From 91e149cbc25151ab1774f6dd6dbe31e81c36961f Mon Sep 17 00:00:00 2001 From: 800a7b32 <1313697+800a7b32@users.noreply.github.com> Date: Wed, 1 Jun 2022 17:00:36 +0000 Subject: [PATCH 03/14] bump required ruby version in gemspec --- qr-bills.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qr-bills.gemspec b/qr-bills.gemspec index 468f258..d38bb01 100644 --- a/qr-bills.gemspec +++ b/qr-bills.gemspec @@ -18,7 +18,7 @@ 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.6" s.add_runtime_dependency("i18n", ">= 1.8.3", "< 2") s.add_runtime_dependency("rqrcode", ">= 2.1", "< 3") s.add_development_dependency("rspec", "~> 3.9") From b0046bba190c7eb2e1eb872ba10e60564ee9458f Mon Sep 17 00:00:00 2001 From: 800a7b32 <1313697+800a7b32@users.noreply.github.com> Date: Wed, 1 Jun 2022 17:10:16 +0000 Subject: [PATCH 04/14] create spec for qr-prawn --- spec/qr-prawn-layout_spec.rb | 134 +++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 spec/qr-prawn-layout_spec.rb diff --git a/spec/qr-prawn-layout_spec.rb b/spec/qr-prawn-layout_spec.rb new file mode 100644 index 0000000..f59ff10 --- /dev/null +++ b/spec/qr-prawn-layout_spec.rb @@ -0,0 +1,134 @@ +require 'i18n' +require 'fileutils' +require 'qr-bills/qr-prawn-layout' +require 'qr-bills/qr-params' + +RSpec.configure do |config| + config.before(:each) do + @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] = 'png' + @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] = 'png' + end + + it "successfully generates prawn/ruby layout + qr code" do + expect{QRPRAWNLayout.create(@params)}.not_to raise_error + end + + it "generates legacy png qrcode" do + @params[:qrcode_format] = nil + @params[:qrcode_filepath] = "#{Dir.pwd}/tmp/qrcode-html.png" + + IO.binwrite("#{Dir.pwd}/tmp/prawn-layout.pdf", QRPRAWNLayout.create(@params).to_s) + expect(File.exist?(filepath)).to be_truthy + expect(File.exist?("#{Dir.pwd}/tmp/qrcode-html.png")).to be_truthy + end + + it "generates png qrcode" do + prawn_output = QRPRAWNLayout.create(@params).to_s + IO.binwrite(filepath, prawn_output) + expect(File.exist?(filepath)).to be_truthy + + # TODO + # Parse generated PDF and determine if it has QR code as ChunkyPNG + # expect(prawn_output).to include("data:image/png;base64,") + end + + it "generates svg qrcode" do + @params[:qrcode_format] = 'svg' + + prawn_output = QRPRAWNLayout.create(@params).to_s + IO.binwrite(filepath, prawn_output) + expect(File.exist?(filepath)).to be_truthy + + # TODO + # Parse generated PDF and determine if it has QR code as svg + # expect(prawn_output).to include("data:image/svg+xml;charset=utf-8,") + end + + it "does not overwrite locale" do + @params[:bill_params][:language] = :de + + QRPRAWNLayout.create(@params) + + expect(I18n.locale).to be :it + end + + it "rounds correctly (1)" do + prawn_output = QRPRAWNLayout.create(@params).to_s + + IO.binwrite(filepath, prawn_output) + expect(File.exist?(filepath)).to be_truthy + + # TODO + # expect(prawn_output).to include("12345.15") + end + + it "rounds correctly (2)" do + @params[:bill_params][:amount] = 12345.1 + + prawn_output = QRPRAWNLayout.create(@params).to_s + + IO.binwrite(filepath, prawn_output) + expect(File.exist?(filepath)).to be_truthy + + # TODO + # expect(prawn_output).to include("12345.10") + end + + it "rounds correctly (3)" do + @params[:bill_params][:amount] = 12345.10 + + prawn_output = QRPRAWNLayout.create(@params).to_s + + IO.binwrite(filepath, prawn_output) + expect(File.exist?(filepath)).to be_truthy + + # TODO + # expect(prawn_output).to include("12345.10") + end + end +end From 52bd8c7e731149ab3c84a2c16383fec92640076c Mon Sep 17 00:00:00 2001 From: 800a7b32 <1313697+800a7b32@users.noreply.github.com> Date: Wed, 1 Jun 2022 17:12:52 +0000 Subject: [PATCH 05/14] add case/when to support prawn --- lib/qr-bills.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/qr-bills.rb b/lib/qr-bills.rb index 6602684..d472ef9 100644 --- a/lib/qr-bills.rb +++ b/lib/qr-bills.rb @@ -2,6 +2,7 @@ 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 @@ -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) else QRGenerator.create(qr_params, qr_params[:qrcode_filepath]) end From 708660496a5826dd7e489049b2fcb4ea13cda5ae Mon Sep 17 00:00:00 2001 From: 800a7b32 <1313697+800a7b32@users.noreply.github.com> Date: Wed, 1 Jun 2022 17:15:56 +0000 Subject: [PATCH 06/14] Placeholder for qr-prawn-layout --- lib/qr-bills/qr-prawn-layout.rb | 256 ++++++++++++++++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 lib/qr-bills/qr-prawn-layout.rb diff --git a/lib/qr-bills/qr-prawn-layout.rb b/lib/qr-bills/qr-prawn-layout.rb new file mode 100644 index 0000000..000054c --- /dev/null +++ b/lib/qr-bills/qr-prawn-layout.rb @@ -0,0 +1,256 @@ +require 'qr-bills/qr-generator' + +module QRPRAWNLayout + def self.create(params) + qrcode = QRGenerator.create(params, params[:qrcode_filepath]) + params[:qrcode_filepath] = convert_qrcode_to_data_url(qrcode) + prawn_layout(params) + end + + def self.convert_qrcode_to_data_url(qrcode) + case qrcode + when ChunkyPNG::Image + qrcode.to_data_url + else + # 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 + end + + def self.prawn_layout(params) + I18n.with_locale(params[:bill_params][:language]) do + layout = "
\n" + layout += "
\n" + layout += "
#{I18n.t("qrbills.receipt").capitalize}
\n" + layout += "
#{I18n.t("qrbills.account").capitalize} / #{I18n.t("qrbills.payable_to").capitalize}
\n" + layout += "
\n" + layout += " #{params[:bill_params][:creditor][:iban]}
\n" + layout += render_address(params[:bill_params][:creditor][:address]) + layout += "
\n" + layout += "

\n" + + if !params[:bill_params][:reference].nil? && !params[:bill_params][:reference].empty? + layout += "
#{I18n.t("qrbills.reference").capitalize}
\n" + layout += "
\n" + layout += " #{params[:bill_params][:reference]}
\n" + layout += "
\n" + layout += "

\n" + end + + layout += "
#{I18n.t("qrbills.payable_by").capitalize}
\n" + layout += "
\n" + layout += render_address(params[:bill_params][:debtor][:address]) + layout += "
\n" + + layout += "
\n" + layout += "
\n" + layout += " #{I18n.t("qrbills.currency").capitalize}
\n" + layout += " #{params[:bill_params][:currency]}
\n" + layout += "
\n" + + layout += "
\n" + layout += " #{I18n.t("qrbills.amount").capitalize}
\n" + layout += " #{format('%.2f', params[:bill_params][:amount])}
\n" + layout += "
\n" + layout += "
\n" + + layout += "
\n" + layout += " #{I18n.t("qrbills.acceptance_point").capitalize}
\n" + layout += "
\n" + + layout += "
\n" + layout += "
\n" + layout += "
\n" + layout += "
#{I18n.t("qrbills.payment_part").capitalize}
\n" + layout += "
\n" + layout += "
\n" + layout += "
\n" + layout += " #{I18n.t("qrbills.currency").capitalize}
\n" + layout += " #{params[:bill_params][:currency]}
\n" + layout += "
\n" + + layout += "
\n" + layout += " #{I18n.t("qrbills.amount").capitalize}
\n" + layout += " #{format('%.2f',params[:bill_params][:amount])}
\n" + layout += "
\n" + layout += "
\n" + + layout += "
\n" + + if !params[:bill_params][:bill_information_coded].nil? && !params[:bill_params][:bill_information_coded].empty? + layout += " #{I18n.t("qrbills.name").capitalize} AV1: #{params[:bill_params][:bill_information_coded]}\n" + end + + if !params[:bill_params][:alternative_scheme_parameters].nil? && !params[:bill_params][:alternative_scheme_parameters].empty? + layout += " #{I18n.t("qrbills.name").capitalize} AV2: #{params[:bill_params][:alternative_scheme_parameters]}\n" + end + + layout += "
\n" + layout += "
\n" + layout += "
\n" + layout += "
#{I18n.t("qrbills.account").capitalize} / #{I18n.t("qrbills.payable_to").capitalize}
\n" + layout += "
\n" + layout += " #{params[:bill_params][:creditor][:iban]}
\n" + layout += render_address(params[:bill_params][:creditor][:address]) + layout += "
\n" + layout += "

\n" + + if !params[:bill_params][:reference].nil? && !params[:bill_params][:reference].empty? + layout += "
#{I18n.t("qrbills.reference").capitalize}
\n" + layout += "
\n" + layout += " #{params[:bill_params][:reference]}
\n" + layout += "
\n" + layout += "

\n" + end + + if !params[:bill_params][:additionally_information].nil? && !params[:bill_params][:additionally_information].empty? + layout += "
#{I18n.t("qrbills.additional_information").capitalize}
\n" + layout += "
\n" + layout += " #{params[:bill_params][:additionally_information]}
\n" + layout += "
\n" + layout += "

\n" + end + + layout += "
#{I18n.t("qrbills.payable_by").capitalize}
\n" + layout += "
\n" + layout += render_address(params[:bill_params][:debtor][:address]) + layout += "
\n" + layout += "
\n" + layout += "
\n" + layout += "
\n" + + layout += "\n" + + layout + end + end + + def self.render_address(address) + case address[:type] + when 'S' + format("%s
\n%s %s
\n%s %s
\n", address[:name], address[:line1], address[:line2], address[:postal_code], address[:town]) + when 'K' + format("%s
\n%s
\n%s
\n", address[:name], address[:line1], address[:line2]) + end + end +end From 222c6592159e46c2f40d6ecd24338c0c2235ac83 Mon Sep 17 00:00:00 2001 From: 800a7b32 <1313697+800a7b32@users.noreply.github.com> Date: Fri, 3 Jun 2022 13:19:32 +0000 Subject: [PATCH 07/14] reduce ruby version --- qr-bills.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qr-bills.gemspec b/qr-bills.gemspec index d38bb01..a5f8325 100644 --- a/qr-bills.gemspec +++ b/qr-bills.gemspec @@ -18,7 +18,7 @@ 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.6" + 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_development_dependency("rspec", "~> 3.9") From cf7e63942b6a1ec03bc3cdaa17256ebc10ebf1fd Mon Sep 17 00:00:00 2001 From: Kaktusyaka Date: Mon, 6 Jun 2022 20:48:37 +1000 Subject: [PATCH 08/14] added prawn template for swiss invoice --- lib/qr-bills.rb | 4 +- lib/qr-bills/qr-prawn-layout.rb | 362 +++++++++++--------------------- qr-bills.gemspec | 3 + 3 files changed, 132 insertions(+), 237 deletions(-) diff --git a/lib/qr-bills.rb b/lib/qr-bills.rb index d472ef9..5f97401 100644 --- a/lib/qr-bills.rb +++ b/lib/qr-bills.rb @@ -6,7 +6,7 @@ require 'qr-bills/qr-creditor-reference' module QRBills - def self.generate(qr_params) + def self.generate(qr_params, pdf = nil) 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) @@ -21,7 +21,7 @@ def self.generate(qr_params) when 'html' QRHTMLLayout.create(qr_params) when 'prawn' - QRPRAWNLayout.create(qr_params) + QRPRAWNLayout.create(qr_params, pdf) else QRGenerator.create(qr_params, qr_params[:qrcode_filepath]) end diff --git a/lib/qr-bills/qr-prawn-layout.rb b/lib/qr-bills/qr-prawn-layout.rb index 000054c..6a89460 100644 --- a/lib/qr-bills/qr-prawn-layout.rb +++ b/lib/qr-bills/qr-prawn-layout.rb @@ -1,256 +1,148 @@ require 'qr-bills/qr-generator' +require 'prawn' +require 'prawn-svg' +require "prawn/measurement_extensions" module QRPRAWNLayout - def self.create(params) + attr_reader :document, :type + + def self.create(params, pdf) qrcode = QRGenerator.create(params, params[:qrcode_filepath]) params[:qrcode_filepath] = convert_qrcode_to_data_url(qrcode) - prawn_layout(params) + prawn_layout(params, pdf) end def self.convert_qrcode_to_data_url(qrcode) - case qrcode - when ChunkyPNG::Image - qrcode.to_data_url - else - # 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 + # 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) - I18n.with_locale(params[:bill_params][:language]) do - layout = "
\n" - layout += "
\n" - layout += "
#{I18n.t("qrbills.receipt").capitalize}
\n" - layout += "
#{I18n.t("qrbills.account").capitalize} / #{I18n.t("qrbills.payable_to").capitalize}
\n" - layout += "
\n" - layout += " #{params[:bill_params][:creditor][:iban]}
\n" - layout += render_address(params[:bill_params][:creditor][:address]) - layout += "
\n" - layout += "

\n" - - if !params[:bill_params][:reference].nil? && !params[:bill_params][:reference].empty? - layout += "
#{I18n.t("qrbills.reference").capitalize}
\n" - layout += "
\n" - layout += " #{params[:bill_params][:reference]}
\n" - layout += "
\n" - layout += "

\n" - end - - layout += "
#{I18n.t("qrbills.payable_by").capitalize}
\n" - layout += "
\n" - layout += render_address(params[:bill_params][:debtor][:address]) - layout += "
\n" - - layout += "
\n" - layout += "
\n" - layout += " #{I18n.t("qrbills.currency").capitalize}
\n" - layout += " #{params[:bill_params][:currency]}
\n" - layout += "
\n" - - layout += "
\n" - layout += " #{I18n.t("qrbills.amount").capitalize}
\n" - layout += " #{format('%.2f', params[:bill_params][:amount])}
\n" - layout += "
\n" - layout += "
\n" - - layout += "
\n" - layout += " #{I18n.t("qrbills.acceptance_point").capitalize}
\n" - layout += "
\n" - - layout += "
\n" - layout += "
\n" - layout += "
\n" - layout += "
#{I18n.t("qrbills.payment_part").capitalize}
\n" - layout += "
\n" - layout += "
\n" - layout += "
\n" - layout += " #{I18n.t("qrbills.currency").capitalize}
\n" - layout += " #{params[:bill_params][:currency]}
\n" - layout += "
\n" - - layout += "
\n" - layout += " #{I18n.t("qrbills.amount").capitalize}
\n" - layout += " #{format('%.2f',params[:bill_params][:amount])}
\n" - layout += "
\n" - layout += "
\n" - - layout += "
\n" - - if !params[:bill_params][:bill_information_coded].nil? && !params[:bill_params][:bill_information_coded].empty? - layout += " #{I18n.t("qrbills.name").capitalize} AV1: #{params[:bill_params][:bill_information_coded]}\n" - end - - if !params[:bill_params][:alternative_scheme_parameters].nil? && !params[:bill_params][:alternative_scheme_parameters].empty? - layout += " #{I18n.t("qrbills.name").capitalize} AV2: #{params[:bill_params][:alternative_scheme_parameters]}\n" - end - - layout += "
\n" - layout += "
\n" - layout += "
\n" - layout += "
#{I18n.t("qrbills.account").capitalize} / #{I18n.t("qrbills.payable_to").capitalize}
\n" - layout += "
\n" - layout += " #{params[:bill_params][:creditor][:iban]}
\n" - layout += render_address(params[:bill_params][:creditor][:address]) - layout += "
\n" - layout += "

\n" - - if !params[:bill_params][:reference].nil? && !params[:bill_params][:reference].empty? - layout += "
#{I18n.t("qrbills.reference").capitalize}
\n" - layout += "
\n" - layout += " #{params[:bill_params][:reference]}
\n" - layout += "
\n" - layout += "

\n" - end - - if !params[:bill_params][:additionally_information].nil? && !params[:bill_params][:additionally_information].empty? - layout += "
#{I18n.t("qrbills.additional_information").capitalize}
\n" - layout += "
\n" - layout += " #{params[:bill_params][:additionally_information]}
\n" - layout += "
\n" - layout += "

\n" + 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 "#{I18n.t("qrbills.name").capitalize} 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 "#{I18n.t("qrbills.name").capitalize} 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 - - layout += "
#{I18n.t("qrbills.payable_by").capitalize}
\n" - layout += "
\n" - layout += render_address(params[:bill_params][:debtor][:address]) - layout += "
\n" - layout += "
\n" - layout += "
\n" - layout += "
\n" - - layout += "\n" - - layout end end def self.render_address(address) case address[:type] when 'S' - format("%s
\n%s %s
\n%s %s
\n", address[:name], address[:line1], address[:line2], address[:postal_code], address[:town]) + format("%s
%s %s
%s %s
", address[:name], address[:line1], address[:line2], address[:postal_code], address[:town]) when 'K' - format("%s
\n%s
\n%s
\n", address[:name], address[:line1], address[:line2]) + format("%s
%s
%s
", address[:name], address[:line1], address[:line2]) end end end diff --git a/qr-bills.gemspec b/qr-bills.gemspec index a5f8325..c3e048e 100644 --- a/qr-bills.gemspec +++ b/qr-bills.gemspec @@ -21,6 +21,9 @@ Gem::Specification.new do |s| 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") From dbe8d43a2cb46bb9a8791d7be6cf26156bf9c4f3 Mon Sep 17 00:00:00 2001 From: Vitalii Shevtsov Date: Mon, 6 Jun 2022 21:06:48 +1000 Subject: [PATCH 09/14] update docs --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 51bb8a5..69bc54c 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ 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 `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. @@ -127,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 = { From 7602acd2e8501aec236b81b48acf54789990b5d6 Mon Sep 17 00:00:00 2001 From: Vitalii Shevtsov Date: Mon, 6 Jun 2022 21:35:21 +1000 Subject: [PATCH 10/14] fixed tests --- Gemfile.lock | 19 ++++++++++++++++++- lib/qr-bills/qr-prawn-layout.rb | 2 +- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index dc7ee8c..58bf10b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,25 +1,41 @@ 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: + addressable (2.8.0) + public_suffix (>= 2.0.2, < 5.0) byebug (11.1.3) 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) @@ -37,6 +53,7 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.11.0) rspec-support (3.11.0) + ttfunk (1.7.0) PLATFORMS ruby diff --git a/lib/qr-bills/qr-prawn-layout.rb b/lib/qr-bills/qr-prawn-layout.rb index 6a89460..61a7941 100644 --- a/lib/qr-bills/qr-prawn-layout.rb +++ b/lib/qr-bills/qr-prawn-layout.rb @@ -6,7 +6,7 @@ module QRPRAWNLayout attr_reader :document, :type - def self.create(params, pdf) + 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) From b6d81f4f69edbb290d055db2602c40e45732c35b Mon Sep 17 00:00:00 2001 From: Vitalii Shevtsov Date: Mon, 6 Jun 2022 21:52:29 +1000 Subject: [PATCH 11/14] fixed tests for prawn layout --- spec/qr-prawn-layout_spec.rb | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/spec/qr-prawn-layout_spec.rb b/spec/qr-prawn-layout_spec.rb index f59ff10..08deaf6 100644 --- a/spec/qr-prawn-layout_spec.rb +++ b/spec/qr-prawn-layout_spec.rb @@ -5,13 +5,14 @@ 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] = 'png' + @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" @@ -51,24 +52,15 @@ describe "layout generation" do before do - @params[:qrcode_format] = 'png' + @params[:qrcode_format] = 'svg' end it "successfully generates prawn/ruby layout + qr code" do - expect{QRPRAWNLayout.create(@params)}.not_to raise_error - end - - it "generates legacy png qrcode" do - @params[:qrcode_format] = nil - @params[:qrcode_filepath] = "#{Dir.pwd}/tmp/qrcode-html.png" - - IO.binwrite("#{Dir.pwd}/tmp/prawn-layout.pdf", QRPRAWNLayout.create(@params).to_s) - expect(File.exist?(filepath)).to be_truthy - expect(File.exist?("#{Dir.pwd}/tmp/qrcode-html.png")).to be_truthy + expect{QRPRAWNLayout.create(@params, @pdf)}.not_to raise_error end - it "generates png qrcode" do - prawn_output = QRPRAWNLayout.create(@params).to_s + it "generates svg qrcode" do + prawn_output = QRPRAWNLayout.create(@params, @pdf).to_s IO.binwrite(filepath, prawn_output) expect(File.exist?(filepath)).to be_truthy @@ -80,7 +72,7 @@ it "generates svg qrcode" do @params[:qrcode_format] = 'svg' - prawn_output = QRPRAWNLayout.create(@params).to_s + prawn_output = QRPRAWNLayout.create(@params, @pdf).to_s IO.binwrite(filepath, prawn_output) expect(File.exist?(filepath)).to be_truthy @@ -92,13 +84,13 @@ it "does not overwrite locale" do @params[:bill_params][:language] = :de - QRPRAWNLayout.create(@params) + QRPRAWNLayout.create(@params, @pdf) expect(I18n.locale).to be :it end it "rounds correctly (1)" do - prawn_output = QRPRAWNLayout.create(@params).to_s + prawn_output = QRPRAWNLayout.create(@params, @pdf).to_s IO.binwrite(filepath, prawn_output) expect(File.exist?(filepath)).to be_truthy @@ -110,7 +102,7 @@ it "rounds correctly (2)" do @params[:bill_params][:amount] = 12345.1 - prawn_output = QRPRAWNLayout.create(@params).to_s + prawn_output = QRPRAWNLayout.create(@params, @pdf).to_s IO.binwrite(filepath, prawn_output) expect(File.exist?(filepath)).to be_truthy @@ -122,7 +114,7 @@ it "rounds correctly (3)" do @params[:bill_params][:amount] = 12345.10 - prawn_output = QRPRAWNLayout.create(@params).to_s + prawn_output = QRPRAWNLayout.create(@params, @pdf).to_s IO.binwrite(filepath, prawn_output) expect(File.exist?(filepath)).to be_truthy From 71ca08636a5f771874de78735c9403624d267371 Mon Sep 17 00:00:00 2001 From: Vitalii Shevtsov Date: Mon, 6 Jun 2022 22:49:01 +1000 Subject: [PATCH 12/14] fixed all tests --- spec/qr-params_spec.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spec/qr-params_spec.rb b/spec/qr-params_spec.rb index edad0ce..5b9df65 100644 --- a/spec/qr-params_spec.rb +++ b/spec/qr-params_spec.rb @@ -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 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 From 6d0035a773c5c7cfc7aeafb198b7f74ee7b7167d Mon Sep 17 00:00:00 2001 From: Vitalii Shevtsov Date: Mon, 6 Jun 2022 23:06:03 +1000 Subject: [PATCH 13/14] added test for checking svg qr code generated. --- spec/qr-prawn-layout_spec.rb | 53 +++--------------------------------- 1 file changed, 4 insertions(+), 49 deletions(-) diff --git a/spec/qr-prawn-layout_spec.rb b/spec/qr-prawn-layout_spec.rb index 08deaf6..2c5c958 100644 --- a/spec/qr-prawn-layout_spec.rb +++ b/spec/qr-prawn-layout_spec.rb @@ -2,6 +2,7 @@ require 'fileutils' require 'qr-bills/qr-prawn-layout' require 'qr-bills/qr-params' +require 'byebug' RSpec.configure do |config| config.before(:each) do @@ -60,25 +61,13 @@ end it "generates svg qrcode" do - prawn_output = QRPRAWNLayout.create(@params, @pdf).to_s - IO.binwrite(filepath, prawn_output) - expect(File.exist?(filepath)).to be_truthy - - # TODO - # Parse generated PDF and determine if it has QR code as ChunkyPNG - # expect(prawn_output).to include("data:image/png;base64,") - end - - it "generates svg qrcode" do - @params[:qrcode_format] = 'svg' + expect(@params[:qrcode_filepath]).to_not include("data:image/svg+xml;") - prawn_output = QRPRAWNLayout.create(@params, @pdf).to_s + prawn_output = QRPRAWNLayout.create(@params, @pdf) IO.binwrite(filepath, prawn_output) expect(File.exist?(filepath)).to be_truthy - # TODO - # Parse generated PDF and determine if it has QR code as svg - # expect(prawn_output).to include("data:image/svg+xml;charset=utf-8,") + expect(@params[:qrcode_filepath]).to include("data:image/svg+xml;") end it "does not overwrite locale" do @@ -88,39 +77,5 @@ expect(I18n.locale).to be :it end - - it "rounds correctly (1)" do - prawn_output = QRPRAWNLayout.create(@params, @pdf).to_s - - IO.binwrite(filepath, prawn_output) - expect(File.exist?(filepath)).to be_truthy - - # TODO - # expect(prawn_output).to include("12345.15") - end - - it "rounds correctly (2)" do - @params[:bill_params][:amount] = 12345.1 - - prawn_output = QRPRAWNLayout.create(@params, @pdf).to_s - - IO.binwrite(filepath, prawn_output) - expect(File.exist?(filepath)).to be_truthy - - # TODO - # expect(prawn_output).to include("12345.10") - end - - it "rounds correctly (3)" do - @params[:bill_params][:amount] = 12345.10 - - prawn_output = QRPRAWNLayout.create(@params, @pdf).to_s - - IO.binwrite(filepath, prawn_output) - expect(File.exist?(filepath)).to be_truthy - - # TODO - # expect(prawn_output).to include("12345.10") - end end end From c4bb3d0307e3b193bd756d20eac4bc55364885ff Mon Sep 17 00:00:00 2001 From: Vitalii Shevtsov Date: Mon, 6 Jun 2022 23:09:29 +1000 Subject: [PATCH 14/14] remove buybug --- Gemfile.lock | 2 -- qr-bills.gemspec | 1 - spec/qr-prawn-layout_spec.rb | 1 - 3 files changed, 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 58bf10b..97d2e4c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -12,7 +12,6 @@ GEM specs: addressable (2.8.0) public_suffix (>= 2.0.2, < 5.0) - byebug (11.1.3) chunky_png (1.4.0) coderay (1.1.3) concurrent-ruby (1.1.10) @@ -59,7 +58,6 @@ PLATFORMS ruby DEPENDENCIES - byebug pry qr-bills! rake (~> 13.0) diff --git a/qr-bills.gemspec b/qr-bills.gemspec index c3e048e..a3365fe 100644 --- a/qr-bills.gemspec +++ b/qr-bills.gemspec @@ -27,5 +27,4 @@ Gem::Specification.new do |s| 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 diff --git a/spec/qr-prawn-layout_spec.rb b/spec/qr-prawn-layout_spec.rb index 2c5c958..5c8b1b4 100644 --- a/spec/qr-prawn-layout_spec.rb +++ b/spec/qr-prawn-layout_spec.rb @@ -2,7 +2,6 @@ require 'fileutils' require 'qr-bills/qr-prawn-layout' require 'qr-bills/qr-params' -require 'byebug' RSpec.configure do |config| config.before(:each) do