PDF Dokumente in ABAP zusammenführen

Dieser Artikel beschreibt, wie man mehrere PDF Dokumente in ABAP zu einem Dokument zusammenführen kann. Dabei stammt ein Dokument aus dem KPRO Dokumentenmanagement (oder GOS). Das zweite Dokument wird mittels ADS erstellt.

Ausgangssituation und Hintergrund

Auf einem PDF Formular wird auf der Rückseite eine Zeichnung im TIF Format ausgegeben. Dazu wurde der XSTRING des Bildes aus dem KPRO gelesen und an das Formular übergeben. Hier wurde mittels eines Grafikknoten das Bild ausgegeben.

Neuere Zeichnungen liegen aber nicht mehr im TIF Format vor, sondern als 2D PDF. Zwar lässt sich auch bei dem PDF Dokument der XSTRING aus dem KPRO lesen, die Daten werden von dem Grafikknoten aber nicht korrekt interpretiert. Folglich bleibt die Rückseite des Dokuments leer.

Anforderung

Das bestehende PDF Dokument (Zeichnung) soll an das neu erzeugte PDF angehangen werden.

Lösung

Um das Problem zu lösen, kann das bestehende PDF und das neu erstellte mittels der Klasse cl_rspo_pdf_merge in ein Dokument vereint werden. Um dies zu erreichen, sind folgende Schritte erforderlich:

Job Öffnen

Beim Öffnen des Druckjobs muss das Feld GETPDF der Struktur IE_OUTPUTPARAMS gesetzt werden. Dies führt dazu, dass das Dokument nicht an den Drucker übergeben wird, sondern die binären Daten des PDF an das aufrufende Programm

  METHOD job_open.

      DATA: ls_outputpar TYPE sfpoutputparams.

      ls_outputpar-copies = gv_copies.
      ls_outputpar-dataset = gv_dataset.
      ls_outputpar-suffix1 = gv_suffix1.
      ls_outputpar-suffix2 = gv_suffix2.
      ls_outputpar-title = gv_sptitle.
      ls_outputpar-covtitle = gv_sptitle.
      ls_outputpar-lifetime = gv_lifetime.
      ls_outputpar-getpdf = abap_true. " Daten des PDF zurückgeben
      ls_outputpar-reqdel = gv_del_print.   " Löschen nach Ausgabe
      ls_outputpar-reqnew = gv_sp_new.    " Neuer Spoolauftrag
      ls_outputpar-reqfinal = gv_sp_final.  " Auftrag ist abgeschlossen
      ls_outputpar-dest = gv_dest.         " Ausgabegerät
      ls_outputpar-reqimm = gv_sp_immed_print. " Sofortdruck

      IF gv_show_dialog = gc_show_dialog.
        ls_outputpar-nodialog = abap_false.
        ls_outputpar-preview = abap_true.
      ELSE.
* falls  Ausgabegerät nicht gefüllt ist -> Benutzerdialog einschalten
        IF ls_outputpar-dest IS INITIAL.
          ls_outputpar-nodialog = abap_false.  "Benutzerdialog einschalten
          ls_outputpar-preview  = abap_true.   "Vorschau
        ELSE.
          ls_outputpar-nodialog = abap_true.
          ls_outputpar-preview = abap_false.
        ENDIF.
      ENDIF.


      CALL FUNCTION 'FP_JOB_OPEN'
        CHANGING
          ie_outputparams = ls_outputpar
        EXCEPTIONS
          cancel          = 1
          usage_error     = 2
          system_error    = 3
          internal_error  = 4
          OTHERS          = 5.

      IF sy-subrc <> 0.
...
      ENDIF.
  ENDMETHOD.

Dokument erstellen lassen

Nun wird mittels Aufruf des Formularbausteins, wie gewohnt, das Dokument erstellt. Wichtig ist hier, dass der Import Parameter /1bcdwb/formoutput entgegen nimmt. In dem Feld pdf stehen nun die binären Daten des erstellten PDF.

Anschließend wird mit der Klasse CL_RSPO_PDF_MERGE das erstellte PDF mit den Binärdaten des bereits bestehenden PDF (Zeichnung) kombiniert. Dazu wird die zunächst die Methode ADD_DOCUMENT aufgerufen. Nachdem alle Dokumente eingefügt wurden, wird die Methode MERGE_DOCUMENTS genutzt. Diese liefert die Binärdaten des neuen, kombinierten Dokuments zurück.

METHOD call_function_module.

    DATA: lv_error_string TYPE string,
          ls_output       TYPE fpformoutput,
          lv_merge type xstring.

