[ABAP] PDF-Dokument per ADS (Adobe Document Service) in XML konvertieren

* https://www.berater-wiki.de/XML_aus_PDF_extrahieren_mit_Testprogramm_FP_PDF_TEST_03
* http://sapfirst.blogspot.com/2014/05/inbound-abap-proxy-with-pdf-attachment.html
* Paket:     SAFP
* Programme: FP_PDF_TEST_01 (PDF erzeugen)
*            FP_PDF_TEST_03 (Datenextraktion)
* XFD (XML Form Data): https://www.berater-wiki.de/XFD-Datei_(Values_of_PDF_form_fields_in_XML)
* XFT (XML Form Template)
* XDC (XML Printer Definitions)

TRY.
    DATA: lv_rc TYPE i.
    DATA: it_files TYPE filetable.
    DATA: lv_action TYPE i.

* FileOpen-Dialog aufrufen
    cl_gui_frontend_services=>file_open_dialog( EXPORTING
                                                  file_filter    = |pdf (*.pdf)\|*.pdf\|{ cl_gui_frontend_services=>filetype_all }|
                                                CHANGING
                                                  file_table  = it_files
                                                  rc          = lv_rc
                                                  user_action = lv_action ).

    IF lv_action = cl_gui_frontend_services=>action_ok.
* wenn mind. zwei Dateien ausgewählt worden sind
      IF lines( it_files ) > 0.
        DATA(lv_filesize) = CONV w3param-cont_len( '0' ).
        DATA(it_bin_data) = VALUE w3mimetabtype( ).

* Datei auf Appl. Server hochladen (binary)
        cl_gui_frontend_services=>gui_upload( EXPORTING
                                                filename   = |{ it_files[ 1 ]-filename }|
                                                filetype   = 'BIN'
                                              IMPORTING
                                                filelength = lv_filesize
                                              CHANGING
                                                data_tab   = it_bin_data ).

* solix -> xstring
        DATA(lv_bin_data) = cl_bcs_convert=>solix_to_xstring( it_solix = it_bin_data ).

* Form Processor erzeugen
        DATA(o_form_processor) = cl_fp=>get_reference( ).
* PDF-Objekt erzeugen
        DATA(o_pdf) = o_form_processor->create_pdf_object( ).
* Binärdaten an PDF-Objekt übergeben
        o_pdf->set_document( pdfdata = lv_bin_data ).
* PDF-Daten extrahieren
        o_pdf->set_extractdata( ).
* ADS-Server rufen
        o_pdf->execute( ).
* extrahierte Daten holen (xstring)
        o_pdf->get_data( IMPORTING formdata = DATA(lv_pdf_formdata) ).

* xstring (binary) -> XML-string (UTF-8)
        DATA(lv_pdf_xml_str) = ||.
        DATA(o_conv) = cl_abap_conv_in_ce=>create( input    = lv_pdf_formdata
                                                   encoding = 'UTF-8' ).
        o_conv->read( IMPORTING data = lv_pdf_xml_str ).

* XML-Zugriff
        DATA(o_ixml) = cl_ixml=>create( ).

        DATA(o_sf) = o_ixml->create_stream_factory( ).
        DATA(o_doc) = o_ixml->create_document( ).
        DATA(o_stream) = o_sf->create_istream_string( lv_pdf_xml_str ).

* XML-Parser
        DATA(o_parser) = o_ixml->create_parser( stream_factory = o_sf
                                                istream        = o_stream
                                                document       = o_doc ).

* XML parsen
        IF o_parser->parse( ) = 0.

          DATA(o_root) = o_doc->get_root_element( ).

          ...

        ELSE.
* Fehlerauswertung
          DO o_parser->num_errors( ) TIMES.
            DATA(o_err) = o_parser->get_error( index = sy-index - 1 ).
            IF o_err IS BOUND.
              WRITE: / o_err->get_column( ), o_err->get_line( ), o_err->get_reason( ).
            ENDIF.
          ENDDO.
        ENDIF.

      ENDIF.
    ENDIF.
  CATCH cx_root INTO DATA(e_txt).
    WRITE: / e_txt->get_text( ).
ENDTRY.

[ABAP] DOM: Hilfs-Klasse zur Ausgabe von XML-Daten

Verwendung

DATA: o_xml_doc TYPE REF TO if_ixml_document.

* Erzeugt String aus DOM-XML-Document
DATA(lv_xml_string) = lcl_xml_tool=>get_xml_string_from_xml_doc( i_dom_xml_doc = o_xml_doc ).

* Gibt rekursiv alle Nodes unterhalb übergebener Node aus
lcl_xml_tool=>print_xml( o_xml_doc->get_root_element( ) ).

Klasse

CLASS lcl_xml_tool DEFINITION.
  PUBLIC SECTION.
  *--------------------------------------------------------------------*
    CLASS-METHODS: get_xml_string_from_xml_doc
      IMPORTING
                i_dom_xml_doc      TYPE REF TO if_ixml_document
                i_encoding         TYPE string DEFAULT 'UTF-8'
      RETURNING VALUE(ret_xml_str) TYPE string.
*--------------------------------------------------------------------*
    CLASS-METHODS: get_node_string
      IMPORTING
                i_dom_xml_node    TYPE REF TO if_ixml_node
                i_close           TYPE boolean
      RETURNING VALUE(ret_string) TYPE string.
*--------------------------------------------------------------------*
    CLASS-METHODS: print_xml
      IMPORTING
        i_dom_xml_root TYPE REF TO if_ixml_node.
