diff --git a/lib/submissions/create_from_pdf.rb b/lib/submissions/create_from_pdf.rb index 09678122..81bb65c9 100644 --- a/lib/submissions/create_from_pdf.rb +++ b/lib/submissions/create_from_pdf.rb @@ -14,55 +14,43 @@ module Submissions template = nil attrs = params.to_h.with_indifferent_access + validate_attrs!(attrs) + + template = build_template_with_documents(user, attrs) + submission = create_submission(template, user, attrs) + + clone_documents_to_submission(template, submission) + enqueue_events(submission) + + submission + rescue Templates::CreateAttachments::PdfEncrypted + raise Error, 'PDF encrypted' + rescue DownloadUtils::UnableToDownload => e + raise Error, e.message + rescue HexaPDF::Error, Pdfium::PdfiumError => e + raise Error, "Invalid PDF: #{e.message}" + ensure + archive_template(template) if template&.persisted? && template.archived_at.blank? + end + + def validate_attrs!(attrs) raise Error, 'documents are required' if attrs[:documents].blank? raise Error, 'submitters are required' if attrs[:submitters].blank? raise Error, 'template_ids are not supported by this endpoint yet' if attrs[:template_ids].present? + end + def build_template_with_documents(user, attrs) template = build_template(user, attrs) documents_attrs = sorted_documents_attrs(attrs[:documents]) documents_info = attach_documents(template, documents_attrs, attrs) - documents = documents_info.pluck(:attachment) - template.schema = documents.map.with_index do |document, index| - { - attachment_uuid: document.uuid, - name: documents_attrs[index][:name].presence || document.filename.base - } - end + template.schema = build_schema(documents_info, documents_attrs) template.submitters = build_template_submitters(attrs) template.fields = build_fields(template, documents_info) raise Error, 'PDF does not contain fields' if template.fields.blank? - template.save! - - submissions = Submissions.create_from_submitters( - template: template, - user: user, - source: :api, - with_template: false, - submitters_order: attrs[:order] || attrs[:submitters_order] || - Submissions::DEFAULT_SUBMITTERS_ORDER, - submissions_attrs: [submission_attrs(attrs)] - ) - - submissions.each { |submission| clone_documents_to_submission(template, submission) } - - WebhookUrls.enqueue_events(submissions, 'submission.created') - Submissions.send_signature_requests(submissions) - SearchEntries.enqueue_reindex(submissions) - - template.update!(archived_at: Time.current) - - submissions.first - rescue Templates::CreateAttachments::PdfEncrypted - raise Error, 'PDF encrypted' - rescue DownloadUtils::UnableToDownload => e - raise Error, e.message - rescue HexaPDF::Error, Pdfium::PdfiumError => e - raise Error, "Invalid PDF: #{e.message}" - ensure - archive_template(template) if template&.persisted? && template.archived_at.blank? + template.tap(&:save!) end def build_template(user, attrs) @@ -78,6 +66,36 @@ module Submissions ) end + def build_schema(documents_info, documents_attrs) + documents_info.pluck(:attachment).map.with_index do |document, index| + { + attachment_uuid: document.uuid, + name: documents_attrs[index][:name].presence || document.filename.base + } + end + end + + def create_submission(template, user, attrs) + Submissions.create_from_submitters( + template: template, + user: user, + source: :api, + with_template: false, + submitters_order: submitters_order(attrs), + submissions_attrs: [submission_attrs(attrs)] + ).first + end + + def submitters_order(attrs) + attrs[:order] || attrs[:submitters_order] || Submissions::DEFAULT_SUBMITTERS_ORDER + end + + def enqueue_events(submission) + WebhookUrls.enqueue_events([submission], 'submission.created') + Submissions.send_signature_requests([submission]) + SearchEntries.enqueue_reindex([submission]) + end + def attach_documents(template, documents_attrs, params) documents_attrs.map do |document_attrs| file, text_tags = build_uploaded_file(document_attrs, remove_tags: remove_tags?(params)) @@ -139,9 +157,10 @@ module Submissions def build_template_submitters(attrs) attrs[:submitters].map.with_index do |submitter_attrs, index| + name = submitter_attrs[:role].presence || submitter_attrs[:name].presence || default_submitter_name(index) + { - 'name' => submitter_attrs[:role].presence || submitter_attrs[:name].presence || - default_submitter_name(index), + 'name' => name, 'uuid' => SecureRandom.uuid, 'order' => submitter_attrs[:order] }.compact diff --git a/lib/templates/find_text_tags.rb b/lib/templates/find_text_tags.rb index 7c9f45e8..a228dfd0 100644 --- a/lib/templates/find_text_tags.rb +++ b/lib/templates/find_text_tags.rb @@ -49,19 +49,21 @@ module Templates end def group_text_nodes_by_line(text_nodes) - text_nodes.each_with_object([]) do |node, lines| - line = lines.find { |items| same_line?(items.first, node) } + lines = text_nodes.each_with_object([]) do |node, items| + line = items.find { |line_items| same_line?(line_items.first, node) } if line line << node else - lines << [node] + items << [node] end - end.map { |line| line.sort_by(&:x) } + end + + lines.map { |line| line.sort_by(&:x) } end - def same_line?(a, b) - (a.endy - b.endy).abs < [a.h, b.h].max + def same_line?(first_node, second_node) + (first_node.endy - second_node.endy).abs < [first_node.h, second_node.h].max end def parse_tag(text) diff --git a/lib/templates/remove_text_tags.rb b/lib/templates/remove_text_tags.rb index 3cb5936f..9818d5f5 100644 --- a/lib/templates/remove_text_tags.rb +++ b/lib/templates/remove_text_tags.rb @@ -11,25 +11,37 @@ module Templates pdf = HexaPDF::Document.new(io: StringIO.new(data)) + cover_tags(pdf, tags) + write_pdf(pdf) + end + + def cover_tags(pdf, tags) tags.group_by { |tag| tag[:area]['page'] }.each do |page_index, page_tags| page = pdf.pages[page_index] next unless page - canvas = page.canvas(type: :overlay) - box = page.box + page_tags.each { |tag| cover_tag(page, tag) } + end + end + + def cover_tag(page, tag) + page.canvas(type: :overlay) + .fill_color('white') + .rectangle(*tag_rect(page.box, tag[:area])) + .fill + end - page_tags.each do |tag| - area = tag[:area] - x = [(area['x'] - PADDING) * box.width, 0].max - y = box.height - ((area['y'] + area['h'] + PADDING) * box.height) - w = [(area['w'] + (PADDING * 2)) * box.width, box.width - x].min - h = (area['h'] + (PADDING * 2)) * box.height + def tag_rect(box, area) + x = [(area['x'] - PADDING) * box.width, 0].max + y = box.height - ((area['y'] + area['h'] + PADDING) * box.height) + w = [(area['w'] + (PADDING * 2)) * box.width, box.width - x].min + h = (area['h'] + (PADDING * 2)) * box.height - canvas.fill_color('white').rectangle(x, y, w, h).fill - end - end + [x, y, w, h] + end + def write_pdf(pdf) io = StringIO.new pdf.write(io, incremental: false, validate: false)