* generierten FuBa aufrufen: PDF ausgeben

    CALL FUNCTION gv_fmname
      EXPORTING
        /1bcdwb/docparams  = gs_docparams
        p_pardata_head     = gs_pardata_head
        p_pardata_target   = gs_pardata_target
        p_partxt           = gs_partxt
        p_docinfo          = gs_docinfo
      IMPORTING
        /1bcdwb/formoutput = ls_output
      EXCEPTIONS
        usage_error        = 1
        system_error       = 2
        internal_error     = 3
        OTHERS             = 4.

    IF sy-subrc NE 0.
      CALL FUNCTION 'FP_GET_LAST_ADS_ERRSTR'
        IMPORTING
          e_adserrstr = lv_error_string.

    ENDIF.

      DATA(lo_cl_merge= NEW cl_rspo_pdf_merge( ).
      lo_cl_merge->add_document( ls_output-pdf ).
      lo_cl_merge->add_document( gs_pardata_head-drawing ).
      lo_cl_merge->merge_documents( IMPORTING merged_document = lv_merge ).
      me->print_merged_pdf( lv_merge ).
    ENDIF.
  ENDMETHOD.

Drucken des kombinierten Dokuments

Das kombinierte Dokument muss nun an den Drucker gesendet werden. Da wir im ersten Schritt die Ausgabe an den Drucker verhindert haben, geschieht dies nicht mehr automatisch. Dazu habe ich folgende Methode erstellt. Zunächst wird ein neuer, leerer Spoolauftrag erzeugt. Anschließend wird der Dateipfad zu dem Spoolauftrag ermittelt, die Datei geöffnet und mit den Binärdaten des kombinierten PDF befüllt.

Mit Abschließen des Spoolauftrags mittels ADS_SR_CLOSE wird die Ausgabe an den Drucker gesendet.

METHOD print_merged_pdf.

    DATA: lv_handle    TYPE sy-tabix,
          lv_spoolid   TYPE rspoid,
          lv_partname  TYPE adspart,
          lv_globaldir TYPE text1024,
          lv_dstfile   TYPE text1024,
          lv_filesize  TYPE i,
          lv_pages     TYPE i.

    " Nur wenn Dateiinhalt vorliegt Dokument ausgeben
    IF iv_content IS NOT INITIAL.

      " ADS Spoolauftrag öffnen
      CALL FUNCTION 'ADS_SR_OPEN'
        EXPORTING
          dest            = me->gv_dest
          suffix1         = me->gv_suffix1
          suffix2         = me->gv_suffix2
          titleline       = conv RSPOTITLE( gv_sptitle )
          doctype         = 'ADSP'
          copies          = conv RSPOCOPIES( me->gv_copies )
          immediate_print = me->gv_sp_immed_print
          auto_delete     = me->gv_del_print
        IMPORTING
          handle          = lv_handle
          spoolid         = lv_spoolid
          partname        = lv_partname
        EXCEPTIONS
          OTHERS          = 1.

      IF sy-subrc NE 0.
        RAISE EXCEPTION TYPE zcx_ads_error
          EXPORTING
            textid = zcx_ads_error=>zcx_ads_error_form_open.
      ENDIF.

      " ADS Spool Pfad ermitteln
      CALL FUNCTION 'ADS_GET_PATH'
        IMPORTING
          ads_path = lv_globaldir.
      CONCATENATE lv_globaldir '/' lv_partname '.pdf' INTO lv_dstfile.
      
      " Übertragen der Daten in die Spooldatei
      OPEN DATASET lv_dstfile FOR OUTPUT IN BINARY MODE.
      IF sy-subrc NE 0.
        RAISE EXCEPTION TYPE zcx_ads_error
          EXPORTING
            textid = zcx_ads_error=>zcx_ads_error.
      ENDIF.

      TRANSFER iv_content TO lv_dstfile.

      IF sy-subrc NE 0.
        RAISE EXCEPTION TYPE zcx_ads_error
          EXPORTING
            textid = zcx_ads_error=>zcx_ads_error.
      ENDIF.

      CLOSE DATASET lv_dstfile.

      IF sy-subrc NE 0.
        RAISE EXCEPTION TYPE zcx_ads_error
          EXPORTING
            textid = zcx_ads_error=>zcx_ads_error.
      ENDIF.

      lv_pages = 2.
      lv_filesize = xstrlen( iv_content ).
      CALL FUNCTION 'ADS_SR_CONFIRM'
        EXPORTING
          handle   = lv_handle
          partname = lv_partname
          size     = lv_filesize
          pages    = lv_pages
          no_pdf   = ' '
        EXCEPTIONS
          OTHERS   = 1.

      IF sy-subrc NE 0.
      ...
      ENDIF.

      " Spoolauftrag abschließen

      CALL FUNCTION 'ADS_SR_CLOSE'
        EXPORTING
          handle = lv_handle
        EXCEPTIONS
          OTHERS = 1.
      IF sy-subrc NE 0.
        RAISE EXCEPTION TYPE zcx_ads_error
          EXPORTING
            textid = zcx_ads_error=>zcx_ads_error_form_close.
      ENDIF.
    ENDIF.
  ENDMETHOD.

Verweise

https://blogs.sap.com/2019/06/01/convert-images-into-pdf-and-merge-with-an-existing-adobe-forms/