Saving ABAP lists to PDF

Have you ever seen an ABAP dump in ST22 and wished you could save it as a PDF, so you (or another developer) could analyse it in colors instead of an ugly plain text file?

Or maybe you wished you could send any other transactions’ list output to someone in a pretty format that wasn’t a fat cumbersome HTML file?

Well, wouldn’t this be nice then?
A new list export option
A dump saved as a PDF
The full dump exported as PDF has only 44 KB!

Here’s how I did it:

1 – Create a new function group based on the standard function group SYSF.

Don’t forget to copy all the text symbols for the languages you use.

Here’s the TOP include (most of it came from the SYSF top include):

FUNCTION-POOL ZNINJA_LISTS MESSAGE-ID 02.

************************************************************************
* Tables                                                               *
************************************************************************
TABLES  RSYSF.
************************************************************************
* Constants                                                            *
************************************************************************
INCLUDE <LIST>.
************************************************************************
* Types                                            *
************************************************************************
TYPE-POOLS: SLIST, abap.

TYPES: BEGIN OF SEARCH_RESULT,
         OFFSET TYPE I,
         LENGTH TYPE I,
       END OF SEARCH_RESULT.
TYPES: SEARCH_RESULT_TAB TYPE SEARCH_RESULT OCCURS 0.


***********************************************************************
*     LIST-Tables
***********************************************************************
INCLUDE <%_LIST>.
DATA: PAGES TYPE SLIST_PAGEDESCR_TAB.
DATA: LIST TYPE SLIST_LIST_TAB WITH HEADER LINE.
DATA: FMBS TYPE SLIST_FMBS_TAB WITH HEADER LINE.
DATA: FMBX TYPE SLIST_FMBS_TAB WITH HEADER LINE.
DATA: FSEL TYPE SLIST_FSEL_TAB WITH HEADER LINE.
DATA: FPOS TYPE SLIST_FPOS_TAB WITH HEADER LINE.

DATA LIST_DESCRIPTION LIKE LISTDS OCCURS 10 WITH HEADER LINE.
DATA PAGE_DESCRIPTION LIKE PAGEDS OCCURS 10 WITH HEADER LINE.

TYPES: BEGIN OF listtable_stack_line,
         list TYPE SLIST_LIST_TAB,
         fmbx TYPE SLIST_FMBS_TAB,
         fsel type slist_fsel_tab,
         fpos type slist_fpos_tab,
       END OF listtable_stack_line.

DATA listtable_stack TYPE TABLE OF listtable_stack_line.
DATA listtable_stack_counter TYPE I VALUE 0.

***********************************************************************
*     LIST-Description
***********************************************************************
DATA: LISTINDEX                      LIKE SY-LSIND,
      START_ROW                      LIKE SY-STARO,
      LINESIZE                       LIKE SY-LINSZ,
      CURRENT_FIRSTLINE              LIKE SY-LILLI,
      CURRENT_LASTLINE               LIKE SY-LILLI,
      CURRENT_LINE                   LIKE SY-LILLI,
      CURRENT_LINES                  LIKE SY-LILLI,
      CURRENT_TOP                    LIKE SY-LILLI,
      CURRENT_HEAD                   LIKE SY-LILLI,
      CURRENT_TITLE                  LIKE SY-LILLI,
      NEW_CURRENT_LINE               LIKE SY-LILLI.

***********************************************************************
* Daten fuer Funktion: Pflegen Ueberschriften in Listen
***********************************************************************
DATA: TITELZEILE(72),
      HEADER1ZEILE TYPE SLIST_MAX_LISTLINE,
      HEADER2ZEILE TYPE SLIST_MAX_LISTLINE,
      HEADER3ZEILE TYPE SLIST_MAX_LISTLINE,
      HEADER4ZEILE TYPE SLIST_MAX_LISTLINE,
      CORRF LIKE SY-SUBRC.

data: filename type string,  "#EC NEEDED
      filefilter type string,
      path type string,      "#EC NEEDED
      fullpath type string.

