Application Development Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
paly_o
Explorer
In my first blog I wrote short introduction about OpenXML in word processing and how we can work with word documents in ABAP. In this blog I will describe custom xml part of word document and give some examples how we can use it in ABAP. For example how custom data can be used in on demand printing requests to provide customer data in document with content control.

Custom XML parts were introduced in Microsoft word 2007 along with Open XML formats. Custom part of microsoft word document is used to store custom data and it is not suprising that data format is XML. In connection to Custom XML we will use also Content Control functionality of Microsoft Word. Custom Control is set of individual objects to control and customize content of the document. One of its purpose is to map data from different sources (in our example from custom XML) to exact place in word document. So let's go with example.

In first part I will show you how to manually create and modify custom xml part. In second part I will provide simple ABAP code which does same.

  1. Maintaining custom XML manually


Purpose of this step is to understand custom xml and content control in word. So open our Test.docx document (or create a new one) in Microsoft Word 2007 or above.  As prerequisite it is necessary to install add-in called "Custom XML Part Editor". Go to Developer Tab-->Add-in and search it and add. After successfull installation you should see new button XML Mapping Pane.



As second step prepare simple custom xml file with some dummy data in XML format. I use following example.
<?xml version="1.0" encoding="utf-8"?>
<data xmlns="custom">
<NAME>%Name%</NAME>
<LAST_NAME>%Last Name%</LAST_NAME>
<DATE>%Date%</DATE>
</data>

Press button XML Mapping Pane to add new custom part. On right side select Add new part... and navigate to our custom.xml file. Aftre adding it now select "custom" part and you should see something like this.



Now place cursor on exact spot in document where you want to map data and by right click on attributes (Name, Last Name, Date) select Insert Content Control-->Plain text. Rewrite values of content controls attributes and save document. I added Name and Last name into our Test.docx document.



Let's now check Main document xml part and Custom xml part which was added into document and now will be visible in document structure. For this I use OOXML tool in chrome.



Our custom XML part has path customXml-->item1.xml and looks like this.



Main part has path word-->document.xml. As it got quick big I will provide only short sample of this xml. please note tag <w:dataBinding> which binds data from custom XML to document.



     2. Maintainong custom XML in ABAP

In second part I will provide ABAP code to do same as in first step. Aftre 1st run of program we read word document, create new custom xml part, attach this part to document and save it into same file. Now it is time to manually map data to places where we want them to appear - in same way how I showed in 1st part. Now with second run of report we change custom xml with data we want.
*&---------------------------------------------------------------------*
*& Report ZCUSTOMXML
*&
*&---------------------------------------------------------------------*
*& Report demonstrates reading word document, creating custom xml part
*& to attach it to document.
*& Pavol Olejar 23.4.2017
*&---------------------------------------------------------------------*
REPORT zcustomxml.

DATA: lv_length TYPE i,
lt_data_tab TYPE STANDARD TABLE OF x255,
lv_docx TYPE xstring,
lv_string TYPE string,
lr_docx TYPE REF TO cl_docx_document,
lr_main TYPE REF TO cl_docx_maindocumentpart,
lr_custom_col TYPE REF TO cl_openxml_partcollection,
lr_custom TYPE REF TO cl_oxml_customxmlpart,
lv_it TYPE i,
lv_custom_xml TYPE xstring,
lv_date TYPE string.

* 1ST STEP - READ FILE AND FIND CUSTOM XML
*--------------------
CALL METHOD cl_gui_frontend_services=>gui_upload
EXPORTING
filename = 'C:\Test.docx'
filetype = 'BIN'
IMPORTING
filelength = lv_length
CHANGING
data_tab = lt_data_tab.

CALL FUNCTION 'SCMS_BINARY_TO_XSTRING'
EXPORTING
input_length = lv_length
IMPORTING
buffer = lv_docx
TABLES
binary_tab = lt_data_tab.

CALL METHOD cl_docx_document=>load_document
EXPORTING
iv_data = lv_docx
RECEIVING
rr_doc = lr_docx.

CHECK lr_docx IS BOUND.
lr_main = lr_docx->get_maindocumentpart( ).
lr_custom_col = lr_main->get_customxmlparts( ).
* We can have more than 1 custom xml files
* so let's find correct one and if exists
DATA(lv_size) = lr_custom_col->get_count( ).
DATA: lv_flag TYPE flag VALUE abap_false.
DO lv_size TIMES.
lv_it = sy-index - 1.
lr_custom ?= lr_custom_col->get_part( iv_index = lv_it ).
DATA(lv_xml) = lr_custom->get_data( ).
CALL FUNCTION 'CRM_IC_XML_XSTRING2STRING'
EXPORTING
inxstring = lv_xml
IMPORTING
outstring = lv_string.

FIND FIRST OCCURRENCE OF 'xmlns="custom"' IN lv_string.
IF sy-subrc EQ 0.
* our custom xml was found
lv_flag = abap_true.
EXIT.
ENDIF.
ENDDO.

* 2ND STEP - CREATE OR CHANGE CUSTOM XML
*---------------------------------------
CASE lv_flag.
WHEN abap_false. "our custom xml is not there yet so we create it
lr_custom = lr_main->add_customxmlpart( ).

CONCATENATE '<?xml version="1.0" encoding="utf-8"?>'
'<data xmlns="custom">'
'<NAME>%Name%</NAME>'
'<LAST_NAME>%Last Name%</LAST_NAME>'
'<DATE>%Date%</DATE>'
'</data>'
INTO DATA(lv_custom).

CALL FUNCTION 'SCMS_STRING_TO_XSTRING'
EXPORTING
text = lv_custom
IMPORTING
buffer = lv_custom_xml.
WHEN abap_true. "we provide data for document
* lr_custom and lv_xml are correctly filled from loop
* Change data in custom XML
CALL FUNCTION 'CRM_IC_XML_XSTRING2STRING'
EXPORTING
inxstring = lv_xml
IMPORTING
outstring = lv_string.
REPLACE FIRST OCCURRENCE OF '%Name%' IN lv_string
WITH 'James'.
REPLACE FIRST OCCURRENCE OF '%Last Name%' IN lv_string
WITH 'Bond'.
CONCATENATE sy-datum+6(2) '.' sy-datum+4(2) '.' sy-datum+0(4)
INTO lv_date.
REPLACE FIRST OCCURRENCE OF '%Date%' IN lv_string
WITH lv_date.

CALL FUNCTION 'SCMS_STRING_TO_XSTRING'
EXPORTING
text = lv_string
IMPORTING
buffer = lv_custom_xml.

ENDCASE.
* 3RD STEP - SAVE IT
*---------------------------------------
lr_custom->feed_data( iv_data = lv_custom_xml ).
lv_docx = lr_docx->get_package_data( ).
lv_length = xstrlen( lv_docx ).

CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
EXPORTING
buffer = lv_docx
TABLES
binary_tab = lt_data_tab.

CALL METHOD cl_gui_frontend_services=>gui_download
EXPORTING
bin_filesize = lv_length
filename = 'C:\Test.docx'
filetype = 'BIN'
confirm_overwrite = 'X'
CHANGING
data_tab = lt_data_tab.

In my next blog I will describe a way how we can use custom XML to fill tables in word document with own data dynamically.
8 Comments