Technical Articles
Merging Multiple Adobe Forms and Downloading it as a single PDF
Hi fellow ABAPERS, sometimes we could a client requirement of merging multiple adobe forms and displaying it as one single pdf to the end-user.
However, after doing a lot of googling and reading numerous threads, I came across various solutions but some gave output as spool or some merged the forms but didn’t gave user to download the generated pdf into the system with desired location and name.
So, here I’ll provide the complete end-to-end solution with steps as well as the complete code, so that it really helps you. So let’s get started..
STEP 1:
Call function module FP_JOB_OPEN, with below parameters-
Data:ls_fp_outputparams TYPE sfpoutputparams. ls_fp_outputparams-dest = 'LP01'"Depends on user settings ls_fp_outputparams-nodialog = abap_true. ls_fp_outputparams-preview = abap_false. ls_fp_outputparams-getpdf = 'M'."(Default value = 'X') ls_fp_outputparams-assemble = 'S'."bigger data ls_fp_outputparams-bumode = 'M'."This is Bundle Mode ls_fp_outputparams-reqnew = abap_true. CALL FUNCTION 'FP_JOB_OPEN' CHANGING ie_outputparams = ls_fp_outputparams EXCEPTIONS cancel = 1 usage_error = 2 system_error = 3 internal_error = 4 OTHERS = 5. IF sy-subrc <> 0. MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4. RETURN. ENDIF. *Remember all the values should be passed exactly the same in order to achieve the functionality.
STEP 2:
Get the Adobe Form function Module using FP_FUNCTION_MODULE_NAME and call the generated unction module for your 1st Adobe Form.
Data:lv_fm_inv TYPE rs38l_fnam, ls_fp_docparams TYPE sfpdocparams, ls_pdf_file TYPE fpformoutput. TRY. CALL FUNCTION 'FP_FUNCTION_MODULE_NAME' EXPORTING i_name = 'ZFORM1' IMPORTING e_funcname = lv_fm_inv. CATCH cx_fp_api_repository. CATCH cx_fp_api_usage. CATCH cx_fp_api_internal. ENDTRY. *&--- Call the generated function module CALL FUNCTION lv_fm_inv EXPORTING /1bcdwb/docparams = ls_fp_docparams IMPORTING /1bcdwb/formoutput = ls_pdf_file EXCEPTIONS usage_error = 1 system_error = 2 internal_error = 3. IF sy-subrc <> 0. * <error handling> MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4. RETURN. ENDIF.
STEP 3:
Repeat Step 2, for 2nd Adobe Form. Call ‘FP_JOB_CLOSE’ to close the spool job.
*&---- Close the spool job CALL FUNCTION 'FP_JOB_CLOSE' * IMPORTING * E_RESULT = EXCEPTIONS usage_error = 1 system_error = 2 internal_error = 3 OTHERS = 4. IF sy-subrc <> 0. * <error handling> MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4. RETURN. ENDIF.
Note: You can use LOOP..ENDLOOP to call FP_FUNCTION_MODULE_NAME and it’s generated function module if you have more than two adobe forms.
TYPES: BEGIN OF lty_forms, form TYPE fpname, END OF lty_forms, lty_t_forms TYPE STANDARD TABLE OF lty_forms WITH EMPTY KEY.. Data:lt_forms type lty_t_forms. LOOP AT lt_forms INTO DATA(lwa_forms). TRY. CALL FUNCTION 'FP_FUNCTION_MODULE_NAME' EXPORTING i_name = lwa_forms-form IMPORTING e_funcname = lv_fm_name. CATCH cx_fp_api. ENDTRY. CALL FUNCTION lv_fm_name EXPORTING /1bcdwb/docparams = ls_fp_docparams iv_final = lwa_final IMPORTING /1bcdwb/formoutput =ls_pdf_file EXCEPTIONS usage_error = 1 system_error = 2 internal_error = 3 OTHERS = 4. IF sy-subrc <> 0. ENDIF. CLEAR: lwa_forms, lv_fm_name. ENDLOOP
STEP 4:
Call function module,’ FP_GET_PDF_TABLE’, to merge multiple generated files into a single pdf.
DATA:lt_data TYPE STANDARD TABLE OF tabl1024, "RAWSTRING
lo_pdf_merger TYPE REF TO cl_rspo_pdf_merge,
lt_pdf_table TYPE tfpcontent,
lv_merged_document TYPE xstring,
lv_len TYPE i,
lv_rc TYPE i VALUE 0.
*&&-- Merging different PDF files into one
CREATE OBJECT lo_pdf_merger.
CALL FUNCTION 'FP_GET_PDF_TABLE'
IMPORTING
e_pdf_table = lt_pdf_table.
We will use standard class cl_rspo_pdf_merge methods which can be
reffered from the program:RSPO_TEST_MERGE_PDF_FILES.The same class
can be used to write to a SAP application server.
* Add documents to attribute table of PDF merger
LOOP AT lt_pdf_table INTO DATA(lwa_form).
lo_pdf_merger->add_document( lwa_form ).
ENDLOOP.
* Call kernel method to do the merge of the specified files.
lo_pdf_merger->merge_documents( IMPORTING merged_document = lv_merged_document
rc = lv_rc ).
CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
EXPORTING
buffer = lv_merged_document
IMPORTING
output_length = lv_len
TABLES
binary_tab = lt_data.
Step 5:
We will call the method cl_gui_frontend_services=>file_save_dialog, which gives user choice to name and save the pdf at the desired location.
DATA:lv_file TYPE string, lv_path TYPE string, lv_file_name TYPE string.
CALL METHOD cl_gui_frontend_services=>file_save_dialog EXPORTING window_title = 'Save Form' "You can pass any value as per your choice default_extension = '.pdf' default_file_name = 'Invoice.pdf' prompt_on_overwrite = 'X' CHANGING filename = lv_file_name path = lv_path fullpath = lv_file EXCEPTIONS cntl_error = 1 error_no_gui = 2 not_supported_by_gui = 3 invalid_default_file_name = 4 OTHERS = 5. IF sy-subrc <> 0. * Implement suitable error handling here ENDIF
. STEP 6:
Finally,We will call the method cl_gui_frontend_services=>gui_download, for downloading the file and cl_gui_frontend_services=>execute, to automatically open the pdf, after it is downloaded.
DATA:lt_data TYPE STANDARD TABLE OF tabl1024, lv_file TYPE string, lv_len TYPE i, lv_rc TYPE i VALUE 0
CALL METHOD cl_gui_frontend_services=>gui_download EXPORTING bin_filesize = lv_len filename = lv_file filetype = 'BIN' CHANGING data_tab = lt_data EXCEPTIONS file_write_error = 1 no_batch = 2 gui_refuse_filetransfer = 3 invalid_type = 4 no_authority = 5 unknown_error = 6 header_not_allowed = 7 separator_not_allowed = 8 filesize_not_allowed = 9 header_too_long = 10 dp_error_create = 11 dp_error_send = 12 dp_error_write = 13 unknown_dp_error = 14 access_denied = 15 dp_out_of_memory = 16 disk_full = 17 dp_timeout = 18 file_not_found = 19 dataprovider_exception = 20 control_flush_error = 21 not_supported_by_gui = 22 error_no_gui = 23 OTHERS = 24. IF sy-subrc IS NOT INITIAL. ENDIF. CALL METHOD cl_gui_frontend_services=>execute EXPORTING document = lv_file synchronous = 'X' EXCEPTIONS cntl_error = 1 error_no_gui = 2 bad_parameter = 3 file_not_found = 4 path_not_found = 5 file_extension_unknown = 6 error_execute_failed = 7 synchronous_failed = 8 not_supported_by_gui = 9 OTHERS = 10. IF sy-subrc IS NOT INITIAL. ENDIF.
Below I’m also posting the complete code-
DATA: lv_fm_dpr TYPE rs38l_fnam, " CHAR 30 0 Name of Function Module lv_fm_inv TYPE rs38l_fnam, ls_fp_docparams TYPE sfpdocparams, " Structure SFPDOCPARAMS Short Description Form Parameters for Form Processing ls_fp_outputparams TYPE sfpoutputparams, " Structure SFPOUTPUTPARAMS Short Description Form Processing Output Parameter ls_pdf_file TYPE fpformoutput. DATA:lt_data TYPE STANDARD TABLE OF tabl1024, lo_pdf_merger TYPE REF TO cl_rspo_pdf_merge, lt_pdf_table TYPE tfpcontent, lv_file TYPE string, lv_path TYPE string, lv_file_name TYPE string, lv_merged_document TYPE xstring, lv_len TYPE i, lv_rc TYPE i VALUE 0. ls_fp_outputparams-dest = 'LP01'. "Depends on user settings ls_fp_outputparams-nodialog = abap_true. ls_fp_outputparams-getpdf = 'M'. ls_fp_outputparams-assemble = 'S'. ls_fp_outputparams-bumode = 'M'. ls_fp_outputparams-reqnew = abap_true. * Sets the output parameters and opens the spool job CALL FUNCTION 'FP_JOB_OPEN' "& Form Processing: Call Form CHANGING ie_outputparams = ls_fp_outputparams EXCEPTIONS cancel = 1 usage_error = 2 system_error = 3 internal_error = 4 OTHERS = 5. IF sy-subrc <> 0. MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4. RETURN. ENDIF. TRY. CALL FUNCTION 'FP_FUNCTION_MODULE_NAME' "& Form Processing Generation EXPORTING i_name = 'ZFORM1' IMPORTING e_funcname = lv_fm_inv. CATCH cx_fp_api_repository. CATCH cx_fp_api_usage. CATCH cx_fp_api_internal. ENDTRY. **&---- Get the name of the generated function module TRY. CALL FUNCTION 'FP_FUNCTION_MODULE_NAME' "& Form Processing Generation EXPORTING i_name = 'ZFORM2' IMPORTING e_funcname = lv_fm_dpr. CATCH cx_fp_api_repository. CATCH cx_fp_api_usage. CATCH cx_fp_api_internal. ENDTRY. * Language and country setting (here US as an example) ls_fp_docparams-langu = 'E'. ls_fp_docparams-country = 'US'. *&--- Call the generated function module CALL FUNCTION lv_fm_inv EXPORTING /1bcdwb/docparams = ls_fp_docparams IMPORTING /1bcdwb/formoutput = ls_pdf_file EXCEPTIONS usage_error = 1 system_error = 2 internal_error = 3. IF sy-subrc <> 0. * <error handling> MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4. RETURN. ENDIF. *&--- Call the generated function module CALL FUNCTION lv_fm_dpr EXPORTING /1bcdwb/docparams = ls_fp_docparams IMPORTING /1bcdwb/formoutput = ls_pdf_file EXCEPTIONS usage_error = 1 system_error = 2 internal_error = 3. IF sy-subrc <> 0. * <error handling> MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4. RETURN. ENDIF. * * ENDLOOP. *&---- Close the spool job CALL FUNCTION 'FP_JOB_CLOSE' * IMPORTING * E_RESULT = EXCEPTIONS usage_error = 1 system_error = 2 internal_error = 3 OTHERS = 4. IF sy-subrc <> 0. * <error handling> MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4. RETURN. ENDIF. *&&-- Merging different PDF files into one CREATE OBJECT lo_pdf_merger. CALL FUNCTION 'FP_GET_PDF_TABLE' IMPORTING e_pdf_table = lt_pdf_table. * Add documents to attribute table of PDF merger LOOP AT lt_pdf_table INTO DATA(lwa_form). lo_pdf_merger->add_document( lwa_form ). ENDLOOP. * Call kernel method to do the merge of the specified files. lo_pdf_merger->merge_documents( IMPORTING merged_document = lv_merged_document rc = lv_rc ). CALL FUNCTION 'SCMS_XSTRING_TO_BINARY' EXPORTING buffer = lv_merged_document IMPORTING output_length = lv_len TABLES binary_tab = lt_data. CALL METHOD cl_gui_frontend_services=>file_save_dialog EXPORTING window_title = 'Save Form' default_extension = '.pdf' default_file_name = 'Invoice.pdf' prompt_on_overwrite = 'X' CHANGING filename = lv_file_name path = lv_path fullpath = lv_file EXCEPTIONS cntl_error = 1 error_no_gui = 2 not_supported_by_gui = 3 invalid_default_file_name = 4 OTHERS = 5. IF sy-subrc <> 0. * Implement suitable error handling here ENDIF. CALL METHOD cl_gui_frontend_services=>gui_download EXPORTING bin_filesize = lv_len filename = lv_file filetype = 'BIN' CHANGING data_tab = lt_data EXCEPTIONS file_write_error = 1 no_batch = 2 gui_refuse_filetransfer = 3 invalid_type = 4 no_authority = 5 unknown_error = 6 header_not_allowed = 7 separator_not_allowed = 8 filesize_not_allowed = 9 header_too_long = 10 dp_error_create = 11 dp_error_send = 12 dp_error_write = 13 unknown_dp_error = 14 access_denied = 15 dp_out_of_memory = 16 disk_full = 17 dp_timeout = 18 file_not_found = 19 dataprovider_exception = 20 control_flush_error = 21 not_supported_by_gui = 22 error_no_gui = 23 OTHERS = 24. IF sy-subrc IS NOT INITIAL. ENDIF. CALL METHOD cl_gui_frontend_services=>execute EXPORTING document = lv_file synchronous = 'X' EXCEPTIONS cntl_error = 1 error_no_gui = 2 bad_parameter = 3 file_not_found = 4 path_not_found = 5 file_extension_unknown = 6 error_execute_failed = 7 synchronous_failed = 8 not_supported_by_gui = 9 OTHERS = 10. IF sy-subrc IS NOT INITIAL. ENDIF. CLEAR:lv_file,lv_len,lv_path,lv_file_name,lt_data.
You can also check this in spool request(SP01) and download it to your system as well.I hope you all will find it helpful.
Thanks for reading my first blog post and also please provide your feedback..!!
Regards,
Rohit
Thank you for sharing, we did something similar but also included download of related ZPL or PCL files using FM ADS_SR_READ_CONTENT_TAB.
Best Regards, Daniel
Hi Daniel,
I hope you find it helpful.Also,thanks for showing me a new approach to this. I'll surely look into it.
Regards,
Rohit
Hi Rohit Srivastava @sriv_bytes,
Thanks for the knowledge sharing. This helped me a lot. Much appreciated.
I have done the same and can able to view the Merged PDF into a single PDF document but the pages are wrong as it is showing Page 1 0f 1 for both different Forms i.e., the Order form as Page 1 of 1, suppposed to be Page 1 of 2 and the next page showing the invoice form as Page 1 of 1 but supposed to Page 2 of 2.
How will we achieve this?
Hi Daniel Pereira @daniela.pereira, please share if you have any valuable solution?
Please reply with the technical codes. Thanks.
Hi Manikandan,
Could you achieve this?
I need the same for my current development.
Best Regards
Dear Rohit Srivastava,
Thank you very much for this valuable and quite helpful blog you just wrote!. 🙂
I have gone through the coding you sent - looks really amazing and fits fine with my merging pdf requirements. 🙂
But there's one additional question I'd like to ask in regards of that.
I mean, I would need a PDF/A document to be merged with a NON PDF/A one.
Would the same logic apply for PDF/A documents as well?.
That would be amazing!. 🙂
Hi Rohit,
Your explanations are great. Thanks for that.
What about the pagination for merged PDFs? Assume you have 3 PDFs and merged them. But the pagination happens for each seperate document in itself. Do you have an idea?
Best Regards