Skip to Content
Technical Articles
Author's profile photo Anton Sikidin

Best way to generate Microsoft Word docx from ABAP

There are several ways to generate Microsoft Word docx documents using ABAP.

All of them have a number of disadvantages:

  • some require a lot of manual actions and saving structures in the ABAP dictionary
  • some require the Microsoft Office package installed on the user’s computer for their work

The advantage of my development:

  • requires a minimum of manual actions
  • works on the server side (does not require the presence of Microsoft Office)

If you know easier way to generate Microsoft Word docx documents – please, notify me.

Metrics for measuring the simplicity (lower is better):

  • mouse click: single click / double click / select = 2 points
  • keyboard type: one word / tab / enter = 1 point
  • switch application / alt + tab = 3 points
  • + cost of change (add 2 fields + remove 2 fields + rename 2 fields)

Source, readme: https://github.com/AntonSikidin/zcl_docx
Video (watch in 1080p):

Instruction and video complement each other.

Installation Install package via ABAPGIT https://docs.abapgit.org/guide-install.html

For example, the following document should be created:

At first toggle developer toolbar in Microsoft Word.
File -> Options -> Customize ribbon.

Go to developer tab, turn on “design mode”.

Select text that will be replaced.

Make tag.

Enter tag name.

 

Repeat for all variable.

For repeated rows or text fragment – select row or text fragment, make repeated control.

To enter properties of repeated control place cursor in the begin or end control.

Tag all variables and repeated part.

Save document. Go to transaction smw0 Select Binary data, enter.

Go to se38 Program ZDOCX_GET_TYPES.

Navigate to your template.

Run.

Program generate data types, based on your document structure.

Copy to your program.

Define variable.

Data
: gs_templ_data TYPE t_data
.

Fill structure with your data.

Then get document.

zcl_docx3=>get_document(
    iv_w3objid    = 'ZDOCX_EXAMLE' " name of our template, obligatory
*      iv_on_desktop = 'X'           " by default save document on desktop
*      iv_folder     = 'report'      " in folder by default 'report'
*      iv_path       = ''            " IF iv_path IS INITIAL  save on desctop or sap_tmp folder
*      iv_file_name  = 'report.docx' " file name by default
*      iv_no_execute = ''            " if filled -- just get document no run office
*      iv_protect    = ''            " if filled protect document from editing, but not protect from sequence
                                   " ctrl+a, ctrl+c, ctrl+n, ctrl+v, edit
    iv_data       = gs_templ_data  " root of our data, obligatory
*      iv_no_save    = ''            " just get binary data not save on disk
    ).

Whole program “ZDOCX_EXAMPLE”:

*&---------------------------------------------------------------------*
*& Report ZDOCX_EXAMPLE
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT zdocx_example.

*describe types

TYPES
: begin of t_TABLE3
,       PERSON type string
,       SALARY type string
, end of t_TABLE3

, tt_TABLE3 type table of t_TABLE3 with empty key


, begin of t_T2
,       F1 type string
,       F2 type string
,       F3 type string
, end of t_T2

, tt_T2 type table of t_T2 with empty key


, begin of t_T1
,       H1 type string
, T2 type tt_T2
, end of t_T1

, tt_T1 type table of t_T1 with empty key


, begin of t_LINE1
,       FIELD1 type string
,       FIELD2 type string
,       FIELD3 type string
,       FIELD4 type string
, end of t_LINE1

, tt_LINE1 type table of t_LINE1 with empty key


, begin of t_TAB1
,       TITLE1 type string
, LINE1 type tt_LINE1
, end of t_TAB1

, tt_TAB1 type table of t_TAB1 with empty key


, begin of t_LINE2
,       FIELD1 type string
,       FIELD2 type string
,       FIELD3 type string
, end of t_LINE2

, tt_LINE2 type table of t_LINE2 with empty key


, begin of t_TAB2
,       TITLE2 type string
, LINE2 type tt_LINE2
, end of t_TAB2

, tt_TAB2 type table of t_TAB2 with empty key


, begin of t_data
,       SH01 type string
,       DATE type string
,       TIME type string
,       USER type string
, TABLE3 type tt_TABLE3
, T1 type tt_T1
, TAB1 type tt_TAB1
, TAB2 type tt_TAB2
, end of t_data

, tt_data type table of t_data with empty key


.



*some variable
DATA
      : gs_templ_data TYPE t_data
      , lv_index TYPE i
      , lv_index2 TYPE i
      , lv_index3 TYPE i
      .


*fill data

gs_templ_data-DATE = |{ sy-datum date = environment }|.
gs_templ_data-TIME = |{ sy-uzeit   TIME = ENVIRONMENT }|.
gs_templ_data-USER  = sy-uname.


*1.  Lurch Schpellchek: 1200 usd
*2.  Russell Sprout: 1300 usd
*3.  Fergus Douchebag: 3000 usd
*4.  Bartholomew Shoe: 100 usd

APPEND INITIAL LINE TO gs_templ_data-table3 ASSIGNING FIELD-SYMBOL(<ls_3>).
<ls_3>-person = 'Lurch Schpellchek'.
<ls_3>-salary = '1200'.