DATA: BEGIN OF TPOOL OCCURS 5.   "Einlese-Tab - READ TEXTPOOL BINK036480
INCLUDE STRUCTURE TEXTPOOL.                                  "BINK036418
*       ID(1),                                               "BINK036418
*       NO(8),                                               "BINK036418
*       LINE(255),                                           "BINK036418
DATA: END OF TPOOL.                                          "BINK036418

DATA: BEGIN OF TPOOLS OCCURS 5.                   "Save Tpool BINK036480
INCLUDE STRUCTURE TEXTPOOL.                                  "BINK036418
DATA: END OF TPOOLS.                                         "BINK036418

DATA: ENQ(8),                                 "Enqueue auf Programmname
      UFLAG,                                               "Update-Flag
      UFLAGT,                    "Titelidentische Überschrift löschen ?
      SYFCODE LIKE SY-UCOMM,
      SFCODE(4),                                           "Save F-Code
      POPUPANSWER.                                           "BINK036418

***********************************************************************
* Daten fuer Funktion: Suchen String in Listen
***********************************************************************
DATA: BEGIN OF SCAN_STRING,
      SCREEN     LIKE SY-DYNNR VALUE 800,
      COL        LIKE SY-CUCOL VALUE 10,
      ROW        LIKE SY-CUROW VALUE 05,
      START(1)   VALUE 'X',            "X = nur ab aktueller Zeile
      RANGE(1)   VALUE ' ',            "X = nur in der aktuellen Seite
      FUZZY(1)   VALUE ' ',            "X = auch aehnliche Worte suchen
      LIMIT      TYPE I VALUE 100,
      MATCHES    TYPE I VALUE 0,
      END OF SCAN_STRING.

DATA: BEGIN OF SCAN_LIST,
      SCREEN LIKE SY-DYNNR VALUE 810,
      COL1   LIKE SY-CUCOL,
      ROW1   LIKE SY-CUROW,
      COL2   LIKE SY-CUCOL,
      ROW2   LIKE SY-CUROW,
      RC     LIKE SY-SUBRC,
      END OF SCAN_LIST.

DATA: SCAN_MATCHING_LILLI LIKE SY-TABIX,
      SCAN_CURSOR_OFFSET  LIKE SY-CUCOL,
      SCAN_SCOLS          LIKE SY-SCOLS.


DATA: OK_CODE LIKE SY-UCOMM.
DATA: tmp_listline    LIKE %_list_wa.

***********************************************************************
* Daten fuer Funktion: Download Liste
***********************************************************************

2 – Create a report that will write a list exported to memory.

This report is very simple, and it will be used to output the list contents to a spool job, so we can convert it to PDF.

*&---------------------------------------------------------------------*
*& Report  ZDISPLAY_LIST
*& Displays an ABAP list (for output to a spool)
*&---------------------------------------------------------------------*

report  zdisplay_list no standard page heading.

data: t_list type table of abaplist.

"// Get the list contents from memory
"// (list contents were exporte by the caller)
call function 'LIST_FROM_MEMORY'
  tables
    listobject = t_list
  exceptions
    not_found  = 1
    others     = 2.
if sy-subrc <> 0.
  message id sy-msgid type 'E' number sy-msgno
    with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
endif.

"// Write the read list to the current list
"// (this is what will be output to the spool)
call function 'WRITE_LIST'
  exporting
    write_only = 'X'
  tables
    listobject = t_list
  exceptions
    empty_list = 1
    others     = 2.
if sy-subrc <> 0.
  message id sy-msgid type 'E' number sy-msgno
    with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
endif.

3 – Create a new function module to export the current list as a PDF file.

Use the same signature as the DOWNLOAD_LIST function module, excluding the COPY_TO_CLIPBOARD parameter.

This function will do a few things:

  1. Ask for the file path to be saved
  2. Submit our new report to a spool job
  3. Convert the generated spool job to a PDF file
  4. Finally, download the PDF file to the client

