I discussed custom XML part and content control and how to work with it to achieve data binding in word document. Custom xml file was structured in very simple way. However there might be requirement to map and create some parts of document dynamically. For example we do not know how many records our table can contain after processing.
In this blog I will show a way how we can fill table dynamically with data.
To get this done I’ve debugged standard demo program ROOXML_DOCX_FORM_MIX_FT_TEST01 in which class CL_DOCX_FORM with method MERGE_DATA does the job of dynamic creation of records in table. It is using in runtime generated XSLT transformation to match binding tags in docx document with given tags and data from custom xml file. As this standard functionality is provided only as demo we will copy it into z class with some small changes.
Copy class CL_DOCX_FORM into ZCL_DOCX_FORM class. Then you have to correct 3 errors stopping you from activation of newly created Z-class. You find them quickly and you just have to rewrite “cl_docx_form” to “zcl_docx_form”. Then go to method MERGE_DATA where you have to do 2 small changes:
1. Find and rewrite following code where transformation name is created:
CONCATENATE 'XSLT' ls_xsltattr-xsltdesc INTO ls_xsltattr-xsltdesc.
to for example this:
CONCATENATE 'Z' ls_xsltattr-xsltdesc INTO ls_xsltattr-xsltdesc.
Reason for this change is that you are allowed to create transformation only under Z or customer namespace and not XSLT namespace.
2. Find FM: XSLT_MAINTENANCE for deleting generated transformation and insert input parameter i_gen_flag with value abap_true
* Delete the transformation since it is not needed any more.
CALL FUNCTION 'XSLT_MAINTENANCE'
EXPORTING
i_operation = 'DELETE' "#EC NOTEXT
i_xslt_attributes = ls_xsltattr
i_gen_flag = abap_true
i_suppress_corr_insert = abap_true
i_suppress_tree_placement = abap_true
EXCEPTIONS
OTHERS = 1.
Reason for this change is to avoid pop-up window in runtime (in case it is used in webUI for example).
Z class ZCL_DOCX_FORM is now ready for use.
Let’s now prepare custom xml file and map it in word document. Here is an example of my custom xml file.
<?xml version="1.0" encoding="utf-8"?>
<data xmlns="http://www.sap.com/SAPForm/0.5">
<TABLE>
<DATA>
<NAME>%NAME%</NAME>
<LAST_NAME>%LAST_NAME%</LAST_NAME>
<DATE>%DATE%</DATE>
</DATA>
</TABLE>
</data>
Make sure that namespace of data tag is exactly “”http://www.sap.com/SAPForm/0.5″”. This validation is used in mentioned method merge_data. Our table is represented by node TABLE and every line in that table is represented by tag <DATA>…</DATA>. Tags inside <DATA> section are simply columns in tables.
Let’s create new docx file called Table.docx. Insert into document simple table with header line Name, Last Name and Birth date and one empty line. Now mark that empty line and press button Repeating Section Content Control. Your word document should looks like this:
In this blog I will show a way how we can fill table dynamically with data.
To get this done I’ve debugged standard demo program ROOXML_DOCX_FORM_MIX_FT_TEST01 in which class CL_DOCX_FORM with method MERGE_DATA does the job of dynamic creation of records in table. It is using in runtime generated XSLT transformation to match binding tags in docx document with given tags and data from custom xml file. As this standard functionality is provided only as demo we will copy it into z class with some small changes.
Copy class CL_DOCX_FORM into ZCL_DOCX_FORM class. Then you have to correct 3 errors stopping you from activation of newly created Z-class. You find them quickly and you just have to rewrite “cl_docx_form” to “zcl_docx_form”. Then go to method MERGE_DATA where you have to do 2 small changes:
1. Find and rewrite following code where transformation name is created:
CONCATENATE 'XSLT' ls_xsltattr-xsltdesc INTO ls_xsltattr-xsltdesc.
to for example this:
CONCATENATE 'Z' ls_xsltattr-xsltdesc INTO ls_xsltattr-xsltdesc.
Reason for this change is that you are allowed to create transformation only under Z or customer namespace and not XSLT namespace.
2. Find FM: XSLT_MAINTENANCE for deleting generated transformation and insert input parameter i_gen_flag with value abap_true
* Delete the transformation since it is not needed any more.
CALL FUNCTION 'XSLT_MAINTENANCE'
EXPORTING
i_operation = 'DELETE' "#EC NOTEXT
i_xslt_attributes = ls_xsltattr
i_gen_flag = abap_true
i_suppress_corr_insert = abap_true
i_suppress_tree_placement = abap_true
EXCEPTIONS
OTHERS = 1.
Reason for this change is to avoid pop-up window in runtime (in case it is used in webUI for example).
Z class ZCL_DOCX_FORM is now ready for use.
Let’s now prepare custom xml file and map it in word document. Here is an example of my custom xml file.
<?xml version="1.0" encoding="utf-8"?>
<data xmlns="http://www.sap.com/SAPForm/0.5">
<TABLE>
<DATA>
<NAME>%NAME%</NAME>
<LAST_NAME>%LAST_NAME%</LAST_NAME>
<DATE>%DATE%</DATE>
</DATA>
</TABLE>
</data>
Make sure that namespace of data tag is exactly “”http://www.sap.com/SAPForm/0.5″”. This validation is used in mentioned method merge_data. Our table is represented by node TABLE and every line in that table is represented by tag <DATA>…</DATA>. Tags inside <DATA> section are simply columns in tables.
Let’s create new docx file called Table.docx. Insert into document simple table with header line Name, Last Name and Birth date and one empty line. Now mark that empty line and press button Repeating Section Content Control. Your word document should looks like this:
Now attach custom xml file created in previous step and map its attributes to columns. Result should looks like this:
Now save word document as Table.docx.
We have now prepared everyhting to run following report to demonstrate dynamic table creation.
*&---------------------------------------------------------------------*
*& Report ZCUSTOMXML_SAP
*&
*&---------------------------------------------------------------------*
*& Report demonstrates creation of custom xml file, then reading
*& document and using generated XSLT tramsformation to merge these files
*& Pavol Olejar 23.4.2017
*&---------------------------------------------------------------------*
REPORT zcustomxml_sap.
TYPES: BEGIN OF t_table,
name TYPE string,
last_name TYPE string,
birth_date TYPE string,
END OF t_table.
DATA: lt_table TYPE TABLE OF t_table,
ls_table TYPE t_table,
lv_length TYPE i,
lt_data_tab TYPE STANDARD TABLE OF x255,
lv_docx TYPE xstring,
lr_docx TYPE REF TO cl_docx_document,
lr_main TYPE REF TO cl_docx_maindocumentpart,
lr_custom TYPE REF TO cl_oxml_customxmlpart,
lv_table_string TYPE string,
lv_custom_xml TYPE xstring.
* 1ST PART - PREPARE DATA FOR MAPPING
ls_table-name = 'James'.
ls_table-last_name = 'Bond'.
ls_table-birth_date = '13.04.1968'.
APPEND ls_table TO lt_table.
ls_table-name = 'Pavol'.
ls_table-last_name = 'Olejar'.
ls_table-birth_date = '27.01.1985'.
APPEND ls_table TO lt_table.
LOOP AT lt_table INTO ls_table.
CONCATENATE lv_table_string
'<DATA>'
'<NAME>' ls_table-name '</NAME>'
'<LAST_NAME>' ls_table-last_name '</LAST_NAME>'
'<DATE>' ls_table-birth_date '</DATE>'
'</DATA>'
INTO lv_table_string.
ENDLOOP.
CONCATENATE '<?xml version="1.0" encoding="utf-8"?>'
'<data xmlns="http://www.sap.com/SAPForm/0.5">'
'<TABLE>'
lv_table_string
'</TABLE>'
'</data>'
INTO DATA(lv_custom).
* 2ND STEP - CREATE CUSTOM XML
CALL FUNCTION 'SCMS_STRING_TO_XSTRING'
EXPORTING
text = lv_custom
IMPORTING
buffer = lv_custom_xml.
* 3RD STEP - READ WORD DOCUMENT
CALL METHOD cl_gui_frontend_services=>gui_upload
EXPORTING
filename = 'C:\Table.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.
lv_docx = lr_docx->get_package_data( ).
* 4TH STEP - MERGE CUSTOM XML FILE AND DOC
TRY.
CALL METHOD zcl_docx_form=>merge_data
EXPORTING
im_formtemplate_data = lv_docx
im_customxml_data = lv_custom_xml
im_delete_sdt_tags = 'Y'
RECEIVING
re_merged_data = lv_docx.
CATCH cx_root.
ENDTRY.
* 5TH STEp - SAVE RESULT
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:\Table.docx'
filetype = 'BIN'
confirm_overwrite = 'X'
CHANGING
data_tab = lt_data_tab.
Result word document looks like this:
I reccomend to use MERGE_DATA method only if you need to generate tables dynamically. When you have word documents with tens of pages then process for generation of transformation followed by usage of that transformation can be time consumig.
Another tip is to check input parameters of this method to influence content control tags behavior – for example to keep them or not.
Note: When using pictures in documents I have experienced problems when extrenal list tag with xpath reference in curly brackets was present. Here is an example.
<a:extLst>
<a:ext uri="{28A0092B-C50C-407E-A947-70E740481C1C}">
<a14:useLocalDpi
xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main" val="0"/>
</a:ext>
</a:extLst>
Method MERGE_DATA cannot parse this successfully. As solution I suggest to replace one curly brackets with two curly brackets and then run MERGE_DATA method. After that you can change it back. Other option is manually remove this tag from document.
FROM
<a:ext uri="{28A0092B-C50C-407E-A947-70E740481C1C}">
TO
<a:ext uri="{{28A0092B-C50C-407E-A947-70E740481C1C}}">
No comments:
Post a Comment