Here is an example of creating word documents from templates stored in web repository (SMW0) using wordprocessingml.
A very simple form can be done in around an hour of time.
USE CASE
Prepare the template that needs to be filled with data.
Write the program that will fill the document.
data: LO_FORM typerefto Z_MWL_FORM.
data: L_TEMPLATE type STRING value'block'.
data: L_INDEX typeIvalue1.
data: L_NUM typeI.
create object LO_FORM
exporting
I_TEMPLATE = 'Z_TEST'.
LO_FORM->REPLICATE(
I_TEMPLATE_ID = L_TEMPLATE
I_COPY_NUM = 2
).
LO_FORM->PREP_SEQ_ACCESS( ).
if LO_FORM->FIND_VARIABLE( 'name' ) eq ABAP_TRUE.
LO_FORM->SET_VALUE( 'Zhambyl').
endif.
if LO_FORM->FIND_VARIABLE( 'age' ) eq ABAP_TRUE.
LO_FORM->SET_VALUE( '25' ).
endif.
if LO_FORM->FIND_VARIABLE( 'gender' ) eq ABAP_TRUE.
LO_FORM->SET_VALUE( 'male' ).
endif.
do2times.
if LO_FORM->FIND_BLOCK( L_INDEX ) eq ABAP_TRUE.
if LO_FORM->FIND_VARIABLE( 'var1' ) eq ABAP_TRUE.
LO_FORM->SET_VALUE( '1' ).
endif.
if LO_FORM->FIND_VARIABLE( 'var2' ) eq ABAP_TRUE.
LO_FORM->SET_VALUE( '2' ).
endif.
if LO_FORM->FIND_VARIABLE( 'var3' ) eq ABAP_TRUE.
LO_FORM->SET_VALUE( '3' ).
endif.
endif.
add1to L_INDEX.
enddo.
LO_FORM->FINISH_SEQ_ACCESS( ).
LO_FORM->CLEAN( ).
LO_FORM->DISPLAY( ).
Open the filled document on client machine.
Implemantaion details
First we need to create a helper class that is responsible for operations related to downloading and uploading file data.
class Z_MWL_FILE definition
public
createpublic .
publicsection.
data EXTENSION type STRING .
data TEMPDIR type STRING .
data BSTRING type XSTRING.
methods: DOWNLOAD " download file from web repository
importing
VALUE(I_TEMPLATE) type STRING
returning
VALUE(R_SUBRC) like SY-SUBRC.
methods GET_BSTRING " returns xstring representation of file
returning
VALUE(R_STRING) type XSTRING.
methods GET_TEMP_DIR " chose file storage location
returning
VALUE(R_PATH) type string.
methods SAVE_ON_FRONTEND " upload file to client
importing
VALUE(I_STRING) type XSTRING
returning
VALUE(R_SUBRC) like SY-SUBRC.
protectedsection.
privatesection.
ENDCLASS.
CLASS Z_MWL_FILE IMPLEMENTATION.
method DOWNLOAD.
data: LS_KEY type WWWDATATAB.
data: LS_MIME type W3MIME.
data: LT_MIME typestandardtableof W3MIME.
field-symbols <LFS_DATA> typeANY.
LS_KEY-RELID = 'MI'.
LS_KEY-OBJID = I_TEMPLATE.
callfunction'WWWDATA_IMPORT'
exporting
KEY = LS_KEY
tables
MIME = LT_MIME
exceptions
WRONG_OBJECT_TYPE = 1
IMPORT_ERROR = 2
others = 3.
if SY-SUBRC eq0.
loopat LT_MIME into LS_MIME.
assign LS_MIME to<LFS_DATA> casting type ('X').
if<LFS_DATA> isassigned.
concatenate BSTRING <LFS_DATA> into BSTRING in byte mode.
unassign <LFS_DATA>.
endif.
endloop.
else.
R_SUBRC = SY-SUBRC.
endif.
endmethod. "DOWNLOAD
method GET_BSTRING.
R_STRING = BSTRING.
endmethod. "GET_BSTRING
method GET_TEMP_DIR.
data: L_WTITLE type STRING.
data: L_NAME type STRING.
data: L_FPATH type STRING.
L_WTITLE = 'CHOSE FILE STORAGE LOCATION'.
CL_GUI_FRONTEND_SERVICES=>FILE_SAVE_DIALOG(
exporting
WINDOW_TITLE = L_WTITLE
DEFAULT_EXTENSION = 'docx'
FILE_FILTER = 'docx'
changing
FILENAME = L_NAME
PATH = TEMPDIR
FULLPATH = L_FPATH ).
CL_GUI_CFW=>FLUSH( ).
R_PATH = L_FPATH.
endmethod. "GET_TEMP_DIR
method SAVE_ON_FRONTEND.
data: LV_FILE_TAB typestandardtableof SOLISTI1,
LV_BYTECOUNT typeI.
data: L_FPATH type STRING.
callfunction'SCMS_XSTRING_TO_BINARY'
exporting
BUFFER = I_STRING
importing
OUTPUT_LENGTH = LV_BYTECOUNT
tables
BINARY_TAB = LV_FILE_TAB.
"Save the file
L_FPATH = GET_TEMP_DIR( ).
if L_FPATH isnotinitial.
CL_GUI_FRONTEND_SERVICES=>GUI_DOWNLOAD(
exporting
BIN_FILESIZE = LV_BYTECOUNT
FILENAME = L_FPATH
FILETYPE = 'BIN'
changing
DATA_TAB = LV_FILE_TAB
).
if SY-SUBRC ne0.
R_SUBRC = SY-SUBRC.
endif.
else.
R_SUBRC = 2.
endif.
endmethod. "SAVE_ON_FRONTEND
ENDCLASS.
Secondly we create main class that is used to manipulate the word document.
This class relies on classes found in packages: S_OOXML_CORE, SXML and XSLT transformations.
class Z_MWL_FORM definition
public
createpublic .
publicsection.
data: MAIN_PART type XSTRING .
data: DOCUMENT type XSTRING .
data: INTRM_PART type XSTRING .
data: FINAL_DOC type XSTRING.
methods CONSTRUCTOR importing I_TEMPLATE type STRING. " Finds the main part of word document from zip pakcage container and
" stores it. I_TEMPLATE is the logical name of the file in smw0
methods DISPLAY. " This method packages updated main part and uploads it to front-end. Dont mind the name.
methods REPLICATE " Replicates marked block of text using transformations and substitues standard markups for custom ones
importing I_TEMPLATE_ID type STRING
I_COPY_NUM typeI.
methods: FIND_VARIABLE " Finds tag named variable using sxml. I_var is a value for name attribute of this tag.
importing I_VAR type STRING
returning VALUE(RV_FOUND) type ABAP_BOOL.
methods: FIND_BLOCK importing I_BLOCK typeI " Finds tag named block using sxml. I_block is a value for number attribute of this tag.
returning VALUE(RV_FOUND) type ABAP_BOOL. " Block contains several variables that can be copyed with different block numbers
methods: SET_VALUE importing I_VAL type STRING. " Replaces value of placeholder variable
methods: PREP_SEQ_ACCESS. " Converts xstring to Xml objects and prepares them for sequencial access
methods: FINISH_SEQ_ACCESS. " Converts from sXML back to xstring representation
methods CLEAN. " Clear's all the custom mark up from main part of word document
protectedsection.
data: O_FILE typerefto ZCL_ZK_MWL_FILE.
data: O_DOC typerefto CL_DOCX_DOCUMENT.
data: O_DOCUMENTPART typerefto CL_DOCX_MAINDOCUMENTPART.
data: O_SREADER typerefto IF_SXML_READER.
data: O_SWRITER typerefto IF_SXML_WRITER.
data: O_SNODE typerefto IF_SXML_NODE.
data: O_SVALUE_NODE typerefto IF_SXML_VALUE_NODE.
privatesection.
ENDCLASS.
CLASS Z_MWL_FORM IMPLEMENTATION.
method CLEAN.
if INTRM_PART isnotinitial.
call transformation Z_CLEAN
source xml INTRM_PART
result xml FINAL_DOC.
endif.
endmethod. "CLEAN
method CONSTRUCTOR.
create object O_FILE.
O_FILE->DOWNLOAD( I_TEMPLATE ).
DOCUMENT = O_FILE->GET_BSTRING( ).
try.
O_DOC = CL_DOCX_DOCUMENT=>LOAD_DOCUMENT( IV_DATA = DOCUMENT ).
* get the maindocument part
O_DOCUMENTPART = O_DOC->GET_MAINDOCUMENTPART( ).
MAIN_PART = O_DOCUMENTPART->GET_DATA( ).
catch CX_OPENXML_FORMAT.
catch CX_OPENXML_NOT_ALLOWED.
catch CX_OPENXML_NOT_FOUND.
catch CX_TRANSFORMATION_ERROR.
endtry.
endmethod. "constructor
method DISPLAY.
if FINAL_DOC isnotinitial.
O_DOCUMENTPART->FEED_DATA( FINAL_DOC ).
elseif MAIN_PART isnotinitial.
O_DOCUMENTPART->FEED_DATA( MAIN_PART ).
endif.
FINAL_DOC = O_DOC->GET_PACKAGE_DATA( ).
if O_FILE->SAVE_ON_FRONTEND( FINAL_DOC ) ne0.
message'Выгрузка отменена'type'S'.
endif.
endmethod. "Display
method FIND_BLOCK.
data: LX_ROOT typerefto CX_SXML_ERROR.
data: LO_OPELEM typerefto IF_SXML_OPEN_ELEMENT.
data: L_AT_VAL typerefto IF_SXML_VALUE.
data: L_VAL type STRING.
if O_SREADER isboundand O_SWRITER isbound.
while RV_FOUND ne ABAP_TRUE.
try.
O_SNODE = O_SREADER->READ_NEXT_NODE( ).
if O_SNODE isinitial.
exit.
endif.
if O_SNODE->TYPE eq IF_SXML_NODE=>CO_NT_ELEMENT_OPEN.
LO_OPELEM ?= O_SNODE.
if LO_OPELEM->IF_SXML_NAMED~QNAME-NAME eq'block'.
L_AT_VAL = LO_OPELEM->GET_ATTRIBUTE_VALUE( 'num' ).
L_VAL = L_AT_VAL->GET_VALUE( ).
if L_VAL eq I_BLOCK.
RV_FOUND = ABAP_TRUE.
endif.
endif.
endif.
O_SWRITER->WRITE_NODE( O_SNODE ).
catch CX_SXML_ERROR into LX_ROOT.
exit.
endtry.
endwhile.
endif.
endmethod. "FIND_BLOCK
method FIND_VARIABLE.
data: LX_ROOT typerefto CX_SXML_ERROR.
data: LO_OPELEM typerefto IF_SXML_OPEN_ELEMENT.
data: L_AT_VAL typerefto IF_SXML_VALUE.
data: L_VAL type STRING.
if O_SREADER isboundand O_SWRITER isbound.
while RV_FOUND ne ABAP_TRUE.
try.
O_SNODE = O_SREADER->READ_NEXT_NODE( ).
if O_SNODE isinitial.
exit.
endif.
if O_SNODE->TYPE eq IF_SXML_NODE=>CO_NT_ELEMENT_OPEN.
LO_OPELEM ?= O_SNODE.
if LO_OPELEM->IF_SXML_NAMED~QNAME-NAME eq'variable'.
L_AT_VAL = LO_OPELEM->GET_ATTRIBUTE_VALUE( 'mark' ).
L_VAL = L_AT_VAL->GET_VALUE( ).
if L_VAL eq I_VAR.
RV_FOUND = ABAP_TRUE.
endif.
endif.
endif.
O_SWRITER->WRITE_NODE( O_SNODE ).
catch CX_SXML_ERROR into LX_ROOT.
exit.
endtry.
endwhile.
clear RV_FOUND.
while RV_FOUND ne ABAP_TRUE.
try.
O_SNODE = O_SREADER->READ_NEXT_NODE( ).
if O_SNODE isinitial.
exit.
endif.
if O_SNODE->TYPE eq IF_SXML_NODE=>CO_NT_ELEMENT_OPEN.
LO_OPELEM ?= O_SNODE.
if LO_OPELEM->IF_SXML_NAMED~QNAME-NAME eq't'.
RV_FOUND = ABAP_TRUE.
endif.
endif.
O_SWRITER->WRITE_NODE( O_SNODE ).
catch CX_SXML_ERROR into LX_ROOT.
exit.
endtry.
endwhile.
clear RV_FOUND.
while RV_FOUND ne ABAP_TRUE.
try.
O_SNODE = O_SREADER->READ_NEXT_NODE( ).
if O_SNODE isinitial.
exit.
endif.
if O_SNODE->TYPE eq IF_SXML_NODE=>CO_NT_VALUE.
O_SVALUE_NODE ?= O_SNODE.
RV_FOUND = ABAP_TRUE.
exit.
endif.
O_SWRITER->WRITE_NODE( O_SNODE ).
catch CX_SXML_ERROR into LX_ROOT.
exit.
endtry.
endwhile.
endif.
endmethod. "FIND_VARIABLE
method FINISH_SEQ_ACCESS.
data: LX_ROOT typerefto CX_SXML_ERROR.
data: LO_WRITER typerefto CL_SXML_STRING_WRITER.
if O_SREADER isnotinitialand O_SWRITER isbound.
do.
try.
O_SNODE = O_SREADER->READ_NEXT_NODE( ).
if O_SNODE isinitial.
exit.
endif.
O_SWRITER->WRITE_NODE( O_SNODE ).
catch CX_SXML_ERROR into LX_ROOT.
exit.
endtry.
enddo.
try.
LO_WRITER ?= O_SWRITER.
INTRM_PART = LO_WRITER->GET_OUTPUT( ).
catch CX_SXML_ERROR into LX_ROOT.
exit.
endtry.
endif.
endmethod. "FINISH_SEQ_ACCESS
method PREP_SEQ_ACCESS.
if INTRM_PART isnotinitial.
O_SREADER ?= CL_SXML_STRING_READER=>CREATE( INTRM_PART ).
O_SWRITER ?= CL_SXML_STRING_WRITER=>CREATE( ).
endif.
endmethod. "prep_seq_access
method REPLICATE.
if INTRM_PART isinitial.
call transformation Z_REPLICATE
source xml MAIN_PART
result xml INTRM_PART
parameters TEMPLATE_ID = I_TEMPLATE_ID
COPY_NUM = I_COPY_NUM.
else.
call transformation Z_REPLICATE
source xml INTRM_PART
result xml INTRM_PART
parameters TEMPLATE_ID = I_TEMPLATE_ID
COPY_NUM = I_COPY_NUM.
endif.
endmethod. "replicate
method SET_VALUE.
data: LX_ROOT typerefto CX_SXML_ERROR.
data: L_XSTRING type XSTRING.
* L_XSTRING = CL_ABAP_CODEPAGE=>CONVERT_TO( I_VAL ).
if O_SVALUE_NODE isboundand O_SWRITER isbound.
try.
O_SVALUE_NODE->IF_SXML_VALUE~SET_VALUE( I_VAL ).
O_SWRITER->WRITE_NODE( O_SNODE ).
catch CX_SXML_ERROR into LX_ROOT.
exit.
endtry.
endif.
endmethod. "set_value
ENDCLASS.
Following are the transformations used in the class described above.
Transformation z_replicate
<xsl:transformxmlns:xsl="http://www.w3.org/1999/XSL/Transform"xmlns:sap="http://www.sap.com/sapxsl"xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" version="1.0"> <xsl:output encoding="UTF-8" indent="yes" method="xml" omit-xml-declaration="no" standalone="yes"/> <xsl:param name="TEMPLATE_ID"/> <xsl:param name="COPY_NUM" sap:type="number"/> <xsl:template match="node()|@*"> <xsl:copy> <xsl:apply-templates select="node()|@*"/> </xsl:copy> </xsl:template> <xsl:template match="w:sdt"> <xsl:choose> <xsl:when test="descendant::w:tag[@w:val=$TEMPLATE_ID]"> <xsl:call-template name="multiply"> <xsl:with-param name="maxCount" select="$COPY_NUM"/> <xsl:with-param name="nodeToCopy" select="."/> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:element name="variable"> <xsl:attribute name="mark"> <xsl:value-of select="descendant::w:tag/@w:val"/> </xsl:attribute> <xsl:apply-templates select="w:sdtContent/node()|@*" mode="variable"/> </xsl:element> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template name="multiply"> <xsl:param name="maxCount"/> <xsl:param name="i" select="1"/> <xsl:param name="nodeToCopy"/> <xsl:choose> <xsl:when test="$i <= $maxCount"> <xsl:element name="block"> <xsl:attribute name="num"> <xsl:value-of select="$i"/> </xsl:attribute> <!-- <xsl:copy-of select="$nodeToCopy/w:sdtContent/node()|@*"/>--> <xsl:apply-templates select="$nodeToCopy/w:sdtContent/node()|@*"/> </xsl:element> <xsl:call-template name="multiply"> <xsl:with-param name="maxCount" select="$maxCount"/> <xsl:with-param name="nodeToCopy" select="$nodeToCopy"/> <xsl:with-param name="i" select="$i+1"/> </xsl:call-template> </xsl:when> <xsl:otherwise/> </xsl:choose> </xsl:template> <xsl:template match="node()|@*" mode="variable"> <xsl:copy> <xsl:apply-templates select="node()|@*" mode="variable"/> </xsl:copy> </xsl:template> <xsl:template match="w:sdtContent/w:r[1]" mode="variable"> <xsl:copy> <xsl:apply-templates select="node()|@*" mode="variable"/> </xsl:copy> </xsl:template> <xsl:template match="w:sdtContent/w:r[position() != 1]" mode="variable"> </xsl:template></xsl:transform>
Transformation z_clean
<xsl:transformxmlns:xsl="http://www.w3.org/1999/XSL/Transform"xmlns:sap="http://www.sap.com/sapxsl"xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" version="1.0"> <xsl:output encoding="UTF-8" indent="yes" method="xml" omit-xml-declaration="no" standalone="yes"/> <xsl:template match="node()|@*"> <xsl:copy> <xsl:apply-templates select="node()|@*"/> </xsl:copy> </xsl:template> <xsl:template match="variable"> <xsl:apply-templates select="node()|@*"/> </xsl:template> <xsl:template match="block"> <xsl:apply-templates select="node()|@*"/> </xsl:template></xsl:transform>