APPEND INITIAL LINE TO gs_templ_data-table3 ASSIGNING <ls_3>.
<ls_3>-person = 'Russell Sprout'.
<ls_3>-salary = '1300'.

APPEND INITIAL LINE TO gs_templ_data-table3 ASSIGNING <ls_3>.
<ls_3>-person = 'Fergus Douchebag'.
<ls_3>-salary = '3000'.

APPEND INITIAL LINE TO gs_templ_data-table3 ASSIGNING <ls_3>.
<ls_3>-person = 'Bartholomew Shoe'.
<ls_3>-salary = '100'.



gs_templ_data-sh01 = 'test aaa'.

DO 3 TIMES.
  lv_index = sy-index.

  APPEND INITIAL LINE TO gs_templ_data-tab1 ASSIGNING FIELD-SYMBOL(<ls_tab1>).
  <ls_tab1>-title1 = |table 1 subtable { lv_index }|.


  DO 3  TIMES.
    lv_index2 = sy-index.
    APPEND INITIAL LINE TO <ls_tab1>-line1 ASSIGNING FIELD-SYMBOL(<ls_line1>).

    DO 4 TIMES.
      lv_index3 = sy-index.

      ASSIGN COMPONENT lv_index3 OF STRUCTURE <ls_line1> TO FIELD-SYMBOL(<fs_any>).

      <fs_any> = |Line { lv_index2  } field { lv_index3 }|.

    ENDDO.


  ENDDO.


ENDDO.


DO 3 TIMES.
  lv_index = sy-index.

  APPEND INITIAL LINE TO gs_templ_data-tab2 ASSIGNING FIELD-SYMBOL(<ls_tab2>).
  <ls_tab2>-title2 = |Table 2 subtable { lv_index }|.


  DO 3  TIMES.
    lv_index2 = sy-index.
    APPEND INITIAL LINE TO <ls_tab2>-line2 ASSIGNING FIELD-SYMBOL(<ls_line2>).

    DO 3 TIMES.
      lv_index3 = sy-index.

      ASSIGN COMPONENT lv_index3 OF STRUCTURE <ls_line2> TO <fs_any>.

      <fs_any> = |Line { lv_index2  } field { lv_index3 }|.

    ENDDO.


  ENDDO.


ENDDO.

gs_templ_data = VALUE #( BASE gs_templ_data
  t1 = VALUE #(
    ( h1 = |1| t2 = VALUE #(
        ( f1 = 'F1' f2 = 'F2' f3 = 'f3' )
        ( f1 = 'F1' f2 = 'F2' f3 = 'f3' )
      )
    )
    ( h1 = |2| t2 = VALUE #(
        ( f1 = 'F1' f2 = 'F2' f3 = 'f3' )
        ( f1 = 'F1' f2 = 'F2' f3 = 'f3' )
        ( f1 = 'F1' f2 = 'F2' f3 = 'f3' )
      )
    )
    ( h1 = |3| t2 = VALUE #(
        ( f1 = 'F1' f2 = 'F2' f3 = 'f3' )
        ( f1 = 'F1' f2 = 'F2' f3 = 'f3' )
        ( f1 = 'F1' f2 = 'F2' f3 = 'f3' )
        ( f1 = 'F1' f2 = 'F2' f3 = 'f3' )
      )
    )
  )
).


*get document

DATA
       : lv_document TYPE xstring  " variable to hold generated document, can be omitted
       .

*first case:  send document as attachment

lv_document = zcl_docx3=>get_document(
    iv_w3objid    = 'ZDOCX_EXAMLE'
    iv_data       = gs_templ_data
    iv_no_save    = 'X'   ).

PERFORM send_document_as_attachment USING lv_document.



*second case: save on desctop and open document

zcl_docx3=>get_document(
    iv_w3objid    = 'ZDOCX_EXAMLE' " name of our template, obligatory
*      iv_on_desktop = 'X'           " by default save document on desktop
*      iv_folder     = 'report'      " in folder by default 'report'
*      iv_path       = ''            " IF iv_path IS INITIAL  save on desctop or sap_tmp folder
*      iv_file_name  = 'report.docx' " file name by default
*      iv_no_execute = ''            " if filled -- just get document no run office
*      iv_protect    = ''            " if filled protect document from editing, but not protect from sequence
                                   " ctrl+a, ctrl+c, ctrl+n, ctrl+v, edit
    iv_data       = gs_templ_data  " root of our data, obligatory
*      iv_no_save    = ''            " just get binary data not save on disk
    ).


FORM send_document_as_attachment USING iv_doc TYPE xstring.

*  implement sending here
  MESSAGE 'Doc sended' TYPE 'S'.
ENDFORM.

 

How to interpret \n as a new line?

No way

If you want several line use Repeating section content control

 

insert field inside table control

 

and make something like

if you want all lines be in single line then make field inside field. Twise use

 

and make this

program

REPORT  z_multiline_test.


TYPES
: begin of t_TABLE1
,       FIELD1 type string
, end of t_TABLE1

, tt_TABLE1 type table of t_TABLE1 with default key

, begin of t_data
, TABLE1 type tt_TABLE1
, end of t_data

, tt_data type table of t_data with default key
.

DATA
      : gs_templ_data TYPE t_data
      .

FIELD-SYMBOLS
               : <fs_line> TYPE t_TABLE1
               .

