This is a test message to test the length of the message box.
Login
ABAP Quick Adobe Forms
Created by Software-Heroes

ABAP Quick - Adobe Forms too big

321

In this little tip we want to look at why, in the worst case, Adobe forms become larger than they should be.

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.


Included topics:
TippAdobe FormsALDSFP
Comments (0)



And further ...

Are you satisfied with the content of the article? We post new content in the ABAP area every Friday and irregularly in all other areas. Take a look at our tools and apps, we provide them free of charge.