diff --git a/app/controllers/api/templates_controller.rb b/app/controllers/api/templates_controller.rb index e32b103e78..1b93ed1782 100644 --- a/app/controllers/api/templates_controller.rb +++ b/app/controllers/api/templates_controller.rb @@ -121,29 +121,22 @@ def pdf def process_documents(template, documents_params) return [] if documents_params.blank? - documents_params.map.with_index do |doc_param, _index| - (doc_param[:file].length / 4.0 * 3).ceil - # Validate base64 string + documents_params.map do |doc_param| raise ArgumentError, 'Invalid base64 string format' unless doc_param[:file].match?(%r{\A[A-Za-z0-9+/]*={0,2}\z}) - # Decode base64 file data file_data = Base64.decode64(doc_param[:file]) + detected = Marcel::MimeType.for(StringIO.new(file_data), name: doc_param[:name]) + content_type = detected.to_s.start_with?('image/') ? detected : 'application/pdf' - # Check if the decoded data looks like a PDF - file_data[0..3] if file_data.size >= 4 - - # Create a temporary file-like object file = Tempfile.new(['document', '.pdf']) file.binmode file.write(file_data) file.rewind - # Add original filename file.define_singleton_method(:original_filename) { doc_param[:name] } - file.define_singleton_method(:content_type) { 'application/pdf' } + file.define_singleton_method(:content_type) { content_type } - result = Templates::CreateAttachments.handle_pdf_or_image(template, file, file_data, {}, extract_fields: true) - result + Templates::CreateAttachments.handle_pdf_or_image(template, file, file_data, {}, extract_fields: true) ensure file&.close file&.unlink diff --git a/spec/requests/templates_spec.rb b/spec/requests/templates_spec.rb index da585858b8..72a56d3406 100644 --- a/spec/requests/templates_spec.rb +++ b/spec/requests/templates_spec.rb @@ -227,6 +227,65 @@ stored_field = Template.last.fields.find { |f| f['name'] == 'First Name' } expect(stored_field['prefill']).to eq('employee_first_name') end + + it 'creates a template from a PNG document' do + png_base64 = Base64.strict_encode64(Rails.root.join('spec/fixtures/sample-image.png').binread) + + post '/api/templates/pdf', + headers: { 'x-auth-token': author.access_token.token }, + params: { + name: 'Image Template', + documents: [{ name: 'sample-image.png', file: png_base64 }] + }.to_json, + env: { 'CONTENT_TYPE' => 'application/json' } + + expect(response).to have_http_status(:ok) + + document = Template.last.documents.first + expect(document.content_type).to eq('image/png') + expect(document.preview_images).to be_present + end + + it 'creates a template from a JPEG document' do + jpeg_data = Vips::Image.new_from_file(Rails.root.join('spec/fixtures/sample-image.png').to_s) + .write_to_buffer('.jpg') + + post '/api/templates/pdf', + headers: { 'x-auth-token': author.access_token.token }, + params: { + name: 'Jpeg Template', + documents: [{ name: 'sample-image.jpg', file: Base64.strict_encode64(jpeg_data) }] + }.to_json, + env: { 'CONTENT_TYPE' => 'application/json' } + + expect(response).to have_http_status(:ok) + expect(Template.last.documents.first.content_type).to eq('image/jpeg') + end + + it 'detects a PNG even when the document name ends in .pdf' do + png_base64 = Base64.strict_encode64(Rails.root.join('spec/fixtures/sample-image.png').binread) + + post '/api/templates/pdf', + headers: { 'x-auth-token': author.access_token.token }, + params: base_params.merge(documents: [{ name: 'scan.pdf', file: png_base64 }]).to_json, + env: { 'CONTENT_TYPE' => 'application/json' } + + expect(response).to have_http_status(:ok) + expect(Template.last.documents.first.content_type).to eq('image/png') + end + + it 'returns the existing error for a document that is not a PDF or image' do + post '/api/templates/pdf', + headers: { 'x-auth-token': author.access_token.token }, + params: { + name: 'Bad Template', + documents: [{ name: 'notes.txt', file: Base64.strict_encode64('not a real file') }] + }.to_json, + env: { 'CONTENT_TYPE' => 'application/json' } + + expect(response).to have_http_status(:unprocessable_entity) + expect(response.parsed_body['error']).to eq('Unable to create template') + end end describe 'POST /api/templates/:id/clone' do