So here’s the code:

function zdownload_list_pdf.
*"----------------------------------------------------------------------
*"*"Local Interface:
*"  IMPORTING
*"     VALUE(LIST_INDEX) LIKE  SY-LSIND DEFAULT SLIST_INDEX_DEFAULT
*"     REFERENCE(COPY_TO_CLIPBOARD) TYPE  CHAR1 DEFAULT SPACE
*"  EXCEPTIONS
*"      LIST_INDEX_INVALID
*"      LIST_DOWNLOAD_ERROR
*"----------------------------------------------------------------------

  data: lv_user_action type i,
        lv_filename type string,
        lv_path type string,
        lv_fullpath type string,
        lv_file_encoding type abap_encoding,
        ls_print_parameters type pri_params,
        lv_spool_nr type rspoid,
        lt_pdf type table of tline,
        lv_listname type syplist value 'LIST EXPORT'.

  if list_index = slist_index_default.
    list_index = sy-lsind.
  endif.

  "//
  "// (1) Ask for the file path
  "//
  call function 'GUI_FILE_SAVE_DIALOG'
    exporting
      file_filter       = 'PDF|*.pdf'
      default_extension = 'pdf'
      default_file_name = '.pdf'
    importing
      filename          = lv_filename
      path              = lv_path
      fullpath          = lv_fullpath
      user_action       = lv_user_action.
  check lv_user_action = 1 or lv_user_action = 0.

  "//
  "// (2) Submit the report to output the list to the spool
  "//
  call function 'GET_PRINT_PARAMETERS'
    exporting
      abap_list              = 'X'
      list_name              = lv_listname
      list_text              = 'Exported ABAP list'
      mode                   = 'OKPRI1'
    importing
      out_parameters         = ls_print_parameters
    exceptions
      archive_info_not_found = 1
      invalid_print_params   = 2
      invalid_archive_params = 3
      others                 = 4.
  if sy-subrc <> 0.
    message id sy-msgid type sy-msgty number sy-msgno
            with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
            raising list_download_error.
  endif.

  call function 'LIST_TO_MEMORY'
    exporting
      list_index         = list_index
      force_write        = 'X'
    exceptions
      list_index_invalid = 1
      empty_list         = 2
      others             = 3.
  if sy-subrc <> 0.
    message id sy-msgid type sy-msgty number sy-msgno
            with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
            raising list_download_error.
  endif.

  submit zdisplay_list and return
    to sap-spool
    spool parameters ls_print_parameters
    without spool dynpro.

  commit work and wait. "// Wait until the spool is completely generated

  "// Get the generated spool job number
  select max( rqident ) from tsp01 into lv_spool_nr
    where
      rqclient = sy-mandt and
      rq2name = lv_listname and
      rqowner  = sy-uname.
  if sy-subrc <> 0.
    message 'Spool job not generated' type 'E'
      raising list_download_error.
  endif.

  "//
  "// (3) Convert the spool output to PDF
  "//
  call function 'CONVERT_ABAPSPOOLJOB_2_PDF'
    exporting
      src_spoolid              = lv_spool_nr
      no_dialog                = 'X'
    tables
      pdf                      = lt_pdf
    exceptions
      err_no_abap_spooljob     = 1
      err_no_spooljob          = 2
      err_no_permission        = 3
      err_conv_not_possible    = 4
      err_bad_destdevice       = 5
      user_cancelled           = 6
      err_spoolerror           = 7
      err_temseerror           = 8
      err_btcjob_open_failed   = 9
      err_btcjob_submit_failed = 10
      err_btcjob_close_failed  = 11
      others                   = 12.
  if sy-subrc <> 0.
    message id sy-msgid type sy-msgty number sy-msgno
            with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
            raising list_download_error.
  endif.

  "//
  "// (4) Save the PDF file
  "//
  call function 'GUI_DOWNLOAD'
    exporting
      filename              = lv_fullpath
      filetype              = 'BIN'
    tables
      data_tab              = lt_pdf
    exceptions
      invalid_type          = 03
      no_batch              = 04
      unknown_error         = 05
      others                = 99.
  if sy-subrc ne 0.
    message id sy-msgid type sy-msgty number sy-msgno
     raising list_download_error.
  endif.