APPEND INITIAL LINE TO gs_templ_data-TABLE1 ASSIGNING <fs_line>.
<fs_line>-FIELD1 = 'asd'.

APPEND INITIAL LINE TO gs_templ_data-TABLE1 ASSIGNING <fs_line>.
<fs_line>-FIELD1 = 'fffff'.

APPEND INITIAL LINE TO gs_templ_data-TABLE1 ASSIGNING <fs_line>.
<fs_line>-FIELD1 = 'hhhhh'.


zcl_docx3=>get_document(
    iv_w3objid    = 'ZDOCX_EXAMLE_LINE'   " name of our template
*      iv_template   = ''            " you can feed template as xstring instead of load from smw0
*      iv_on_desktop = 'X'           " by default save document on desktop
*      iv_folder     = 'report'      " in folder by default 'report'
*      iv_path       = ''            " IF iv_path IS INITIAL  save on desctop or sap_tmp folder
      iv_file_name  = 'multiline.docx' " file name by default
*      iv_no_execute = ''            " if filled -- just get document no run office
*      iv_protect    = ''            " if filled protect document from editing, but not protect from sequence
                                   " ctrl+a, ctrl+c, ctrl+n, ctrl+v, edit
    iv_data       = gs_templ_data  " root of our data, obligatory
*      iv_no_save    = ''            " just get binary data not save on disk
    ).

 

result

 

Conclusion:

  • Using this development in your daily work you will save your working time.
  • Freed up time you can spend on legacy code refactoring.
  • After reading this post you can think of a way to generate reports better than this.
  • Your boss will notice your progress.
  • You can ask for a bigger salary.
  • Your hair will become clean and silky.
  • Your dog will stop do bad things.
  • You can play the piano again even if you didn’t.
  • People will populate Mars and there will be apple orchards on Mars.

