ABAP Quick - Adobe Forms too big
In this little tip we want to look at why, in the worst case, Adobe forms become larger than they should be.
Table of contents
In this article we want to address a small bug that makes generated PDF documents larger in the system than they should actually be. In the worst case, the system generates an error message and no longer generates any documents.
Background
Recently, we stumbled across a bug in our system that alerted us to this issue. In particular, it is about Adobe PDFs that are too large, although hardly any content is printed in the forms. This problem was noticed when the Adobe Document Service (ADS) aborted with an error message and no longer wanted to create a form.
The same case worked without any problems in the test system, but it took a long time to generate the document. At the same time, the document was over 36 MB, even though there were only 4 pages in the document and there was a logo in the header. At first glance, this size seemed strange and not appropriate to the content.
The first error was found in the system log (transaction SM21). The message refers to the ADS and a problem when calling the interface:
Theory
After a short debugging session of the form interface and the invocation, the theory arose that the submitted data was to blame for the problem. The form interface was structured generically, so that all data of the process is transferred with each form. This data also includes the attachments of the process, which are passed directly RAW. This data, i.e. the attachments, are not required at all for the creation of this form, the interface seems oversized.
Structure of the demo
To take a closer look at the theory, let's set up a simple demo form and simulate the case. To do this, a form and a form interface must first be set up using transaction SFP.
Scructure
For a clean interface to the form, let's create some data types that we can then use in the actual form interface. First of all we need a structure with which we can give simple information to the form and display it directly in a text field.
Second, we need a table type and a structure that gives additional data to the form, but is not really relevant to the creation. Here we provide the interface with some attachments from the process.
Interface
Before the form is created, we define an interface through which we can transfer the data from outside to the form. To do this, an interface based on DDIC objects must be created in transaction SFP. After creation, we can include the structure and the table type with the standard parameters under IMPORTING.
Form
The form is very simple for test purposes. After integrating the interface, the parameters must be dragged into the context of the form so that they are available and can be used at build time:
We then transfer our information from the structure to the form as a simple text field so that we can also use content from the context:
Test
Now it's time to prove the theory. To do this, we write a small test class that calls up the form with different data and measures the corresponding sizes of the file. We will send different amounts of data to the form to determine a possible correlation. To create a form, there are some standard function modules and logic:
DATA:
ld_functionname TYPE funcname,
ls_outputparams TYPE sfpoutputparams,
ls_docparams TYPE sfpdocparams,
ls_formoutput TYPE fpformoutput.
CALL FUNCTION 'FP_FUNCTION_MODULE_NAME'
EXPORTING
i_name = 'ZBS_DEMO_FORM'
IMPORTING
e_funcname = ld_functionname.
ls_outputparams-getpdf = abap_true.
ls_outputparams-dest = 'LOCL'.
CALL FUNCTION 'FP_JOB_OPEN'
CHANGING
ie_outputparams = ls_outputparams
EXCEPTIONS
cancel = 1
usage_error = 2
system_error = 3
internal_error = 4
OTHERS = 5.
IF sy-subrc <> 0.
ENDIF.
ls_docparams-langu = 'D'.
ls_docparams-country = 'DE'.
ls_docparams-dynamic = abap_true.
CALL FUNCTION ld_functionname
EXPORTING
/1bcdwb/docparams = ls_docparams
is_data = is_data
it_documents = it_documents
IMPORTING
/1bcdwb/formoutput = ls_formoutput
EXCEPTIONS
usage_error = 1
system_error = 2
internal_error = 3
OTHERS = 4.
IF sy-subrc <> 0.
ENDIF.
rd_result = xstrlen( ls_formoutput-pdf ).
CALL FUNCTION 'FP_JOB_CLOSE'
EXCEPTIONS
usage_error = 1
system_error = 2
internal_error = 3
OTHERS = 4.
IF sy-subrc <> 0.
ENDIF.
We only expect the size of the generated PDF as a result in order to compare it. Now we need a method that dynamically provides content in the form of files. To do this, we read a file from the file system and append it to the table again and again.
DATA:
ls_document TYPE zbs_s_demo_document.
TRY.
OPEN DATASET c_dummy_file FOR INPUT IN BINARY MODE.
READ DATASET c_dummy_file INTO ls_document-content.
CLOSE DATASET c_dummy_file.
CATCH cx_root.
CLEAR ls_document-content.
ENDTRY.
ls_document-file_name = c_dummy_file.
ls_document-length = xstrlen( ls_document-content ).
DO id_count TIMES.
INSERT ls_document INTO TABLE rt_result.
ENDDO.
Finally, the complete example of the executable class again. In the main method we implement our test cases, which generate the forms for different sizes. The result and the transmitted content are then compared in the output method:
CLASS zcl_bs_demo_formtest DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
PROTECTED SECTION.
PRIVATE SECTION.
TYPES:
td_num TYPE p LENGTH 16 DECIMALS 4.
CONSTANTS:
c_conversion TYPE i VALUE 1048576,
c_dummy_file TYPE string VALUE `PUT A FILE IN HERE`,
c_header_text TYPE string VALUE 'Test'.
METHODS:
create_pdf_form
IMPORTING
is_data TYPE zbs_s_demo_data
it_documents TYPE zbs_t_demo_document OPTIONAL
RETURNING VALUE(rd_result) TYPE i,
generate_content
IMPORTING
id_count TYPE i
RETURNING VALUE(rt_result) TYPE zbs_t_demo_document,
output_data
IMPORTING
io_out TYPE REF TO if_oo_adt_intrnl_classrun
id_file_length TYPE i
it_documents TYPE zbs_t_demo_document.
ENDCLASS.
CLASS zcl_bs_demo_formtest IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA:
ld_file_length TYPE i,
lt_documents TYPE zbs_t_demo_document.
ld_file_length = create_pdf_form(
is_data = VALUE #( header = c_header_text )
it_documents = VALUE #( )
).
output_data( io_out = out id_file_length = ld_file_length it_documents = lt_documents ).
lt_documents = generate_content( 1 ).
ld_file_length = create_pdf_form(
is_data = VALUE #( header = c_header_text )
it_documents = lt_documents
).
output_data( io_out = out id_file_length = ld_file_length it_documents = lt_documents ).
lt_documents = generate_content( 2 ).
ld_file_length = create_pdf_form(
is_data = VALUE #( header = c_header_text )
it_documents = lt_documents
).
output_data( io_out = out id_file_length = ld_file_length it_documents = lt_documents ).
lt_documents = generate_content( 16 ).
ld_file_length = create_pdf_form(
is_data = VALUE #( header = c_header_text )
it_documents = lt_documents
).
output_data( io_out = out id_file_length = ld_file_length it_documents = lt_documents ).
lt_documents = generate_content( 64 ).
ld_file_length = create_pdf_form(
is_data = VALUE #( header = c_header_text )
it_documents = lt_documents
).
output_data( io_out = out id_file_length = ld_file_length it_documents = lt_documents ).
ENDMETHOD.
METHOD create_pdf_form.
DATA:
ld_functionname TYPE funcname,
ls_outputparams TYPE sfpoutputparams,
ls_docparams TYPE sfpdocparams,
ls_formoutput TYPE fpformoutput.
CALL FUNCTION 'FP_FUNCTION_MODULE_NAME'
EXPORTING
i_name = 'ZBS_DEMO_FORM'
IMPORTING
e_funcname = ld_functionname.
ls_outputparams-getpdf = abap_true.
ls_outputparams-dest = 'LOCL'.
CALL FUNCTION 'FP_JOB_OPEN'
CHANGING
ie_outputparams = ls_outputparams
EXCEPTIONS
cancel = 1
usage_error = 2
system_error = 3
internal_error = 4
OTHERS = 5.
IF sy-subrc <> 0.
ENDIF.
ls_docparams-langu = 'D'.
ls_docparams-country = 'DE'.
ls_docparams-dynamic = abap_true.
CALL FUNCTION ld_functionname
EXPORTING
/1bcdwb/docparams = ls_docparams
is_data = is_data
it_documents = it_documents
IMPORTING
/1bcdwb/formoutput = ls_formoutput
EXCEPTIONS
usage_error = 1
system_error = 2
internal_error = 3
OTHERS = 4.
IF sy-subrc <> 0.
ENDIF.
rd_result = xstrlen( ls_formoutput-pdf ).
CALL FUNCTION 'FP_JOB_CLOSE'
EXCEPTIONS
usage_error = 1
system_error = 2
internal_error = 3
OTHERS = 4.
IF sy-subrc <> 0.
ENDIF.
ENDMETHOD.
METHOD generate_content.
DATA:
ls_document TYPE zbs_s_demo_document.
TRY.
OPEN DATASET c_dummy_file FOR INPUT IN BINARY MODE.
READ DATASET c_dummy_file INTO ls_document-content.
CLOSE DATASET c_dummy_file.
CATCH cx_root.
CLEAR ls_document-content.
ENDTRY.
ls_document-file_name = c_dummy_file.
ls_document-length = xstrlen( ls_document-content ).
DO id_count TIMES.
INSERT ls_document INTO TABLE rt_result.
ENDDO.
ENDMETHOD.
METHOD output_data.
DATA:
ld_add_content TYPE i.
LOOP AT it_documents INTO DATA(ls_document).
ld_add_content = ld_add_content + ls_document-length.
ENDLOOP.
io_out->write( |--- New document with { lines( it_documents ) } lines| ).
io_out->write( |Generated PDF file: { CONV td_num( id_file_length / c_conversion ) } MB| ).
io_out->write( |Additional content: { CONV td_num( ld_add_content / c_conversion ) } MB| ).
ENDMETHOD.
ENDCLASS.
Now to the most exciting part of the test, the actual execution of the test class and the actual result:
What can we now derive from the measurements:
- A file in the content is about 0.5 MB in size
- The more documents we transfer to the PDF, the larger the generated file will be
- The files are not used on the form
- It is very likely that the information will be stored as meta information in the PDF
- Delivered content and file size correlate
- The content appears to be compressed
Learning
Our tip to you is that when creating the form interface, you should be careful not to make it too generic and if you do, only transfer the most necessary information to the form, as this information is also saved in the generated PDF.
In our internal case, the PDF could no longer be generated at some point because the SOAP interface to the ADS was overloaded. It is also hardly possible to send the documents, because the actual 180 KB files were now 36 MB in size.
Conclusion
We hope the tip was helpful for you and you pay more attention to the interfaces you build in the future. Anyway, it's always better to hand over only the most necessary information, who knows who is listening on the other side.