endfunction.

5 – Copy the function module LIST_DOWNLOAD to a new function module.

This function module will replace the standard LIST_DOWNLOAD.
Add the PDF format option and add the call to our new function module.

FUNCTION ZLIST_DOWNLOAD_NEW.
*"----------------------------------------------------------------------
*"*"Local Interface:
*"  IMPORTING
*"     VALUE(LIST_INDEX) LIKE  SY-LSIND DEFAULT SLIST_INDEX_DEFAULT
*"     VALUE(METHOD) TYPE  C DEFAULT SPACE
*"  EXCEPTIONS
*"      LIST_DOWNLOAD_ERROR
*"----------------------------------------------------------------------
  DATA: ANSWER.
  DATA: CHOICES LIKE SPOPLI OCCURS 3 WITH HEADER LINE.
  DATA: FLAG_NO_POPUP VALUE SPACE.

  AUTHORITY-CHECK OBJECT 'S_GUI'
           ID 'ACTVT' FIELD '61'.
  IF SY-SUBRC NE 0.
    MESSAGE I013 WITH SY-SUBRC.
    EXIT.
  ENDIF.
  IF LIST_INDEX = SLIST_INDEX_DEFAULT.
    LIST_INDEX = SY-LSIND.
  ENDIF.

  CASE METHOD.
    WHEN 'NOCO'.
      ANSWER = '1'.
      FLAG_NO_POPUP = 'X'.
    WHEN 'DAT'.
      ANSWER = '2'.
      FLAG_NO_POPUP = 'X'.
    WHEN 'RTF'.
      ANSWER = '3'.
      FLAG_NO_POPUP = 'X'.
    WHEN 'HTML'.
      ANSWER = '4'.
      FLAG_NO_POPUP = 'X'.
  ENDCASE.

  CHOICES-VAROPTION = 'unkonvertiert'(ASC).              APPEND CHOICES.
  CHOICES-VAROPTION = 'Tabellenkalkulation'(DAT).        APPEND CHOICES.
  CHOICES-VAROPTION = 'Rich Text Format'(RTF).           APPEND CHOICES.
  CHOICES-VAROPTION = 'HTML Format'(HTM).                APPEND CHOICES.
  CHOICES-VAROPTION = 'In die Zwischenablage'(CLP).      APPEND CHOICES.
  CHOICES-VAROPTION = 'PDF format'(PDF).                 APPEND CHOICES. "// abapninja

  IF FLAG_NO_POPUP NE 'X'.
    CALL FUNCTION 'POPUP_TO_DECIDE_LIST'
         EXPORTING
              CURSORLINE         = 1
*           mark_max           = 1
              TITEL              = 'Liste sichern in Datei...'(DL0)
           TEXTLINE1          = 'In welchem Format soll die Liste'(DL1)
              TEXTLINE2          = 'gesichert werden ?'(DL2)