Assigned Tags

      57 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Shai Sinai
      Shai Sinai

      Thanks for sharing.

      Check also Generate DOCX file in ABAP of Sébastien HERMANN.

      Author's profile photo Anton Sikidin
      Anton Sikidin
      Blog Post Author

      Thank you.

      I checked this project.
      The author has done a great job.
      I have something to learn from this project.

      But this project does not solve the main problem: fill out the template with data for the least amount of action.

      Anton

      Author's profile photo Michael Keller
      Michael Keller

      I'm a fan of the "conclusion part" 🙂

      Author's profile photo Sandra Rossi
      Sandra Rossi

      Thank you! Very smart way of generating Word documents. Minimal work for the developer. I don't see how it could be easier.

      Is it possible to:

      • make the tool more "basis", like CRM_IC_XML_XSTRING2STRING doesn't exist in ABAP developer editions (if it's UTF-8, this should work from ABAP 7.02: string = cl_abap_codepage=>convert_from( xstring )),
      • have a Word template stored anywhere instead of forcing the storage in SMW0 (like a new parameter iv_template of type xstring, making iv_w3objid optional),
      • have the option to generate the ABAP types with the comma at the traditional end position so that the Eclipse ADT pretty printer can align the "TYPES",
      • and make the tool available as an online version instead of ZIP so that other developers can easily propose changes?

      That would be nice if someone proposes the same kind of solution for Excel (that would be easier than abap2xlsx) 😉

      Sandra

      Author's profile photo Anton Sikidin
      Anton Sikidin
      Blog Post Author

      Thank you, Sandra, for your feedback.
      Your suggestions have been submitted to the master branch.

      There may be some problems when processing Unicode text in national alphabets in a non-Unicode system.

      I will fix it when I see it.

       

      Two years ago i write some patch to the abap2xlsx.

      Since then, the project has gone further in development and my patches are no longer compatible.

      All you need it is add few methods:

      • load_from_smw0 - it is for me, i prefer to store template this way
      • find_row_by_tag - find row index by your tag(some text in the cell for example [ROW1])
      • extract_row - make local copy of row template for further use
      • extract_rows - make local copy of whole table(header, row, subtotal, total)
      • copy_row - insert your local row template
      • del_row -It is obvious
      • write_cell_by_tag - replace text in cell with text [TAG_HERE]
      • some useful tool to adress cell by index instead of AZ12

       

      With these tools you can decompose the template into atoms and assemble in the desired order

      Almost every method returns an index of an added, deleted, found, modified row in order to orientate at least a little in space.

      Anton

      Author's profile photo Sandra Rossi
      Sandra Rossi

      Impressive reactivity, thank you!

      Author's profile photo Anton Sikidin
      Anton Sikidin
      Blog Post Author

      I think about fill template for abap2xlsx.

      It is possible.

      Author's profile photo Anton Sikidin
      Anton Sikidin
      Blog Post Author

      Hello, Sandra.

      I think it is what your looking for.

      https://blogs.sap.com/2020/05/22/best-way-to-generate-microsoft-excel-xlsx-from-template-in-abap/

      Author's profile photo Sandra Rossi
      Sandra Rossi

      Really nice, thank you! I will test it and surely propose as an alternative to abap2xlsx. Now let's see the reaction of the community.

      Author's profile photo Manuel Robalinho
      Manuel Robalinho

      Great! Nice information!

      Author's profile photo Fina Martí
      Fina Martí

      Awesome development!!!!

      Thank you very much.

      A question: Is there any way to change the color of a font?

      Author's profile photo Anton Sikidin
      Anton Sikidin
      Blog Post Author

      Yes, You define color, size, position on template. Then my program just replace text.

      source

      source

       

      result

      result

      Author's profile photo Dhiraj More
      Dhiraj More

      Great blog:). Can we also include SO10 text using this method.

       

      Thanks,

      Dhiraj M

      Author's profile photo Ghadeer Alsubsie
      Ghadeer Alsubsie

      This blog is legendry !!!  will definitely save my life of alot manual work .

      can I convert the result word to PDF then Base64 ? if you have any insights on that would like to know it .

      Author's profile photo Paul Hardy
      Paul Hardy

      That worked really well. It is a lot less effort than the other method (mentioned in the first comment) because all the formatting is done in Word (which it what is designed for) and all the data processing is done in ABAP (which is what it is designed for).

      Unless you have any violent objections I would like to mention this in the next edition of my "ABAP to the Future" book.

      I do have one silly question - I can email the Word document to myself no problem but the attachment has no DOCX suffix. What do you pass into the TYPE parameter of the BCS class? To get an XLSX suffix in an attachment you pass in EXT which I have never really understood why. There must be some sort of mapping hiding somewhere inside SAP.

      Cheersy Cheers

      Paul

      Author's profile photo Paul Hardy
      Paul Hardy

      Just one more thing (as Columbo would say)

      You can set this up so the document opens up in protected mode, but whoever opens the document can easily switch that off.

      I am looking at how to turn the document into a PDF document inside SAP (must be possible I would have thought) but if I fail I imagine I could generate a 32 digit UID and then inject into the XML definition of the document that it is password protected and the password is the UID.

      That way no-one would ever be able to change the generated document.

      Author's profile photo Paul Hardy
      Paul Hardy

      I just debugged the class, it already sets a random encrypted password as far as I can see so that is OK, which is just as well, as turning a word document into a PDF inside SAP seems to fall into the "too hard" basket.....

      Author's profile photo Anton Sikidin
      Anton Sikidin
      Blog Post Author

      Generating pdf in this way is my third project, after abap based instant messaging and graph isomorphism problem.

      Author's profile photo Hans-Joachim Schmidtke
      Hans-Joachim Schmidtke

      HI,

       

      I have also a question related to this. We are using you Winword creation and it works really great to create files, we store them as Winword in DMS etc.

      BUT : a problem comes up with printing.

      So we use Smartforms in PP or QM etc. and want to print as an appending basically a DMS Winword "Form" document. Now: of course we want to print in together with the Smart for on the server side to use SAP printer and not front end printer. So ..... if it would be a PDF File we would use binary combined printing. But it seems with Winword documents that is not working.

      So we get stuck here.

      A solution could be : to convert before printing the  Winword document to PDF and then use binary printing of the Smart form and PDF file. But I can not find any Bapis to do a Winword to PDF conversion.

      Do you have any recommendation or hints

       

      Author's profile photo Shai Sinai
      Shai Sinai

      Hi,

      Regarding filename extensions longer than 3 characters (e.g. DOCX), you have the following note:

      1459896 - BCS: Support for four-digit file name extensions

      Author's profile photo Paul Hardy
      Paul Hardy

      That worked fine. Thanks for that,

      Author's profile photo Anton Sikidin
      Anton Sikidin
      Blog Post Author

      Hello, I have no objection. If it's possible, I would like to get a copy of the book.

      Author's profile photo Naveen Inuganti
      Naveen Inuganti

      Hi,

       

      First of all. This is really very nice blog. Thanks for sharing.

       

      I am printing some text on the Word document with the same approach. But, all line breaks are gone and text is printed as one big paragraph. I tried cl_abap_char_utilities=>newline  and other attributes from the class. Tried ^p and ^l as suggested in other forums. Nothing is working. Can you suggest how to handle it.

       

      Regards,

      Naveen

      Author's profile photo Sandra Rossi
      Sandra Rossi

      (for information ^p and ^l are special characters for "new paragraph" and "new line" used by the Find/Replace user feature of Word and by its corresponding methods in the Word Object Model (VBA, OLE, ...), but they are not used anywhere else)

      Author's profile photo Anton Sikidin
      Anton Sikidin
      Blog Post Author

      new line in word is complex.

      in notepad new line represent as cl_abap_char_utilities=>newline

      in html new line represent as <br>

      in word new line represent as

      <w:p w14:paraId="34BB7615" w14:textId="07E74D42" w:rsidR="00E41B12" w:rsidRPr="009C0DF9" w:rsidRDefault="009C0DF9">
      <w:pPr>
      <w:rPr>
      <w:lang w:val="en-US"/>
      </w:rPr>
      </w:pPr>
      <w:r>
      <w:rPr>
      <w:lang w:val="en-US"/>
      </w:rPr>
      <w:t>paragraph</w:t>
      </w:r>
      <w:bookmarkStart w:id="0" w:name="_GoBack"/>
      <w:bookmarkEnd w:id="0"/>
      </w:p>

      and text in word represent as

      <w:t>paragraph</w:t>

       

      so if you wont several lines you shold pass it as table of line.

      Author's profile photo Chandini Bangera
      Chandini Bangera

      Hi, I have an issue with adding a new line on the word.  For example, I have to get the texts from SO10 text. Say a text has 3 sentences that need to be printed one below the other.

       

      in the template, i have @TEXT@

      this @TEXT@ need to be replaced by the 3 sentences.  Currently its getting displayed as a single block. I need to add a newline after each sentence. I tried to add <w:t> manually while concatenating the texts however they appear as it is.

       

      Author's profile photo Maksim Huziuk
      Maksim Huziuk

      Can we place tag for IMG in MS Word  and then put there different img?

      Author's profile photo Anton Sikidin
      Anton Sikidin
      Blog Post Author

      Yes, it is possible but take a lot of work, because image save in other way than text and this is need additional transformation.

      Author's profile photo Daisy Ding
      Daisy Ding

      Hi  Sikidin,

      Thanks for sharing.

      Can we change the checkbox defined in word developer by your code?

      And how to do the wrap of the text ?

      Author's profile photo Anton Sikidin
      Anton Sikidin
      Blog Post Author

      yes, it is possible to change check box, i will add it to repositary soon.

       

      Please explain your secon question with example, i dont understand it

      Author's profile photo Daisy Ding
      Daisy Ding

      Never mind the second question, I find it can be set in the word.(wrap text).

      For changing checkbox , which method /code you have expand?

      Thanks a lot !!

       

       

      Author's profile photo Anton Sikidin
      Anton Sikidin
      Blog Post Author

      I think it is not method, it is xslt transformation zdocx_fill_template

      Author's profile photo Daisy Ding
      Daisy Ding

      Thanks !!

      But I am not  good at xslt transformation.

      I will have a look if you have update it.

      Author's profile photo Anton Sikidin
      Anton Sikidin
      Blog Post Author

      i am working on update

      Author's profile photo Anton Sikidin
      Anton Sikidin
      Blog Post Author

      As temporary workaround you can define 2 line with checked and unchecked box and show whitch you need

      Author's profile photo Anton Sikidin
      Anton Sikidin
      Blog Post Author

      Solution found, updating repo

      Author's profile photo Anton Sikidin
      Anton Sikidin
      Blog Post Author

      repo is updated!

       

      • empty table is fixed(when table have no data emty line is not shown)
      • add support of checkboxes

       

      example program - zdocx_example_checkbox

      example template - zdocx_example_checkbox

      source template

      template%20source

      template source

       

      result

      result

       

      how it works:

      add checkbox

       

      name  it. any unnamed fields keept as is, but you can have problem with zdocx_get_types.

       

      There is  some limitation.

      As you can see there is option to change symbol of checked and unchecked box

      I don't recomend to do this.

      This symbols in word xml represent as unicode symbols which is not printable in sap wen conwert in sap string to process.

       

      to avoid this problem i replace ☒ with [E29892] and ☐ with [E29890] before processing in zcl_docx=>protect_space

         DATA
                : lv_string TYPE string
                , lt_content_source TYPE TABLE OF string
                , lt_content_dest TYPE TABLE OF string
                , lv_from_xstring92(3) TYPE x VALUE 'E29892'
                , lv_from_xstring90(3) TYPE x VALUE 'E29890'
                , lv_to_string92(8) TYPE x VALUE '5B4532393839325D'
                , lv_to_string90(8) TYPE x VALUE '5B4532393839305D'
                , lv_content TYPE xstring
                .
      
          lv_content = iv_content.
      
          REPLACE ALL OCCURRENCES OF lv_from_xstring92 IN lv_content WITH lv_to_string92 IN BYTE MODE .
          REPLACE ALL OCCURRENCES OF lv_from_xstring90 IN lv_content WITH lv_to_string90 IN BYTE MODE .

       

      Then restore in zcl_docx=>restore_space

          DATA
                    : lv_from_xstring92(3) TYPE x VALUE 'E29892'
                    , lv_from_xstring90(3) TYPE x VALUE 'E29890'
                    , lv_to_string92(8) TYPE x VALUE '5B4532393839325D'
                    , lv_to_string90(8) TYPE x VALUE '5B4532393839305D'
                    .
      
      
      
          REPLACE ALL OCCURRENCES OF lv_to_string92 IN rv_content WITH lv_from_xstring92 IN BYTE MODE .
          REPLACE ALL OCCURRENCES OF lv_to_string90 IN rv_content WITH lv_from_xstring90 IN BYTE MODE .
      *    CALL FUNCTION 'CRM_IC_XML_STRING2XSTRING'

       

      and in xslt transform zdocx_fill_template.xslt

                  <!--  переменная  -->
                  <xsl:variable name="value">
                    <xsl:choose>
                      <xsl:when test="$is_check_box = 0">
                        <!--  не чекбокс  -->
                        <xsl:value-of select="$tmp_value"/>
                        <!--  копируем переменную  -->
                      </xsl:when>
                      <xsl:otherwise>
                        <!--  тут чекбокс  -->
                        <xsl:choose>
                          <xsl:when test="$tmp_value != ''">
                            <!--  тут крестик  -->
                            <xsl:value-of select="string('[E29892]')"/>
                          </xsl:when>
                          <xsl:otherwise>
                            <!--  тут без крестика  -->
                            <xsl:value-of select="string('[E29890]')"/>
                          </xsl:otherwise>
                        </xsl:choose>
                      </xsl:otherwise>
                    </xsl:choose>
                  </xsl:variable>

      if you wont different symbols rewrite this 3 places, if you find universal solution make pull request.

       

      in gs_template_data

      • if field is not "" it represent as ☒ checked
      • if field is empty it is represent as  ☐ unchecked
      Author's profile photo Daisy Ding
      Daisy Ding

      Hi  Sikidin,

      I follow your step, update the protect_space , restore_space(add at end of the method) and transform zdocx_fill_template.xslt ,

      Now it works fine , Thanks a lot!!

      Author's profile photo Evgeniy Khlopov
      Evgeniy Khlopov

      Hi Anton,

       

      Error with no exception handling

       

      We have updated the code

       

      Is there another solution or correction of the source code possible?

       

      Thank you

       

      Author's profile photo Anton Sikidin
      Anton Sikidin
      Blog Post Author

      Hi Evgeniy Khlopov

      I assume that the original template is corrupted, or one that I have not seen. Could you send it to me for analysis.

      There are many places where data validation is not performed.

      All input is assumed to be valid.
      Your solution is the best we have.

      But I would add an error message so that the user can uniquely identify the problem, instead the document is not printed.

      For example "template is corrupted"

       

      I will need to add checks to all places where something might go wrong.

      At the moment, I am striking a balance between the frequency of error reproducibility and the effort required to neutralize it.

      Submit a pull request, or later with the next update I will fix this error.

       

      Thank you

      Author's profile photo Daniel Ruiz
      Daniel Ruiz

      Notable,
      felicitaciones por este extraordinario trabajo.

      Author's profile photo Emil Schmid
      Emil Schmid

      Hi Anton,

      Great. That work really well…

      I have two questions:

      1. Is it possible, not delete the content controls in the generated document?

      If I set the "Content control cannot be deleted", in the Content Control Properties for rich text, the content control is be removed anyway in the new document.

      2. Can also reuse the content control in the header or footer?

      Author's profile photo Anton Sikidin
      Anton Sikidin
      Blog Post Author

      I have answer for first question.  Yes you can ommit deleting content controls.

      in method get_document just comment this lines  from line 125.

          call transformation zdocx_del_wsdt
            source xml lv_res_xml_xstr
            result xml lv_res_xml_xstr.

       

      i dont understand your second question, can you explain?

      Author's profile photo Emil Schmid
      Emil Schmid

      Thank you for your quick answer.

      Question 1

      I tried your suggestion but the tag name is lost from the content control property.

      asdf

      contentControlProperty in the new document

      question 2

      I would use the content control in the header area of the word document.

      Unfortunately, the values from the ABAP are not transferred to the header in the word document.

      What am I doing wrong?

      word%20header%20area

      word header area

      Thank you

       

      Author's profile photo Lukas Weidmann
      Lukas Weidmann

      You could try to select the file 'word/header1.xml' by using the method lo_docx->mo_zip->get( ...
      and then execute the same code procedure as it is done for lo_docx->mc_document ( word/document.xml) and replace mc_document with 'word/header1.xml' :

      Author's profile photo Emil Schmid
      Emil Schmid

      Thank you for your support, it works excellent.

      Author's profile photo Lech Dzierzawski
      Lech Dzierzawski

      Hi

      could you please provide a sample code?

      thank you.

      Lech

       

      Author's profile photo Lech Dzierzawski
      Lech Dzierzawski

      sample code:

       

          lo_docx->mo_zip->get( EXPORTING  name =  lo_docx->mc_document
                          IMPORTING content = lv_content ).
      
          lo_docx->protect_space(
            EXPORTING
              iv_content = lv_content
            RECEIVING
              rv_content = lv_doc_xml_xstr )   .
      
          CALL TRANSFORMATION zdocx_del_repeated_text
                SOURCE XML lv_doc_xml_xstr
                RESULT XML lv_doc_xml_xstr.
      
          CALL TRANSFORMATION zdocx_fill_template
                SOURCE XML lv_doc_xml_xstr
                PARAMETERS data = lo_docx->mo_templ_data_nc
                RESULT XML lv_res_xml_xstr.
      
            CALL TRANSFORMATION zdocx_del_wsdt
               SOURCE XML lv_res_xml_xstr
               RESULT XML lv_res_xml_xstr.
      
          lo_docx->restore_space(
            EXPORTING
              iv_content = lv_res_xml_xstr
            RECEIVING
              rv_content = lv_content )  .
      
          IF iv_protect IS NOT INITIAL .
            lo_docx->protect( ).
          ENDIF.
      
          lo_docx->mo_zip->delete( name = lo_docx->mc_document ).
          lo_docx->mo_zip->add( name    = lo_docx->mc_document
                     content = lv_content ).
      
      **********************************************************************
      *** Word header section
          lo_docx->mo_zip->get( EXPORTING  name =  lo_docx->mc_document_header
                          IMPORTING content = lv_content ).
      
          lo_docx->protect_space(
            EXPORTING
              iv_content = lv_content
            RECEIVING
              rv_content = lv_doc_xml_xstr )   .
      
          CALL TRANSFORMATION zdocx_del_repeated_text
                SOURCE XML lv_doc_xml_xstr
                RESULT XML lv_doc_xml_xstr.
      
          CALL TRANSFORMATION zdocx_fill_template
                SOURCE XML lv_doc_xml_xstr
                PARAMETERS data = lo_docx->mo_templ_data_nc
                RESULT XML lv_res_xml_xstr.
      
            CALL TRANSFORMATION zdocx_del_wsdt
               SOURCE XML lv_res_xml_xstr
               RESULT XML lv_res_xml_xstr.
      
          lo_docx->restore_space(
            EXPORTING
              iv_content = lv_res_xml_xstr
            RECEIVING
              rv_content = lv_content )  .
      
          IF iv_protect IS NOT INITIAL .
            lo_docx->protect( ).
          ENDIF.
      
          lo_docx->mo_zip->delete( name = lo_docx->mc_document_header ).
          lo_docx->mo_zip->add( name    = lo_docx->mc_document_header
                     content = lv_content ).
      
      *** Word Footer section
          lo_docx->mo_zip->get( EXPORTING  name =  lo_docx->mc_document_footer
                          IMPORTING content = lv_content ).
      
          lo_docx->protect_space(
            EXPORTING
              iv_content = lv_content
            RECEIVING
              rv_content = lv_doc_xml_xstr )   .
      
          CALL TRANSFORMATION zdocx_del_repeated_text
                SOURCE XML lv_doc_xml_xstr
                RESULT XML lv_doc_xml_xstr.
      
          CALL TRANSFORMATION zdocx_fill_template
                SOURCE XML lv_doc_xml_xstr
                PARAMETERS data = lo_docx->mo_templ_data_nc
                RESULT XML lv_res_xml_xstr.
      
            CALL TRANSFORMATION zdocx_del_wsdt
               SOURCE XML lv_res_xml_xstr
               RESULT XML lv_res_xml_xstr.
      
          lo_docx->restore_space(
            EXPORTING
              iv_content = lv_res_xml_xstr
            RECEIVING
              rv_content = lv_content )  .
      
          IF iv_protect IS NOT INITIAL .
            lo_docx->protect( ).
          ENDIF.
      
          lo_docx->mo_zip->delete( name = lo_docx->mc_document_footer ).
          lo_docx->mo_zip->add( name    = lo_docx->mc_document_footer
                     content = lv_content ).
      **********************************************************************
      
          rv_document = lo_docx->mo_zip->save( ).
      Author's profile photo Emil Schmid
      Emil Schmid

      Hi Lech,

      My solution looks like this:

        METHOD USE_HEADER.

      DATA: lv_content      TYPE xstring,
      lv_res_xml_xstr TYPE xstring,
      lv_doc_xml_xstr TYPE xstring.

      DATA(itl_zip) = mo_zip->files.
      SORT itl_zip BY name ASCENDING.
      DELETE itl_zip WHERE name NS 'word/header'.
      LOOP AT itl_zip INTO DATA(zip) WHERE name CS 'word/header'.
      IF sy-subrc EQ 0.
      mo_zip->get( EXPORTING  name =  zip-name
      IMPORTING content = lv_content ).

      protect_space(
      EXPORTING
      iv_content = lv_content
      RECEIVING
      rv_content = lv_doc_xml_xstr ).

      CALL TRANSFORMATION zdocx_del_repeated_text
      SOURCE XML lv_doc_xml_xstr
      RESULT XML lv_doc_xml_xstr.

      CALL TRANSFORMATION zdocx_fill_template
      SOURCE XML lv_doc_xml_xstr
      PARAMETERS data = mo_templ_data_nc
      RESULT XML lv_res_xml_xstr.

      CALL TRANSFORMATION zdocx_del_wsdt
      SOURCE XML lv_res_xml_xstr
      RESULT XML lv_res_xml_xstr.

      restore_space(
      EXPORTING
      iv_content = lv_res_xml_xstr
      RECEIVING
      rv_content = lv_content )  .

      *-- Die Headerdateien werden nicht einzeln geschützt
      *          protect( ).
      mo_zip->delete( name = zip-name ).
      mo_zip->add( name    = zip-name content = lv_content ).
      ENDIF.
      ENDLOOP.

      ENDMETHOD.

       

        METHOD use_footer.

      DATAlv_content      TYPE xstring,
      lv_res_xml_xstr TYPE xstring,
      lv_doc_xml_xstr TYPE xstring.

      DATA(itl_zipmo_zip->files.
      SORT itl_zip BY name ASCENDING.
      DELETE itl_zip WHERE name NS 'word/footer'.
      LOOP AT itl_zip INTO DATA(zipWHERE name CS 'word/footer'.
      IF sy-subrc EQ 0.
      mo_zip->getEXPORTING  name =  zip-name
      IMPORTING content lv_content ).

      protect_space(
      EXPORTING
      iv_content lv_content
      RECEIVING
      rv_content lv_doc_xml_xstr ).

      CALL TRANSFORMATION zdocx_del_repeated_text
      SOURCE XML lv_doc_xml_xstr
      RESULT XML lv_doc_xml_xstr.

      CALL TRANSFORMATION zdocx_fill_template
      SOURCE XML lv_doc_xml_xstr
      PARAMETERS data mo_templ_data_nc
      RESULT XML lv_res_xml_xstr.

      CALL TRANSFORMATION zdocx_del_wsdt
      SOURCE XML lv_res_xml_xstr
      RESULT XML lv_res_xml_xstr.

      restore_space(
      EXPORTING
      iv_content lv_res_xml_xstr
      RECEIVING
      rv_content lv_content )  .

      *-- Die Footerdateien werden nicht einzeln geschützt
      *          protect( ).
      mo_zip->deletename zip-name ).
      mo_zip->addname    zip-name content lv_content ).
      ENDIF.
      ENDLOOP.

      ENDMETHOD.

      Author's profile photo Anton Sikidin
      Anton Sikidin
      Blog Post Author

      On the first question, I remembered.

      Not everything is so simple there, you need to change the xslt transformation, my previous answer is incorrect.

      On the second question, @Lukas Weidmann  answered correctly.

      Author's profile photo Emil Schmid
      Emil Schmid

      Thank you Anton for the response.

      The first question was another idea because i couldn't put the data into the header line.

      @Lukas Weidmann's solution solves my whole problem to write in the header.

       

      Thank you and have a nice weekend

       

       

       

      Author's profile photo Anton Sikidin
      Anton Sikidin
      Blog Post Author

      Hi Emil,

      i am just made some change in zdocx_fill_template.xslt.source.xml

      https://github.com/AntonSikidin/zcl_docx/commit/b9413eeecdb92ef95249a5adbf3692c817e9ad9e

      now if you commit in method get_document.

          call transformation zdocx_del_wsdt
            source xml lv_res_xml_xstr
            result xml lv_res_xml_xstr.

      checkbox working in result document.

      Author's profile photo JianHua Zhou
      JianHua Zhou

      I am getting the following error during import
      Current login language 'EN' does not match main language 'UK'.
      Is there any solution?

      Thanks

      Author's profile photo Anton Sikidin
      Anton Sikidin
      Blog Post Author

      i am make some fix to repo.

      But you can update abapgit to latest version.

      v1.60.1 show same error

      new version 1.120.0 load repo normal

       

      Author's profile photo Martin Blomeyer
      Martin Blomeyer

      Hi,

      Really nice project, has saved me days of work already!

      I was trying to feed the template from a file instead of SMW0, but as soon as I start to assign a path, I get the following error message:

      1 error found. Should it still be activated?
      Line 70: "P_FILE" is not type-compatible with formal parameter "IV_TEMPLATE"

      As far as I understand, the get_document method, it performs the conversion to an xstring in that part of the code or do I have to feed the IV_TEMPLATE variable with a docx converted to xstring myself?

      Author's profile photo Anton Sikidin
      Anton Sikidin
      Blog Post Author

      you tried to pass file path as template.

      You should upload file by your path, then convert to xstring, then pass template.

      data
      : lt_data            type table of ssfbin
      , lv_template          type xstring
      .
      
      cl_gui_frontend_services=>gui_upload(
        exporting
          filename                = p_file
          filetype                = 'BIN'
        importing
          filelength = lv_filelength
        changing
          data_tab                = lt_data
        exceptions
          file_open_error         = 1
          file_read_error         = 2
          no_batch                = 3
          gui_refuse_filetransfer = 4
          invalid_type            = 5
          no_authority            = 6
          unknown_error           = 7
          bad_data_format         = 8
          header_not_allowed      = 9
          separator_not_allowed   = 10
          header_too_long         = 11
          unknown_dp_error        = 12
          access_denied           = 13
          dp_out_of_memory        = 14
          disk_full               = 15
          dp_timeout              = 16
          not_supported_by_gui    = 17
          error_no_gui            = 18
          others                  = 19 ).
      if sy-subrc ne 0.
        message id sy-msgid type sy-msgty number sy-msgno with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
      endif.
      
      call function 'SCMS_BINARY_TO_XSTRING'
        exporting
          input_length = lv_filelength
        importing
          buffer       = lv_template
        tables
          binary_tab   = lt_data
        exceptions
          failed       = 1
          others       = 2.
      if sy-subrc ne 0.
        message id sy-msgid type sy-msgty number sy-msgno with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
      endif.
      
      zcl_docx3=>get_document(
      *    iv_w3objid    = 'ZDOCX_EXAMLE'  " name of our template
            iv_template   = lv_template    " you can feed template as xstring instead of load from smw0
      *      iv_on_desktop = 'X'           " by default save document on desktop
      *      iv_folder     = 'report'      " in folder by default 'report'
      *      iv_path       = ''            " IF iv_path IS INITIAL  save on desctop or sap_tmp folder
      *      iv_file_name  = 'report.docx' " file name by default
      *      iv_no_execute = ''            " if filled -- just get document no run office
      *      iv_protect    = ''            " if filled protect document from editing, but not protect from sequence
                                         " ctrl+a, ctrl+c, ctrl+n, ctrl+v, edit
          iv_data       = gs_templ_data  " root of our data, obligatory
      *      iv_no_save    = ''            " just get binary data not save on disk
          ).
      Author's profile photo Martin Blomeyer
      Martin Blomeyer

      Works perfect, thanks for the quick reply.