*--------------------------------------------------------------------*
  PRIVATE SECTION.
    CONSTANTS: co_stretch_factor TYPE i VALUE 4.
ENDCLASS.
*--------------------------------------------------------------------*
CLASS lcl_xml_tool IMPLEMENTATION.
*--------------------------------------------------------------------*
* Erzeugt String aus DOM-XML-Document
*--------------------------------------------------------------------*
* -> i_dom_xml_doc - DOM-XML-Document
* -> i_encoding    - XML-Encoding
* <- ret_xml_str   - DOM-XML-Document als String
*--------------------------------------------------------------------*
  METHOD get_xml_string_from_xml_doc.

    ret_xml_str = ''.

    TRY.
        DATA(o_ixml) = cl_ixml=>create( ).
        DATA(o_sf) = o_ixml->create_stream_factory( ).
        DATA(o_encoding) = o_ixml->create_encoding( character_set = i_encoding
                                                    byte_order = if_ixml_encoding=>co_none ).

        DATA: lv_xml TYPE string.

        DATA(o_ostream) = o_sf->create_ostream_cstring( lv_xml ).
        o_ostream->set_encoding( encoding = o_encoding ).
        o_ostream->set_pretty_print( pretty_print = abap_true ).

        DATA(o_render) = o_ixml->create_renderer( ostream  = o_ostream
                                                  document = i_dom_xml_doc ).

* XML-String in lv_xml generieren
        DATA(lv_rc) = o_render->render( ).

* Dateigröße in Bytes
        DATA(lv_size) = o_ostream->get_num_written_raw( ).

* Stream schließen
        o_ostream->close( ).

        IF lv_rc = 0 AND lv_size > 0.
          ret_xml_str = lv_xml.
        ENDIF.
      CATCH cx_root.
    ENDTRY.

  ENDMETHOD.
*--------------------------------------------------------------------*
* Erzeugt String aus Node
*--------------------------------------------------------------------*
* -> i_dom_xml_node - Node
* -> i_close        - evtl. Schließen-Tag ausgeben
* <- ret_string     - Node-String
*--------------------------------------------------------------------*
  METHOD get_node_string.

    ret_string = ''.

    TRY.
        CASE i_dom_xml_node->get_type( ).
* Element
          WHEN if_ixml_node=>co_node_element.
            DATA(o_attr) = i_dom_xml_node->get_attributes( ).

            DATA: lv_attr TYPE string.

            IF o_attr IS BOUND.
              DATA(lv_attr_cnt) = o_attr->get_length( ).

* alle Attribute ausgeben
              DO lv_attr_cnt TIMES.
                DATA(o_attri_item) = o_attr->get_item( sy-index - 1 ).

* Namespace
                DATA(lv_namespace) = o_attri_item->get_namespace( ).
*                DATA(lv_prefix) = o_attri_item->get_namespace_prefix( ).

                DATA(lv_ns_attr) = lv_namespace.

* Namespace:Attribut
                IF NOT lv_ns_attr IS INITIAL.
                  lv_ns_attr = |{ lv_ns_attr }:{ o_attri_item->get_name( ) }|.
                else.
                  lv_ns_attr = |{ o_attri_item->get_name( ) }|.
                ENDIF.

                IF lv_attr IS INITIAL.
                  lv_attr = |{ lv_ns_attr }="{ o_attri_item->get_value( ) }"|.
                ELSE.
                  lv_attr = |{ lv_attr } { lv_ns_attr }="{ o_attri_item->get_value( ) }"|.
                ENDIF.
              ENDDO.
            ENDIF.

            IF i_close = abap_false.
              DATA(lv_close_tag) = |>|.
* Gibt es Unterelemente zur Node?
              IF i_dom_xml_node->get_first_child( ) IS INITIAL.
                lv_close_tag = |/>|.
              ENDIF.

              IF strlen( lv_attr ) = 0.
                ret_string = |<{ i_dom_xml_node->get_name( ) }{ lv_close_tag }|.
              ELSE.
                ret_string = |<{ i_dom_xml_node->get_name( ) } { lv_attr }{ lv_close_tag }|.
              ENDIF.
            ELSE.
              ret_string = |</{ i_dom_xml_node->get_name( ) }>|.
            ENDIF.
* Werte
          WHEN if_ixml_node=>co_node_text OR if_ixml_node=>co_node_cdata_section.
            ret_string = i_dom_xml_node->get_value( ).
        ENDCASE.
      CATCH cx_root.
    ENDTRY.

  ENDMETHOD.
*--------------------------------------------------------------------*
* Gibt rekursiv alle Nodes ab i_xml_root (Root) aus
*--------------------------------------------------------------------*
* -> i_dom_xml_root - Root-Node
*--------------------------------------------------------------------*
  METHOD print_xml.
* Einrückung
    DATA(lv_indent) = i_dom_xml_root->get_height( ) * co_stretch_factor.

    WRITE: / |{ ' ' WIDTH = lv_indent }{ get_node_string( i_dom_xml_node = i_dom_xml_root
                                                          i_close        = abap_false ) }|.

    DATA(o_node) = i_dom_xml_root->get_first_child( ).

    IF NOT o_node IS INITIAL.
* Wenn Childs vorhanden -> auflisten
      WHILE NOT o_node IS INITIAL.
        print_xml( o_node ).
        o_node = o_node->get_next( ).
      ENDWHILE.
* evtl. Schließ-Tag setzen
      WRITE: / |{ ' ' WIDTH = lv_indent }{ get_node_string( i_dom_xml_node = i_dom_xml_root
                                                            i_close        = abap_true ) }|.
    ENDIF.

  ENDMETHOD.