*           textline3          = text-dl3
         IMPORTING
              ANSWER            = ANSWER
          TABLES
              T_SPOPLI          = CHOICES.
  ENDIF.

  CASE ANSWER.
    WHEN 'A'. EXIT.
    WHEN '1'.
      CALL FUNCTION 'DOWNLOAD_LIST'
           EXPORTING
                LIST_INDEX = LIST_INDEX
           EXCEPTIONS
                LIST_DOWNLOAD_ERROR
                OTHERS.
    IF SY-SUBRC <> 0.
       MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
               RAISING LIST_DOWNLOAD_ERROR.
    ENDIF.
    WHEN '2'.
      PERFORM GET_LISTLEVEL_TABLES IN PROGRAM SAPLSYSF
                                USING
                                   PAGES[]
                                   LIST[]
                                   FMBS[]
                                   FMBX[]
                                   FSEL[]
                                   FPOS[]
                                   LIST_INDEX.
      CALL FUNCTION 'LIST_CONVERT_TO_DAT'
           EXPORTING
                PAGES = PAGES[]
           TABLES
                LIST  = LIST[]
                FMBS  = FMBS[]
                FMBX  = FMBX[]
                FSEL  = FSEL[].
    WHEN '3'.
      PERFORM GET_LISTLEVEL_TABLES IN PROGRAM SAPLSYSF
                                USING
                                   PAGES[]
                                   LIST[]
                                   FMBS[]
                                   FMBX[]
                                   FSEL[]
                                   FPOS[]
                                   LIST_INDEX.
      CALL FUNCTION 'LIST_CONVERT_TO_RTF'
           TABLES
                LIST                      = LIST
                FMBS                      = FMBS
                FMBX                      = FMBX
                FSEL                      = FSEL
                FPOS                      = FPOS
           EXCEPTIONS
                DOWNLOAD_FILE_WRITE_ERROR
                DOWNLOAD_NO_BATCH
                DOWNLOAD_UNKNOWN_ERROR
                OTHERS.
    IF SY-SUBRC <> 0.
       MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
               RAISING LIST_DOWNLOAD_ERROR.
    ENDIF.
  WHEN '4'.
    CALL FUNCTION 'LIST_DOWNLOAD_HTML'
         EXPORTING
              LIST_INDEX         = LIST_INDEX
         EXCEPTIONS
              LIST_INDEX_INVALID = 1
              DOWNLOAD_ERROR     = 2
              OTHERS             = 3.
    IF SY-SUBRC NE 0.
       MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
               RAISING LIST_DOWNLOAD_ERROR.
    ENDIF.
  when '5'.
      CALL FUNCTION 'DOWNLOAD_LIST'
           EXPORTING
                LIST_INDEX = LIST_INDEX
                copy_to_clipboard = 'X'
           EXCEPTIONS
                LIST_DOWNLOAD_ERROR
                OTHERS.
    IF SY-SUBRC <> 0.
       MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
               RAISING LIST_DOWNLOAD_ERROR.
    ENDIF.

  when '6'. "// PDF format
      CALL FUNCTION 'ZDOWNLOAD_LIST_PDF'
           EXPORTING
                LIST_INDEX = LIST_INDEX
           EXCEPTIONS
                LIST_DOWNLOAD_ERROR
                OTHERS.
    IF SY-SUBRC <> 0.
       MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
               RAISING LIST_DOWNLOAD_ERROR.
    ENDIF.
ENDCASE.
ENDFUNCTION.

6 – Create a new enhancement implementation at the beginning of function module LIST_DOWNLOAD.

This enhancement implementation will detour the execution of the LIST_DOWNLOAD function module to our Z replacement with the PDF option:

FUNCTION LIST_DOWNLOAD.
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""$"$\SE:(1) Function Module LIST_DOWNLOAD, Start                                                                                                              A
*$*$-Start: (1)---------------------------------------------------------------------------------$*$*
ENHANCEMENT 1  ZNINJA_LISTS.    "active version
* Detour execution to the new list download function
  call function 'ZLIST_DOWNLOAD_NEW'
   exporting
     list_index          = list_index
     method              = method
   exceptions
     list_download_error = 1
     others              = 2.
  RETURN.
ENDENHANCEMENT.
*$*$-End:   (1)---------------------------------------------------------------------------------$*$*
...

Done!

Enjoy exporting your lists to lightweight PDFs! 🙂

This enhancement has a very small footprint, so you can transport it to productive systems (it’s specially useful in productive systems). But you might even make it less obtrusive, for example checking for a user parameter before detouring to the new function.

If you have any ideas, suggestions or ramblings, please leave a comment. 🙂

 

abapninja

A coder/dancer/actor/singer, usually in that order. Works as an SAP consultant/developer, and loves to tinker with software.