Quelquepart

Blog d'un développeur

Vous êtes ici : Accueil>Show code in Nugget

Show code in Nugget ZCL_WORD.nugg

Table of content

Program ZCL_WORD: Include ZCL_WORD

ABAP Code

*----------------------------------------------------------------------*
*       CLASS   : cl_word
*       Author  : S. Hermann
*       Date    : 14.07.2017
*       Version : v1.4
*----------------------------------------------------------------------*
*       Abstraction class to create MS Word DOCX from ABAP
*----------------------------------------------------------------------*
* 14.07.2017 - v1.4
*         Add insert_attachment method
* 23.01.2017 - v1.3.1
*         Add Allow direct input of xml into a virtual field
*         Add Allow input of table (or other paragraph xml fragment
*             into virtual field
*         Add Allow breakpage to be virtual" to add breakpage/line
*             inside an xml fragment
* 18.04.2016 - v1.3
*         Fix bug with table width definition (for word 2007)
*         Mod documentation on canvas
*         Fix bug on virtual parameter for write_line
*         Add write_text can now generate also virtual paragraph
*         Fix compatibility issue with old ECC version
*         Add option in constructor  to create docx from xstring input
*         Add line_style_effect option on write_text to close paragraph
*             and apply detailed style on it
*         Add virtual option on write_table
*         Add option on insert_image method to allow the image to be
*             inserted in a existing paragraph
*         Add float management for images (inline, float, over, behind)
*         Add position management for images (left, right, center)
*         Add margin management for images
*         Add management of forms (rectangle, elipse, rounded rect)
* 24.01.2016 - v1.2
*         Add option on set_params to hide spellcheck in document
*         Add allow merge cells in write_table
* 13.12.2015 - v1.1
*         Add option to keep loaded template content
*         Add get_zip_file method
*         Add write_note method
*         Add write_comment method
*         Add image insertion in table
*         Add xml insertion in table for complex cell content
*         Add option on write_text to insert label number
*         Add option on write_line to get XML instead of buffer into doc
*         Add option to save zip file on server
*         Add options on method write_toc
*             Now you can write a toc of specific label(for ex.: figure)
*         Fix bug when template path is invalid
*         Fix dump when reuse an image
* v1.02 : Initial public release
*----------------------------------------------------------------------*

CLASS cl_word DEFINITION FINAL.
  PUBLIC SECTION.
    CONSTANTS :
      c_true                         TYPE i VALUE 1,
      c_false                        TYPE i VALUE 0,
* Predefined fields that can be added
      c_field_pagecount              TYPE string VALUE '##FIELD#PAGE##',    " MS Word current page counter
      c_field_pagetotal              TYPE string VALUE '##FIELD#NUMPAGES##', " MS Word total page counter
      c_field_title                  TYPE string VALUE '##FIELD#TITLE##',
      c_field_author                 TYPE string VALUE '##FIELD#AUTHOR##',
      c_field_authorlastmod          TYPE string VALUE '##FIELD#LASTSAVEDBY##',
      c_field_comments               TYPE string VALUE '##FIELD#COMMENTS##',
      c_field_keywords               TYPE string VALUE '##FIELD#KEYWORDS##',
      c_field_category               TYPE string VALUE '##FIELD#DOCPROPERTY CATEGORY##',
      c_field_subject                TYPE string VALUE '##FIELD#SUBJECT##',
      c_field_revision               TYPE string VALUE '##FIELD#REVNUM##',
      c_field_creationdate           TYPE string VALUE '##FIELD#CREATEDATE##',
      c_field_moddate                TYPE string VALUE '##FIELD#SAVEDATE##',
      c_field_todaydate              TYPE string VALUE '##FIELD#DATE##',
      c_field_filename               TYPE string VALUE '##FIELD#FILENAME##',
* Anchor for label
      c_label_anchor                 TYPE string VALUE '##LABEL##',
* Prefix for SAPWR url
      c_sapwr_prefix(6)              TYPE c VALUE 'SAPWR:',
* Predefined fonts
      c_font_arial                   TYPE string VALUE 'Arial', "#EC NOTEXT
      c_font_times                   TYPE string VALUE 'Times New Roman', "#EC NOTEXT
      c_font_comic                   TYPE string VALUE 'Comic Sans MS', "#EC NOTEXT
      c_font_calibri                 TYPE string VALUE 'Calibri', "#EC NOTEXT
      c_font_cambria                 TYPE string VALUE 'Cambria', "#EC NOTEXT
      c_font_courier                 TYPE string VALUE 'Courier New', "#EC NOTEXT
      c_font_symbol                  TYPE string VALUE 'Wingdings', "#EC NOTEXT
* Predefined Styles
* Regarding your document language, style could be named differently
* For example, in french ms document, title1 is labelled "Titre1"
      c_style_title                  TYPE string VALUE 'Title', "#EC NOTEXT
      c_style_title1                 TYPE string VALUE 'Title1', "#EC NOTEXT
      c_style_title2                 TYPE string VALUE 'Title2', "#EC NOTEXT
      c_style_title3                 TYPE string VALUE 'Title3', "#EC NOTEXT
      c_style_title4                 TYPE string VALUE 'Title4', "#EC NOTEXT
      c_style_normal                 TYPE string VALUE 'Normal', "#EC NOTEXT
* Predefined break types
      c_breaktype_line               TYPE i VALUE 6,
      c_breaktype_page               TYPE i VALUE 7,
      c_breaktype_column             TYPE i VALUE 1, "NOT MANAGED
      c_breaktype_section            TYPE i VALUE 2,
      c_breaktype_section_continuous TYPE i VALUE 3,
* Predefined symbols
      c_symbol_checkbox_checked      TYPE string VALUE 'þ', "#EC NOTEXT
      c_symbol_checkbox              TYPE string VALUE 'o', "#EC NOTEXT
* Draw CANVAS objects
      c_draw_image                   TYPE i VALUE 0,
      c_draw_rectangle               TYPE i VALUE 1,
* Direct draw objects
      c_ddraw_rectangle              TYPE string VALUE 'rect',
      c_ddraw_rectangle_rounded      TYPE string VALUE 'roundrect',
      c_ddraw_ovale                  TYPE string VALUE 'oval',

* Text alignment
      c_align_left                   TYPE string VALUE 'left', "#EC NOTEXT
      c_align_center                 TYPE string VALUE 'center', "#EC NOTEXT
      c_align_right                  TYPE string VALUE 'right', "#EC NOTEXT
      c_align_justify                TYPE string VALUE 'both', "#EC NOTEXT
* Vertical alignment
      c_valign_top                   TYPE string VALUE 'top', "#EC NOTEXT
      c_valign_middle                TYPE string VALUE 'center', "#EC NOTEXT
      c_valign_bottom                TYPE string VALUE 'bottom', "#EC NOTEXT
* Image type
* inline : image is included in a line of text
      c_imgtype_inline               TYPE i VALUE 1,
* float : text is wraping around the image
      c_imgtype_float                TYPE i VALUE 2,
* over : image is over the text
      c_imgtype_over                 TYPE i VALUE 3,
* behind : image is behind the text
      c_imgtype_behind               TYPE i VALUE 4,

* Image horizontal position
      c_imgposx_left                 TYPE string VALUE 'left',
      c_imgposx_right                TYPE string VALUE 'right',
      c_imgposx_center               TYPE string VALUE 'center',

* Image vertical position
      c_imgposy_top                  TYPE string VALUE 'top',
      c_imgposy_middle               TYPE string VALUE 'center',
      c_imgposy_bottom               TYPE string VALUE 'bottom',

* Types for header/footer
      c_type_header                  TYPE string VALUE 'header', "#EC NOTEXT
      c_type_footer                  TYPE string VALUE 'footer', "#EC NOTEXT
* Style type
      c_type_paragraph               TYPE string VALUE 'paragraph',
      c_type_character               TYPE string VALUE 'character',
      c_type_table                   TYPE string VALUE 'table',
      c_type_numbering               TYPE string VALUE 'numbering',
* Image type
      c_type_image                   TYPE string VALUE 'image',
* Page orientation
      c_orient_landscape             TYPE i VALUE 1, " Landscape
      c_orient_portrait              TYPE i VALUE 0, " Portrait
* Note type
      c_notetype_foot                TYPE i VALUE 1, "foot note
      c_notetype_end                 TYPE i VALUE 2, "end note
* Predefined colors
      c_color_black                  TYPE string VALUE '000000',
      c_color_blue                   TYPE string VALUE '0000FF',
      c_color_turquoise              TYPE string VALUE '00FFFF',
      c_color_brightgreen            TYPE string VALUE '00FF00',
      c_color_pink                   TYPE string VALUE 'FF00FF',
      c_color_red                    TYPE string VALUE 'FF0000',
      c_color_yellow                 TYPE string VALUE 'FFFF00',
      c_color_white                  TYPE string VALUE 'FFFFFF',
      c_color_darkblue               TYPE string VALUE '000080',
      c_color_teal                   TYPE string VALUE '008080',
      c_color_green                  TYPE string VALUE '008000',
      c_color_violet                 TYPE string VALUE '800080',
      c_color_darkred                TYPE string VALUE '800000',
      c_color_darkyellow             TYPE string VALUE '808000',
      c_color_gray                   TYPE string VALUE '808080',
      c_color_lightgray              TYPE string VALUE 'C0C0C0',
* Predefined border style
      c_border_simple                TYPE string VALUE 'single',
      c_border_double                TYPE string VALUE 'double',
      c_border_triple                TYPE string VALUE 'triple',
      c_border_dot                   TYPE string VALUE 'dotted',
      c_border_dash                  TYPE string VALUE 'dashed',
      c_border_wave                  TYPE string VALUE 'wave',
* Predefined font highlight color
      c_highlight_yellow             TYPE string VALUE 'yellow',
      c_highlight_green              TYPE string VALUE 'green',
      c_highlight_cyan               TYPE string VALUE 'cyan',
      c_highlight_magenta            TYPE string VALUE 'magenta',
      c_highlight_blue               TYPE string VALUE 'blue',
      c_highlight_red                TYPE string VALUE 'red',
      c_highlight_darkblue           TYPE string VALUE 'darkBlue',
      c_highlight_darkcyan           TYPE string VALUE 'darkCyan',
      c_highlight_darkgreen          TYPE string VALUE 'darkGreen',
      c_highlight_darkmagenta        TYPE string VALUE 'darkMagenta',
      c_highlight_darkred            TYPE string VALUE 'darkRed',
      c_highlight_darkyellow         TYPE string VALUE 'darkYellow',
      c_highlight_darkgray           TYPE string VALUE 'darkGray',
      c_highlight_lightgray          TYPE string VALUE 'lightGray',
      c_highlight_black              TYPE string VALUE 'black'
      .

    TYPES:
      BEGIN OF ty_border,
* Border width
* Integer: 8 = 1pt
* Default: no border
        width TYPE i,

* Space between border and content
* Integer
* Default: document default
        space TYPE i,

* Border color
* You can use the predefined font color constants or specify any rgb hexa color code
* Default : document default
        color TYPE string,

* Border style
* You can use the predefined border style constants
* Default : document default
        style TYPE string, "border style
      END OF ty_border,

      BEGIN OF ty_character_style_effect,
* Font name to use for the character text fragment
* You can use the predefined font constants
* Default : document default
        font      TYPE string,

* Size of the font in pt
* Default : document default
        size      TYPE i,

* Font color to apply to the character text fragment
* You can use the predefined font color constants or specify any rgb hexa color code
* Default : document default
        color     TYPE string,

* Background color to apply to the character text fragment
* You can use the predefined font color constants or specify any rgb hexa color code
* Default : document default
        bgcolor   TYPE string,

* Highlight color to apply to the character text fragment
* You must use the predefined highlight color constants (limited color choice)
* If you want to use other color, please use bgcolor instead of highlight
* Default : document default
        highlight TYPE string,

* Set character text fragment as bold (boolean)
* You must use predefined true/false constants
* Default : not bold
        bold      TYPE i,

* Set character text fragment as italic (boolean)
* You must use predefined true/false constants
* Default : not italic
        italic    TYPE i,

* Set character text fragment as underline (boolean)
* You must use predefined true/false constants
* Default : not underline
        underline TYPE i,

* Set character text fragment as strike (boolean)
* You must use predefined true/false constants
* Default : not strike
        strike    TYPE i,

* Set character text fragment as exponent (boolean)
* You must use predefined true/false constants
* Default : not exponent
        sup       TYPE i,

* Set character text fragment as subscript (boolean)
* You must use predefined true/false constants
* Default : not subscript
        sub       TYPE i,

* Set character text fragment as upper case (boolean)
* You must use predefined true/false constants
* Default : not upper case
        caps      TYPE i,

* Set character text fragment as small upper case (boolean)
* You must use predefined true/false constants
* Default : not small upper case
        smallcaps TYPE i,

* Letter spacing to apply to the character text fragment
* 0 = normal, +20 = expand 1pt, -20 = condense 1pt
* Default : document default
        spacing   TYPE string,

* Name of the label to use for this text fragment
* Be carefull that if c_label_anchor is not found in text fragment,
* this attribute is ignored
        label     TYPE string,
      END OF ty_character_style_effect,

      BEGIN OF ty_paragraph_style_effect,
* Set alignment for the paragraph (left, right, center, justify)
* Use the predefined alignment constants
* Default : document default
        alignment           TYPE string,

* Set spacing before paragraph to "auto" (boolean)
* Use the predefined true/false constants
* Default : document default
        spacing_before_auto TYPE i, "boolean

* Set spacing before paragraph
* Integer value, 20 = 1pt
* Default : document default
        spacing_before      TYPE string,

* Set spacing after paragraph to "auto" (boolean)
* Use the predefined true/false constants
* Default : document default
        spacing_after_auto  TYPE i, "boolean

* Set spacing after paragraph
* Integer value: 20 = 1pt
* Default : document default
        spacing_after       TYPE string,

* Set interline in paragraph
* Integer value: 240 = normal interline, 120 = multiple x0.5, 480 = multiple x2
* Default : document default
        interline           TYPE i,

* Set left indentation in paragraph
* Integer value: 567 = 1cm
* Default : document default
        leftindent          TYPE string,

* Set right indentation in paragraph
* Integer value: 567 = 1cm
* Default : document default
        rightindent         TYPE string,

* Set left indentation for first line in paragraph
* Integer value: 567 = 1cm. Negative value allowed
* Default : document default
        firstindent         TYPE string,

* Add a breakpage before paragraph (boolean)
* Use the predefined true/false constants
* Default : no breakpage
        break_before        TYPE i,

* Set the hierarchical title level of the paragraph
* 1 for title1, 2 for title2...
* Default : not a hierarchical title
        hierarchy_level     TYPE i,

* Set the left border of the paragraph
* See class type ty_border for details
* Default : No border
        border_left         TYPE ty_border,

* Set the top border of the paragraph
* See class type ty_border for details
* Default : No border
        border_top          TYPE ty_border,

* Set the right border of the paragraph
* See class type ty_border for details
* Default : No border
        border_right        TYPE ty_border,

* Set the bottom border of the paragraph
* See class type ty_border for details
* Default : No border
        border_bottom       TYPE ty_border,

* Background color to apply to the paragraph
* You can use the predefined font color constants or specify any rgb hexa color code
* Default : document default
        bgcolor             TYPE string,
      END OF ty_paragraph_style_effect,

      BEGIN OF ty_list_style,
        type TYPE string,
        name TYPE string,
      END OF ty_list_style,
      ty_list_style_table TYPE STANDARD TABLE OF ty_list_style,
      BEGIN OF ty_list_object,
        id   TYPE string,
        type TYPE string,
        path TYPE string,
      END OF ty_list_object,
      ty_list_object_table TYPE STANDARD TABLE OF ty_list_object,

      BEGIN OF ty_table_style_field,
* Content of the cell
        textline          TYPE string,
* Character style name
        style             TYPE string,
* Direct character style effect
        style_effect      TYPE ty_character_style_effect,
* Paragraph style name
        line_style        TYPE string,
* Direct paragraph style effect
        line_style_effect TYPE ty_paragraph_style_effect,
* Cell background color in hexa RGB
        bgcolor           TYPE string,
* Set vertical alignment for cell
        valign            TYPE string,
* Set number of horizontal cell merged
* Start from 2 to merge the next cell with current
* Next cell will be completely ignored
* 0 or 1 to ignore this parameter
        merge             TYPE i,
* Instead of text, insert an image in the cell
* textline, style, style_effect are ignored
        image_id          TYPE string,
* For complex cell content, insert xml paragraph fragment
* You cannot insert xml fragment that are not in a (or many) paragraph
* textline, style, style_effect, line_style, line_style_effect, image_id are ignored
        xml               TYPE string,
      END OF ty_table_style_field,

      BEGIN OF ty_style_table,
        firstcol TYPE i, "boolean, first col is different
        firstrow TYPE i, "boolean, first row is different
        lastcol  TYPE i, "boolean, last col is different
        lastrow  TYPE i, "boolean, last row is different
        nozebra  TYPE i, "boolean, no line zebra
        novband  TYPE i, "boolean, no column zebra
      END OF ty_style_table.

    METHODS:
*****************************************************************************
* Method constructor
* Constructor method create the word objet and initialize some general data
* - tpl : Empty document to use as a template (docx, dotx, docm, dotm)
*         You could use template in SAPWR instead
*         Use this syntax : SAPWR:<objname>
* - keep_tpl_content:keep the template content as begin of document content
*         (boolean)
*         Default : false
* - content : Instead of create docx file from scratch or from template
*         create the docx from given docx xstring.
*         Must be a valid word document/template
*         Fot template, you have to fill "tpl" with template extension (dotx,
*         dotm...)
*****************************************************************************
      constructor
        IMPORTING
          tpl              TYPE string OPTIONAL
          keep_tpl_content TYPE i DEFAULT c_false
          content          TYPE xstring OPTIONAL,

*****************************************************************************
* Method write_text
* Write any text fragment
* You can specify which character style apply to this fragment
* You can also specify detailed style effect to apply to this fragment
* - textline  : the text fragment
* - style     : Style name for the text fragment
*               Use only "character" style here.
*               Be carrefull, you must use "internal" name of the style
*               Generaly, it's the same as external without space
*               Default : document default
* - style_effect:Detailed style effect to apply to text fragment
*               Check documentation of class type ty_character_style_effect
* - line_style: If filled, close the paragraph and apply given paragraph
*               style. Be carrefull, you must use "internal" name of the
*               style. Generaly, it's the same as external without space
* - line_style_effect:If filled, close the paragraph and apply detailed style
*               effect
*               Check documentation of class type ty_character_style_effect
* - virtual   : Get generated XML fragment instead of buffer it into document
*               If you ask this parameter, no xml is written to document
* - invalid_style:Character style name given in importing parameter does not
*               exist in document (boolean)
* - invalid_line_style:Paragraph style name given in importing parameter does
*               not exist in document (boolean)
*****************************************************************************
      write_text
        IMPORTING
          textline           TYPE string
          style              TYPE string OPTIONAL
          style_effect       TYPE ty_character_style_effect OPTIONAL
          line_style         TYPE string OPTIONAL
          line_style_effect  TYPE ty_paragraph_style_effect OPTIONAL
        EXPORTING
          virtual            TYPE string
          invalid_style      TYPE i
          invalid_line_style TYPE i,

*****************************************************************************
* Method write_line
* Close a paragraph (equivalent to use ENTER in MS Word)
* - style     : Style name for the paragraph
*               Use only "paragraph" style here.
*               you can use predefined style constants
*               Be carrefull, you must use "internal" name of the style
*               Generaly, it's the same as external without space
*               Default : document default
* - style_effect:Detailed style effect to apply to paragraph
*               Check documentation of class type ty_paragraph_style_effect
* - invalid_style:Paragraph style name given in importing parameter does not
*               exist in document (boolean)
* - virtual (input):If filled, given XML fragment is used to write a
*               paragraph instead of buffered XML fragment
* - virtual (output):Get generated XML paragraph instead of buffer it into
*               document
*               If you ask this parameter, no xml is written to document
*****************************************************************************
      write_line
        IMPORTING
          style         TYPE string OPTIONAL
          style_effect  TYPE ty_paragraph_style_effect OPTIONAL
        EXPORTING
          invalid_style TYPE i
        CHANGING
          virtual       TYPE string OPTIONAL,

*****************************************************************************
* Method write_break
* Insert a break
* - breaktype : Define the type of break to insert (line, page, section)
*               Use the predefined breaktype constants
*               Default : linebreak
* - write_line: Close current paragraph (boolean)
*               Default : false for break line, true for other breaks
* - virtual (input/output): Add a break in a given xml fragment
*               If you ask this parameter, no xml is written to document
*               Only allowed for brealine and breakpage
*****************************************************************************
      write_break
        IMPORTING
          breaktype  TYPE i DEFAULT c_breaktype_line
          write_line TYPE i OPTIONAL
        CHANGING
          virtual    TYPE string OPTIONAL,

*****************************************************************************
* Method write_symbol
* Insert a symbol
* - symbol : Define the symbol to insert
*            Use the predefined symbol constants
*****************************************************************************
      write_symbol
        IMPORTING
          symbol TYPE string,

*****************************************************************************
* Method write_table
* Write a MS Word table from an abap table
* - content   : Datatable to write
*               Structure of the table fields can be plain text or like
*               ty_table_style_field
* - style     : Style name to apply to the table
*               Use only "table" style here.
*               You can use predefined style constants
*               Be carrefull, you must use "internal" name of the style
*               Generaly, it's the same as external without space
*               Default : document default
* - style_overwrite:Allow you to redefine some style parameters
*               Structure of the parameter : ty_style_table
*               You can redefine if first/last line/column is different and
*               if there is zebra or vertical band
* - border    : Boolean. If no table style is given, display basic border
*               Default : true
* - tblwidth  : Width of the table
*               9300 correspond to full size for classic portrait page
*               Default : min required width for table content
* - virtual   : Get generated XML table instead of write it into document
*               If you ask this parameter, no xml is written to document
* - invalid_style:Table style name given in importing parameter does not
*               exist in document (boolean)
*****************************************************************************
      write_table
        IMPORTING
          content         TYPE STANDARD TABLE
          style           TYPE string OPTIONAL
          style_overwrite TYPE ty_style_table OPTIONAL
          border          TYPE i DEFAULT c_true
          tblwidth        TYPE i DEFAULT 0
        EXPORTING
          virtual         TYPE string
          invalid_style   TYPE i,

*****************************************************************************
* Method write_headerfooter
* Create a new header footer. You could define where use this new
* header/footer or use it manually with his ID
* - type     : Header or Footer
*              You must use the predefined type constants
*              Default : header
* - textline : Header/footer content
* - usenow_default:Boolean to define if created header/footer is used in
*              current section.
*              Default : true
* - usenow_first:Boolean to define if created header/footer is used in
*              current section (first page).
*              Default : true
* - style    : Character style name
*              Use only "character" style here.
*              Be carrefull, you must use "internal" name of the style
*              Generaly, it's the same as external without space
*              Default : document default
* - style_effect:Detailed style effect to apply
*              Check documentation of class type ty_character_style_effect
* - line_style:Paragraph style name
*              Use only "paragraph" style here.
*              Be carrefull, you must use "internal" name of the style
*              Generaly, it's the same as external without space
*              Default : document default
* - line_style_effect:Detailed style effect to apply
*              Check documentation of class type ty_paragraph_style_effect
* - ID       : ID of the new header/footer
* - invalid_style:Character style name given in importing parameter does not
*              exist in document (boolean)
* - invalid_line_style:Paragraph style name given in importing parameter does
*              not exist in document (boolean)
*****************************************************************************
      write_headerfooter
        IMPORTING
          type               TYPE string DEFAULT c_type_header
          textline           TYPE string
          usenow_default     TYPE i DEFAULT c_true
          usenow_first       TYPE i DEFAULT c_true
          style              TYPE string OPTIONAL
          style_effect       TYPE ty_character_style_effect OPTIONAL
          line_style         TYPE string OPTIONAL
          line_style_effect  TYPE ty_paragraph_style_effect OPTIONAL
        EXPORTING
          id                 TYPE string
          invalid_style      TYPE i
          invalid_line_style TYPE i,

*****************************************************************************
* Method set_title        #### Obsolete method, please use set_properties ###
* Define title for the document
* - title : title of the document
*****************************************************************************
      set_title "obsolete, kept for compatibility
        IMPORTING
          title TYPE string,

*****************************************************************************
* Method write_newpage       #### Obsolete method, please use write_break ###
* Insert a page break
*****************************************************************************
      write_newpage, "obsolete, kept for compatibility

*****************************************************************************
* Method write_toc
* Insert a table of content (list of titles in document) or a table of label
* (list of a given specific label in document [figure, table,...])
* Please note that it is the old word 97-2003 toc object
* - default : Default text displayed instead of TOC content
* - label   : Name of the label to display toc
*             Default : Main document TOC
*****************************************************************************
      write_toc
        IMPORTING
          default TYPE string OPTIONAL
          label   TYPE string OPTIONAL,

*****************************************************************************
* Method write_note
* Insert a note at end of the page (foot note)
* or at end of the document (end note)
* - text      : note to insert.
* - type      : foot note or end note
*               You must use the predefined note type constant
*               Default : foot note
* - style     : Style name for the note
*               Use only "character" style here.
*               Be carrefull, you must use "internal" name of the style
*               Generaly, it's the same as external without space
*               Default : document default
* - style_effect:Detailed style effect to apply to the note
*               Check documentation of class type ty_character_style_effect
* - line_style: Style name for the note
*               Use only "paragraph" style here.
*               Be carrefull, you must use "internal" name of the style
*               Generaly, it's the same as external without space
*               Default : document default
* - line_style_effect:Detailed style effect to apply to the note
*               Check documentation of class type ty_paragraph_style_effect
* - link_style: Style name for the note link in document
*               Use only "character" style here.
*               Be carrefull, you must use "internal" name of the style
*               Generaly, it's the same as external without space
*               Default : document default
* - link_style_effect:Detailed style effect to apply to the note link in document
*               Check documentation of class type ty_character_style_effect
* - invalid_style:Character style name given in importing parameter does not
*              exist in document (boolean)
* - invalid_link_style:Character style name given in importing parameter does
*              not exist in document (boolean)
* - invalid_line_style:Paragraph style name given in importing parameter does
*              not exist in document (boolean)
*****************************************************************************
      write_note
        IMPORTING
          text               TYPE string
          type               TYPE i DEFAULT c_notetype_foot
          style              TYPE string OPTIONAL
          style_effect       TYPE ty_character_style_effect OPTIONAL
          line_style         TYPE string OPTIONAL
          line_style_effect  TYPE ty_paragraph_style_effect OPTIONAL
          link_style         TYPE string OPTIONAL
          link_style_effect  TYPE ty_character_style_effect OPTIONAL
        EXPORTING
          invalid_style      TYPE i
          invalid_link_style TYPE i
          invalid_line_style TYPE i,

*****************************************************************************
* Method write_comment
* Insert a comment at the rigth side of the document
* - text      : comment to insert
* - style     : Style name for the comment
*               Use only "character" style here.
*               Be carrefull, you must use "internal" name of the style
*               Generaly, it's the same as external without space
*               Default : document default
* - style_effect:Detailed style effect to apply to the comment
*               Check documentation of class type ty_character_style_effect
* - line_style: Style name for the comment
*               Use only "paragraph" style here.
*               Be carrefull, you must use "internal" name of the style
*               Generaly, it's the same as external without space
*               Default : document default
* - line_style_effect:Detailed style effect to apply to the comment
*               Check documentation of class type ty_paragraph_style_effect
* - head_style: Style name for the comment header
*               Use only "character" style here.
*               Be carrefull, you must use "internal" name of the style
*               Generaly, it's the same as external without space
*               Default : document default
* - head_style_effect:Detailed style effect to apply to the comment header
*               Check documentation of class type ty_character_style_effect
* - datum     : Date of the comment
*               Default : Current date
* - uzeit     : Time of the comment
*               Default : Current server time
* - author    : Author name of the comment
*               Default : document author name
* - initials  : Initials of the author
*               Default : author name
* - invalid_style:Character style name given in importing parameter does not
*              exist in document (boolean)
* - invalid_head_style:Character style name given in importing parameter does
*              not exist in document (boolean)
* - invalid_line_style:Paragraph style name given in importing parameter does
*              not exist in document (boolean)
*****************************************************************************
      write_comment
        IMPORTING
          text               TYPE string
          style              TYPE string OPTIONAL
          style_effect       TYPE ty_character_style_effect OPTIONAL
          line_style         TYPE string OPTIONAL
          line_style_effect  TYPE ty_paragraph_style_effect OPTIONAL
          head_style         TYPE string OPTIONAL
          head_style_effect  TYPE ty_character_style_effect OPTIONAL
          datum              TYPE d DEFAULT sy-datum
          uzeit              TYPE t DEFAULT sy-uzeit
          author             TYPE string OPTIONAL
          initials           TYPE string OPTIONAL
        EXPORTING
          invalid_style      TYPE i
          invalid_head_style TYPE i
          invalid_line_style TYPE i,

*****************************************************************************
* Method direct_draw
* Draw an object without canvas paperboard.
* This method act like write_text : the object is drawn directly in the
* current paragraph. You have to "write_line" to finally display the object
* Positionning of object could be harder than with canvas
* - type      : Type of object to draw
*               You must use predefined class constant
* - width     : Width of the object
* - height    : Height of the object
* - posx      : Starting horizontal position of the object.
*               Default : Begin of paragraph
* - posy      : Starting vertical position of the object.
*               Default : Begin of paragraph
* - zindex    : Layer where is displayed object
*               To display an object over another, simply affect greater zindex
*               Default : 100
* - bg_color  : Color of the background
*               You can use the predefined font color constants or specify
*               any rgb hexa color code
*               Default : transparent
* - border_color:Color of the border
*               You can use the predefined font color constants or specify
*               any rgb hexa color code
*               Default : document default
* - border_width:Width of the border
*               Default : No border
* - unit      : Unit of other parameters (width, height, posx, posy, border_width)
*               Do not change it if you dont know what you do
*               Default : pt
* - text      : A text to write inside the object
* - text_style: Style for the text inside the object
* - text_line_style:Line style for the text inside the object
*               Default : Normal
* - text_xml  : Used to define an xml fragment (must be a valid paragraph)
*               to write inside the object
*****************************************************************************
      direct_draw
        IMPORTING
          type            TYPE string
          width           TYPE p
          height          TYPE p
          posx            TYPE p OPTIONAL
          posy            TYPE p OPTIONAL
          zindex          TYPE i DEFAULT 100
          bg_color        TYPE string DEFAULT ''
          border_color    TYPE string OPTIONAL
          border_width    TYPE p OPTIONAL
          unit            TYPE string DEFAULT 'pt'
          text            TYPE string OPTIONAL
          text_style      TYPE string OPTIONAL
          text_line_style TYPE string DEFAULT c_style_normal
          text_xml        TYPE string OPTIONAL,

*****************************************************************************
* Method draw_init
* Initialize draw CANVAS (the paperboard). All further drawed objects are
* included in this canvas. No one can overflow his size.
* This is working only from Word 2010, but will display a text message
* instead of the canvas in Word 2007.
* - left    : Left position to start the canvas (in pt)
* - top     : Top position to start the canvas (in pt)
* - width   : Length of the canvas
* - height  : Height of the canvas
* - bgcolor : Background color of the canvas
*             You can use the predefined font color constants
*             or specify any rgb hexa color code
*             Default : transparent
* - bdcolor : Border color of the canvas
*             You can use the predefined font color constants
*             or specify any rgb hexa color code
*             You must specify both bdcolor & bdwidth to have effect applied
*             Default : transparent
* - bdwidth : Border width of the canvas in pt
*             You must specify both bdcolor & bdwidth to have effect applied
*             Default : none
*****************************************************************************
      draw_init
        IMPORTING
          left    TYPE i
          top     TYPE i
          width   TYPE i
          height  TYPE i
          bgcolor TYPE string OPTIONAL
          bdcolor TYPE string OPTIONAL
          bdwidth TYPE f OPTIONAL,

*****************************************************************************
* Method draw
* Draw an object in a canvas (you must create it before with draw_init)
* - object  : Type of object to draw
* - left    : Left position to start the canvas (in pt)
* - top     : Top position to start the canvas (in pt)
* - width   : Length of the canvas
* - height  : Height of the canvas
* - url     : In case of image, url of the picture to load
*             You could use image in SAPWR instead
*             Use this syntax : SAPWR:<objname>
* - bgcolor : Background color of the object
*             You can use the predefined font color constants
*             or specify any rgb hexa color code
*             Default : transparent for image, white for other objects
* - bdcolor : Border color of the object
*             You can use the predefined font color constants
*             or specify any rgb hexa color code
*             You must specify both bdcolor & bdwidth to have effect applied
*             Default : transparent for image, black for other objects
* - bdwidth : Border width of the object in pt
*             You must specify both bdcolor & bdwidth to have effect applied
*             Default : none for image, 1px for other objects
* - invalid_image:Image to insert does not exist (boolean)
* - ID (input): You could give ID of an existing image in document instead of
*             url
* - ID (output): ID of the image in document
*****************************************************************************
      draw
        IMPORTING
          object        TYPE i
          left          TYPE i DEFAULT 0
          top           TYPE i DEFAULT 0
          width         TYPE i DEFAULT 0
          height        TYPE i DEFAULT 0
          url           TYPE string OPTIONAL
          bgcolor       TYPE string OPTIONAL
          bdcolor       TYPE string OPTIONAL
          bdwidth       TYPE f OPTIONAL
        EXPORTING
          invalid_image TYPE i
        CHANGING
          id            TYPE string OPTIONAL,

*****************************************************************************
* Method draw_finalize
* Write all the canvas object in document
* Call this method once you have finished your draw all yours objects.
*****************************************************************************
      draw_finalize,

*****************************************************************************
* Method insert_custom_field
* Insert a custom field link in the document.
* It is not required that the field exist
* - field : Name of the custom field to insert
*****************************************************************************
      insert_custom_field
        IMPORTING
          field TYPE string,

*****************************************************************************
* Method insert_virtual_field
* Insert a temporary anchor in the document
* The objective of this anchor is to write something here later
* It is usefull when you dont have the content when you write this part,
* but you will have before end of the document
* Use replace_virtual_field to replace the anchor by a viewable content
* - field : Name of the anchor to insert
* - field_as_paragraph:Virtual field is considered as a complete paragraph
*           Usefull if you want to replace virtual field by a table
*****************************************************************************
      insert_virtual_field
        IMPORTING
          field              TYPE string
          field_as_paragraph TYPE i DEFAULT c_false,

*****************************************************************************
* Method replace_virtual_field
* replace an anchor created with insert_virtual_field
* - field : Name of the anchor to replace
* - value : text content to insert
* - value_as_xml:value is not text but xml fragment to insert directly
*           If filled, style & style_effet are ignored
* - style : Character style name
*           Use only "character" style here.
*           Be carrefull, you must use "internal" name of the style
*           Generaly, it's the same as external without space
*           Default : document default
* - style_effect:Detailed style effect to apply to text fragment
*           Check documentation of class type ty_character_style_effect
* - invalid_style:Character style name given in importing parameter does not
*           exist in document (boolean)
*****************************************************************************
      replace_virtual_field
        IMPORTING
          field         TYPE string
          value         TYPE string
          value_as_xml  TYPE i DEFAULT c_false
          style_effect  TYPE ty_character_style_effect OPTIONAL
          style         TYPE string OPTIONAL
        EXPORTING
          invalid_style TYPE i,

*****************************************************************************
* Method create_custom_field
* Create a custom field in the document properties and assign a value
* - field : Name of the custom field to create
* - value : text content of the field
*****************************************************************************
      create_custom_field
        IMPORTING
          field TYPE string
          value TYPE string,

*****************************************************************************
* Method create_character_style
* Create a character style in document for further usage
* - output_name : Name of character style displayed in word
* - style_effect: Detailed style effect
*                 Check documentation of class type ty_character_style_effect
* - style_ref   : Define an existing character style as reference
* - name        : Internal name of the created style.
*                 You have to use this name to apply style from now
*                 (output name is only usable in word)
* - invalid_style:Character style name given as reference in importing
*                 parameter does not exist in document (boolean)
*****************************************************************************
      create_character_style
        IMPORTING
          output_name   TYPE string
          style_effect  TYPE ty_character_style_effect
          style_ref     TYPE string OPTIONAL
        EXPORTING
          name          TYPE string
          invalid_style TYPE i,

*****************************************************************************
* Method create_paragraph_style
* Create a paragraph style in document for further usage
* - output_name : Name of paragraph style displayed in word
* - style_effect: Detailed character style effect
*                 Check documentation of class type ty_character_style_effect
* - line_style_effect: Detailed paragraph style effect
*                 Check documentation of class type ty_paragraph_style_effect
* - style_ref   : Define an existing paragraph style as reference
* - name        : Internal name of the created style.
*                 You have to use this name to apply style from now
*                 (output name is only usable in word)
* - invalid_style:Paragraph style name given as reference in importing
*                 parameter does not exist in document (boolean)
*****************************************************************************
      create_paragraph_style
        IMPORTING
          output_name       TYPE string
          style_effect      TYPE ty_character_style_effect
          line_style_effect TYPE ty_paragraph_style_effect
          style_ref         TYPE string OPTIONAL
        EXPORTING
          name              TYPE string
          invalid_style     TYPE i,

*****************************************************************************
* Method insert_image
* Insert an image as paragraph
* - url         : path+name of the image to insert
*                 You could use image in SAPWR instead
*                 Optional only if ID supplied
*                 Use this syntax : SAPWR:<objname>
*                 If file content is provided, used only to know the file
*                 name
* - file_content: File content to insert
* - Zoom        : Zoom value to apply to the image
*                 Example : 2 to extend image to 200%, 0.5 to reduce image to 50%
*                 Default : 1
* - as_paragraph: Write the image alone in a paragraph or with other text
*                 style & style_effect parameters are ignored if this parameter
*                 is not true
*                 Default : true (Alone in a paragraph)
* - Style       : Paragraph style to apply to the image
*                 Use only "paragraph" style here.
*                 Be carrefull, you must use "internal" name of the style
*                 Generaly, it's the same as external without space
*                 Default : document default
* - style_effect: Detailed style effect to apply
*                 Check documentation of class type ty_paragraph_style_effect
* - type        : Choose type of display
*                 You must use predefined constant
*                 Default : inline
* - posx        : For float, over and behind image type, you have to specify
*                 the horizontal image position
*                 You can use predefined class contant or direct input a
*                 numerical value
*                 Default : left
* - posy        : For float, over and behind image type, you have to specify
*                 the vertical image position
*                 You can use predefined class contant or direct input a
*                 numerical value
*                 Default : top
* - margin      : For float image type, you can specify a distance between
*                 image and text
*                 Default : 0
* - invalid_image:Image to insert does not exist (boolean)
* - invalid_style:Paragraph style name given in importing parameter does
*                 not exist in document (boolean)
* - virtual     : Get generated XML fragment instead of buffer it into document
*                 If you ask this parameter, no xml is written to document
* - ID (input)  : You could give ID of an existing image in document instead of
*                 url
* - ID (output) : ID of the image in document
*****************************************************************************
      insert_image
        IMPORTING
          url           TYPE string OPTIONAL
          file_content  TYPE xstring OPTIONAL
          zoom          TYPE f OPTIONAL
          as_paragraph  TYPE i DEFAULT c_true
          style         TYPE string OPTIONAL
          style_effect  TYPE ty_paragraph_style_effect OPTIONAL
          type          TYPE i DEFAULT c_imgtype_inline
          posx          TYPE string DEFAULT c_imgposx_left
          posy          TYPE string DEFAULT c_imgposy_top
          margin        TYPE i OPTIONAL
        EXPORTING
          invalid_image TYPE i
          invalid_style TYPE i
          virtual       TYPE string
        CHANGING
          id            TYPE string OPTIONAL,

*****************************************************************************
* Method insert_attachment
* Insert an attached file in the currend document and display it as an icon
* The file could be any type, icon have to be given
* - url         : path+name of the file to insert
*                 You could use file in SAPWR instead
*                 Use this syntax : SAPWR:<objname>
*                 If file content is provided, used only to know the file
*                 name
* - file_content: File content to insert
* - url_img     : path+name of the icon of the file to insert
*                 You could use file in SAPWR instead
*                 Use this syntax : SAPWR:<objname>
*                 Optional only if ID_IMG supplied
* - as_paragraph: Write the attachment alone in a paragraph or with other text
*                 style & style_effect parameters are ignored if this parameter
*                 is not true
*                 Default : true (Alone in a paragraph)
* - Style       : Paragraph style to apply to the attachment image
*                 Use only "paragraph" style here.
*                 Be carrefull, you must use "internal" name of the style
*                 Generaly, it's the same as external without space
*                 Default : document default
* - style_effect: Detailed style effect to apply
*                 Check documentation of class type ty_paragraph_style_effect
* - invalid_image:Icon to insert does not exist (boolean)
* - invalid_file: File to insert does not exist (boolean)
* - invalid_style:Paragraph style name given in importing parameter does
*                 not exist in document (boolean)
* - virtual     : Get generated XML fragment instead of buffer it into document
*                 If you ask this parameter, no xml is written to document
* - id_img(input):You could give ID of an existing image in document
*                 instead of url_img
* - id_img(output):ID of the image in document
*****************************************************************************
      insert_attachment
        IMPORTING
          url           TYPE string
          file_content  TYPE xstring OPTIONAL
          url_img       TYPE string OPTIONAL
          as_paragraph  TYPE i DEFAULT c_true
          style         TYPE string OPTIONAL
          style_effect  TYPE ty_paragraph_style_effect OPTIONAL
        EXPORTING
          invalid_image TYPE i
          invalid_file  TYPE i
          invalid_style TYPE i
          virtual       TYPE string
        CHANGING
          id_img        TYPE string OPTIONAL,

*****************************************************************************
* Method set_properties
* Define document properties
* - title       : Title of the document
* - author      : Creator of the document
*                 Default : SAP user name (or SAP ID if no name found)
* - description : Description of the document
* - object      : Object of the document
* - category    : Category of the document
* - keywords    : Keywords of the document
* - status      : Status of the document
* - creationdate: Creation date of the document
*                 Default : date of generation
* - creationtime: Creation time of the document
*                 Default : time of generation
* - revision    : Internal revision number
*                 Default : 1
*****************************************************************************
      set_properties
        IMPORTING
          title        TYPE string OPTIONAL
          author       TYPE string OPTIONAL
          description  TYPE string OPTIONAL
          object       TYPE string OPTIONAL
          category     TYPE string OPTIONAL
          keywords     TYPE string OPTIONAL
          status       TYPE string OPTIONAL
          creationdate TYPE d OPTIONAL
          creationtime TYPE t OPTIONAL
          revision     TYPE i OPTIONAL,

*****************************************************************************
* Method set_params
* Define options
* - orientation : set page orientation
*                 you must use predefined orientation constants
*                 Default : portrait
* - border_left : Set the left border of the section
*                 See class type ty_border for details
*                 Default : No border
* - border_top : Set the top border of the section
*                 See class type ty_border for details
*                 Default : No border
* - border_right : Set the right border of the section
*                 See class type ty_border for details
*                 Default : No border
* - border_bottom : Set the bottom border of the section
*                 See class type ty_border for details
*                 Default : No border
*****************************************************************************
      set_params
        IMPORTING
          orientation   TYPE i DEFAULT c_orient_portrait
          border_left   TYPE ty_border OPTIONAL
          border_top    TYPE ty_border OPTIONAL
          border_right  TYPE ty_border OPTIONAL
          border_bottom TYPE ty_border OPTIONAL
          nospellcheck  TYPE i DEFAULT c_false,

*****************************************************************************
* Method save
* Save created document
* - url   : path+name of the saved document
* - local : url is a local path (boolean)
*           Default : true
*****************************************************************************
      save
        IMPORTING
          url   TYPE string
          local TYPE i DEFAULT c_true,

*****************************************************************************
* Method get_docx_file
* Low level method (for advanced user)
* Get the content of the docx
* - xcontent : content of the docx
*****************************************************************************
      get_docx_file
        EXPORTING
          xcontent TYPE xstring,

*****************************************************************************
* Method header_footer_direct_assign
* Low level method (for advanced user)
* Use existing header/footer directly
* You can get header/footer id with method get_list_headerfooter or when
* creating a new header/footer with method write_headerfooter
* - header       : ID of the header to use
* - header_first : ID of the header to use for the first page of the section
* - footer       : ID of the footer to use
* - footer_first : ID of the footer to use for the first page of the section
* - invalid_header:ID of the header given is invalid (boolean)
* - invalid_footer:ID of the footer given is invalid (boolean)
*****************************************************************************
      header_footer_direct_assign
        IMPORTING
          header         TYPE string OPTIONAL
          header_first   TYPE string OPTIONAL
          footer         TYPE string OPTIONAL
          footer_first   TYPE string OPTIONAL
        EXPORTING
          invalid_header TYPE i
          invalid_footer TYPE i,

*****************************************************************************
* Method get_list_style
* Low level method (for advanced user)
* Get list of existing styles
* - style_list : List of styles in current document
*****************************************************************************
      get_list_style
        EXPORTING
          style_list TYPE ty_list_style_table,

*****************************************************************************
* Method get_list_image
* Low level method (for advanced user)
* Get list of existing images
* - image_list : List of images in current document
*****************************************************************************
      get_list_image
        EXPORTING
          image_list TYPE ty_list_object_table,

*****************************************************************************
* Method get_list_headerfooter
* Low level method (for advanced user)
* Get list of existing header / footer
* - headerfooter_list : List of header / footer in current document
*****************************************************************************
      get_list_headerfooter
        EXPORTING
          headerfooter_list TYPE ty_list_object_table,

*****************************************************************************
* Method insert_xml_fragment
* Low level method (for advanced user)
* If some function is missing, you could inject direct xml fragment in the
* current text line with this method.
* Dont forget you will need to use write_line to write this fragment in
* document
* - xml : XML fragment to insert
*****************************************************************************
      insert_xml_fragment
        IMPORTING
          xml TYPE string,

*****************************************************************************
* Method insert_xml
* Low level method (for advanced user)
* If some function is missing, you could inject direct xml in the document
* with this method.
* Current xml fragment stay untouched and could continue to be builded
* - xml : XML code to insert in document
*****************************************************************************
      insert_xml
        IMPORTING
          xml TYPE string.

  PRIVATE SECTION.
    DATA : mw_docxml   TYPE string,
           mw_fragxml  TYPE string,
           mw_imgmaxid TYPE i VALUE 100,
           mw_attach   TYPE i VALUE 10000,
           mo_zip      TYPE REF TO cl_abap_zip,
           BEGIN OF ms_section,
             landscape     TYPE i,
             continuous    TYPE i,
             header_first  TYPE string,
             header        TYPE string,
             footer_first  TYPE string,
             footer        TYPE string,
             border_left   TYPE ty_border,
             border_top    TYPE ty_border,
             border_right  TYPE ty_border,
             border_bottom TYPE ty_border,
           END OF ms_section,
           mw_section_xml     TYPE string,
           mw_tpl_section_xml TYPE string,
           mt_list_style      TYPE ty_list_style_table,
           mt_list_object     TYPE ty_list_object_table,
           mw_author          TYPE string.
    CONSTANTS : c_basesize TYPE i VALUE 12700.
    METHODS :
* Prepare the section xml part
      _write_section,

* Read a zip file and return string content
      _get_zip_file
        IMPORTING
          filename TYPE string
        EXPORTING
          content  TYPE string,

* Replace zip file with string content
      _update_zip_file
        IMPORTING
          filename TYPE string
          content  TYPE string,

* Load a file and return xstring content
      _load_file
        IMPORTING
          filename TYPE string
        EXPORTING
          xcontent TYPE xstring,

* Load an image into docx structure. Give ID, image res and extension
      _load_image
        IMPORTING
          url          TYPE string
          file_content TYPE xstring OPTIONAL
        EXPORTING
          imgres_x     TYPE i
          imgres_y     TYPE i
          extension    TYPE string
        CHANGING
          id           TYPE string,

* Create a foot/end note
      _create_note
        IMPORTING
          text               TYPE string
          type               TYPE i
          style              TYPE string OPTIONAL
          style_effect       TYPE ty_character_style_effect OPTIONAL
          line_style         TYPE string OPTIONAL
          line_style_effect  TYPE ty_paragraph_style_effect OPTIONAL
          link_style         TYPE string OPTIONAL
          link_style_effect  TYPE ty_character_style_effect OPTIONAL
        EXPORTING
          invalid_style      TYPE i
          invalid_line_style TYPE i
          id                 TYPE string,

* Replace xml reserved character by escaped ones
      _protect_string
        IMPORTING
          in  TYPE string
        EXPORTING
          out TYPE string,

* Replace invalid label character by allowed ones
      _protect_label
        IMPORTING
          in  TYPE string
        EXPORTING
          out TYPE string,

* Write character style xml fragment
      _build_character_style
        IMPORTING
          style         TYPE string OPTIONAL
          style_effect  TYPE ty_character_style_effect OPTIONAL
        EXPORTING
          xml           TYPE string
          invalid_style TYPE i,

* Write paragraph style xml fragment
      _build_paragraph_style
        IMPORTING
          style         TYPE string OPTIONAL
          style_effect  TYPE ty_paragraph_style_effect OPTIONAL
        EXPORTING
          xml           TYPE string
          invalid_style TYPE i,

      _get_xml_ns
        EXPORTING
          xml TYPE string.

ENDCLASS.                    "cl_word DEFINITION

*----------------------------------------------------------------------*
*       CLASS cl_word IMPLEMENTATION
*----------------------------------------------------------------------*
CLASS cl_word IMPLEMENTATION.
  METHOD constructor.
    DATA : lw_docx         TYPE xstring,
           lw_extension    TYPE string,
           lw_file         TYPE string,
           lw_string       TYPE string,
           ls_list_style   TYPE ty_list_style,
           ls_list_object  TYPE ty_list_object,
           lt_find_result  TYPE match_result_tab,
           ls_find_result  LIKE LINE OF lt_find_result,
           lw_url_begin(6) TYPE c.

    CREATE OBJECT mo_zip.

    IF content IS SUPPLIED AND NOT content IS INITIAL.
      lw_docx = content.
    ELSEIF tpl IS SUPPLIED AND NOT tpl IS INITIAL.
* Load template document
      CALL METHOD _load_file
        EXPORTING
          filename = tpl
        IMPORTING
          xcontent = lw_docx.
      IF lw_docx IS INITIAL.
        MESSAGE 'Cannot open template, please check' TYPE 'A'.
      ENDIF.
    ELSE.
* Empty docx creation
      TRY.
          lw_docx = cl_docx_form=>create_form(  ).
        CATCH cx_openxml_not_allowed
              cx_openxml_not_found
              cx_openxml_format
              cx_docx_form_not_unicode.
          MESSAGE 'Cannot create empty doc, please use template' TYPE 'A'.
      ENDTRY.
    ENDIF.

* Load docx into zip object
    CALL METHOD mo_zip->load
      EXPORTING
        zip             = lw_docx
      EXCEPTIONS
        zip_parse_error = 1
        OTHERS          = 2.

* Keep actual content
    IF keep_tpl_content = c_true.
      CALL METHOD _get_zip_file
        EXPORTING
          filename = 'word/document.xml'                    "#EC NOTEXT
        IMPORTING
          content  = lw_file.

      FIND FIRST OCCURRENCE OF '<w:body' IN lw_file
           MATCH OFFSET sy-fdpos IGNORING CASE.
      IF sy-subrc = 0.
        lw_file = lw_file+sy-fdpos.
        FIND FIRST OCCURRENCE OF '>' IN lw_file
             MATCH OFFSET sy-fdpos IGNORING CASE.
      ENDIF.
      IF sy-subrc = 0.
        sy-fdpos = sy-fdpos + 1.
        lw_file = lw_file+sy-fdpos.
        FIND FIRST OCCURRENCE OF '</w:body' IN lw_file
             MATCH OFFSET sy-fdpos IGNORING CASE.
      ENDIF.
      IF sy-subrc = 0.
        mw_docxml = lw_file(sy-fdpos).
        FIND ALL OCCURRENCES OF '<w:sectPr' IN mw_docxml
             MATCH OFFSET sy-fdpos IGNORING CASE.
        IF sy-subrc = 0.
          mw_tpl_section_xml = mw_docxml+sy-fdpos.
          mw_docxml = mw_docxml(sy-fdpos).
        ENDIF.
      ELSE.
        MESSAGE 'Cannot parse template content: empty document created'
                TYPE 'I'.
      ENDIF.
    ENDIF.

* Remove docx body
    CALL METHOD mo_zip->delete
      EXPORTING
        name            = 'word/document.xml'               "#EC NOTEXT
      EXCEPTIONS
        zip_index_error = 1
        OTHERS          = 2.

* If modele is a template, transform it to document
    IF tpl IS SUPPLIED AND NOT tpl IS INITIAL.
      lw_url_begin = tpl.
      IF lw_url_begin = c_sapwr_prefix.
        SELECT SINGLE value INTO lw_extension
               FROM wwwparams
               WHERE relid = 'MI'
               AND objid = tpl+6
               AND name = 'fileextension'.
        IF NOT lw_extension IS INITIAL AND lw_extension(1) = '.'.
          lw_extension = lw_extension+1.
        ENDIF.
      ELSE.
        FIND ALL OCCURRENCES OF '.' IN tpl MATCH OFFSET sy-fdpos.
        IF sy-subrc = 0.
          sy-fdpos = sy-fdpos + 1.
          lw_extension = tpl+sy-fdpos.
        ELSE.
          lw_extension = tpl.
        ENDIF.
      ENDIF.
      TRANSLATE lw_extension TO LOWER CASE.

* Template without macro
      IF lw_extension = 'dotx'.
        CALL METHOD _get_zip_file
          EXPORTING
            filename = '[Content_Types].xml'                "#EC NOTEXT
          IMPORTING
            content  = lw_file.

        REPLACE ALL OCCURRENCES OF 'wordprocessingml.template'
                IN lw_file WITH 'wordprocessingml.document'. "#EC NOTEXT

        CALL METHOD _update_zip_file
          EXPORTING
            filename = '[Content_Types].xml'
            content  = lw_file.
* Template with macro
      ELSEIF lw_extension = 'dotm'.
        CALL METHOD _get_zip_file
          EXPORTING
            filename = '[Content_Types].xml'                "#EC NOTEXT
          IMPORTING
            content  = lw_file.

        REPLACE ALL OCCURRENCES OF 'template.macroEnabledTemplate'
                IN lw_file WITH 'document.macroEnabled'.    "#EC NOTEXT

        CALL METHOD _update_zip_file
          EXPORTING
            filename = '[Content_Types].xml'
            content  = lw_file.
      ENDIF.
    ENDIF.

* Get author name
    CLEAR mw_author.
    SELECT SINGLE name_textc INTO mw_author
           FROM user_addr
           WHERE bname = sy-uname.                          "#EC WARNOK
    IF sy-subrc NE 0.
      mw_author = sy-uname.
    ENDIF.

* Set Author, Creation date and Version number properties
    CALL METHOD set_properties
      EXPORTING
        author       = mw_author
        creationdate = sy-datlo
        creationtime = sy-timlo
        revision     = 1.

* Get style file
    CALL METHOD _get_zip_file
      EXPORTING
        filename = 'word/styles.xml'                        "#EC NOTEXT
      IMPORTING
        content  = lw_file.

* Scan style file to search all styles
    FIND ALL OCCURRENCES OF REGEX '<w:style ([^>]*)>'
         IN lw_file RESULTS lt_find_result
         IGNORING CASE.
    LOOP AT lt_find_result INTO ls_find_result.
      CLEAR ls_list_style.
      FIND FIRST OCCURRENCE OF REGEX 'w:styleId="([^"]*)"'
           IN SECTION OFFSET ls_find_result-offset
                      LENGTH ls_find_result-length
                      OF lw_file
           SUBMATCHES lw_string
           IGNORING CASE.
      IF sy-subrc NE 0.
        CONTINUE.
      ENDIF.
      ls_list_style-name = lw_string.
      FIND FIRST OCCURRENCE OF REGEX 'w:type="(paragraph|character|numbering|table)"'
           IN SECTION OFFSET ls_find_result-offset
                      LENGTH ls_find_result-length
                      OF lw_file
           SUBMATCHES lw_string
           IGNORING CASE.
      IF sy-subrc = 0.
        ls_list_style-type = lw_string.
      ENDIF.
      APPEND ls_list_style TO mt_list_style.
    ENDLOOP.
    SORT mt_list_style BY type name.

* Get relation file
    CALL METHOD _get_zip_file
      EXPORTING
        filename = 'word/_rels/document.xml.rels'           "#EC NOTEXT
      IMPORTING
        content  = lw_file.

* Scan relation file to get all objects
    FIND ALL OCCURRENCES OF REGEX '<Relationship ([^>]*)/>'
         IN lw_file RESULTS lt_find_result
         IGNORING CASE.
    LOOP AT lt_find_result INTO ls_find_result.
      CLEAR ls_list_object.
* Search id of object
      FIND FIRST OCCURRENCE OF REGEX 'Id="([^"]*)"'
           IN SECTION OFFSET ls_find_result-offset
                      LENGTH ls_find_result-length
                      OF lw_file
           SUBMATCHES lw_string
           IGNORING CASE.
      IF sy-subrc NE 0.
        CONTINUE.
      ENDIF.
      ls_list_object-id = lw_string.
* Search type of object
      FIND FIRST OCCURRENCE OF REGEX 'Type=".*(footer|header|image)"'
           IN SECTION OFFSET ls_find_result-offset
                      LENGTH ls_find_result-length
                      OF lw_file
           SUBMATCHES lw_string
           IGNORING CASE.
      IF sy-subrc NE 0.
        CONTINUE.
      ENDIF.
      ls_list_object-type = lw_string.

* Search path of file
      FIND FIRST OCCURRENCE OF REGEX 'Target="([^"]*)"'
           IN SECTION OFFSET ls_find_result-offset
                      LENGTH ls_find_result-length
                      OF lw_file
           SUBMATCHES lw_string
           IGNORING CASE.
      IF sy-subrc NE 0.
        CONTINUE.
      ENDIF.
      CONCATENATE 'word/' lw_string INTO ls_list_object-path.

      APPEND ls_list_object TO mt_list_object.
    ENDLOOP.
    SORT mt_list_object BY type id.
  ENDMETHOD.                    "constructor

  METHOD write_text.
    DATA : lw_style   TYPE string,
           lw_string  TYPE string,
           lw_field   TYPE string.
    DATA : lt_find_result TYPE match_result_tab,
           ls_find_result LIKE LINE OF lt_find_result,
           lw_off         TYPE i,
           lw_len         TYPE i.

* Get font style section
    IF style_effect IS SUPPLIED OR NOT style IS INITIAL.
      CALL METHOD _build_character_style
        EXPORTING
          style         = style
          style_effect  = style_effect
        IMPORTING
          xml           = lw_style
          invalid_style = invalid_style.
    ENDIF.

* Escape invalid character
    CALL METHOD _protect_string
      EXPORTING
        in  = textline
      IMPORTING
        out = lw_string.

* Replace fields in content
    IF lw_string CS '##FIELD#'.
* Regex to search all fields to replace
      FIND ALL OCCURRENCES OF REGEX '##FIELD#([A-Z ])*##' IN lw_string RESULTS lt_find_result.
      SORT lt_find_result BY offset DESCENDING.
* For each result, replace
      LOOP AT lt_find_result INTO ls_find_result.
        lw_off = ls_find_result-offset + 8.
        lw_len = ls_find_result-length - 10.
        CASE lw_string+ls_find_result-offset(ls_find_result-length).
          WHEN c_field_pagecount OR c_field_pagetotal.
            CONCATENATE lw_string+lw_off(lw_len) '\* Arabic'
                        INTO lw_field SEPARATED BY space.
          WHEN c_field_filename.
            CONCATENATE lw_string+lw_off(lw_len) '\p'
                        INTO lw_field SEPARATED BY space.
          WHEN c_field_creationdate OR c_field_moddate
          OR c_field_todaydate.
            CONCATENATE lw_string+lw_off(lw_len)
                        '\@ &quot;dd/MM/yyyy&quot;'
                        INTO lw_field SEPARATED BY space.
          WHEN OTHERS.
            lw_field = lw_string+lw_off(lw_len).
        ENDCASE.
        CONCATENATE '<w:fldSimple w:instr="'
                    lw_field
                    ' \* MERGEFORMAT"/>'
                    INTO lw_field RESPECTING BLANKS.
        REPLACE lw_string+ls_find_result-offset(ls_find_result-length)
                IN lw_string WITH lw_field.
      ENDLOOP.
    ENDIF.

* Replace label anchor by it's value
    IF NOT style_effect-label IS INITIAL AND lw_string CS c_label_anchor.
      CALL METHOD _protect_label
        EXPORTING
          in  = style_effect-label
        IMPORTING
          out = lw_field.
      CONCATENATE '<w:fldSimple w:instr=" SEQ '
                  lw_field
                  ' \* ARABIC "/>'
                  INTO lw_field RESPECTING BLANKS.
      REPLACE c_label_anchor IN lw_string WITH lw_field.
    ENDIF.

    IF virtual IS SUPPLIED.
      CONCATENATE '<w:r>'
                  lw_style
                  '<w:t xml:space="preserve">'
                  lw_string
                  '</w:t>'
                  '</w:r>'
                  INTO virtual RESPECTING BLANKS.

      IF ( line_style IS SUPPLIED AND line_style IS NOT INITIAL )
      OR ( line_style_effect IS SUPPLIED
           AND line_style_effect IS NOT INITIAL ).
        CALL METHOD write_line
          EXPORTING
            style         = line_style
            style_effect  = line_style_effect
          IMPORTING
            invalid_style = invalid_line_style
          CHANGING
            virtual       = virtual.
      ENDIF.


      RETURN.
    ELSE.
      CONCATENATE mw_fragxml
                  '<w:r>'
                  lw_style
                  '<w:t xml:space="preserve">'
                  lw_string
                  '</w:t>'
                  '</w:r>'
                  INTO mw_fragxml RESPECTING BLANKS.

      IF ( line_style IS SUPPLIED AND line_style IS NOT INITIAL )
      OR ( line_style_effect IS SUPPLIED
           AND line_style_effect IS NOT INITIAL ).
        CALL METHOD write_line
          EXPORTING
            style         = line_style
            style_effect  = line_style_effect
          IMPORTING
            invalid_style = invalid_line_style.
      ENDIF.
    ENDIF.
  ENDMETHOD.                    "write_text

  METHOD write_line.
    DATA : lw_style    TYPE string.

    CLEAR lw_style.

    CALL METHOD _build_paragraph_style
      EXPORTING
        style         = style
        style_effect  = style_effect
      IMPORTING
        xml           = lw_style
        invalid_style = invalid_style.

    IF virtual IS SUPPLIED.
      IF virtual IS INITIAL.
        CONCATENATE mw_docxml
                    '<w:p>'
                    lw_style
                    mw_fragxml
                    '</w:p>'
                    INTO virtual.
        CLEAR mw_fragxml.
      ELSE.
        CONCATENATE '<w:p>'
                    lw_style
                    virtual
                    '</w:p>'
                    INTO virtual.
      ENDIF.
    ELSE.
      CONCATENATE mw_docxml
                  '<w:p>'
                  lw_style
                  mw_fragxml
                  '</w:p>'
                  INTO mw_docxml.
      CLEAR mw_fragxml.
    ENDIF.
  ENDMETHOD.                    "write_line

  METHOD write_table.
    DATA : ls_content     TYPE REF TO data,
           lw_type(1)     TYPE c,                           "#EC NEEDED
           lw_lines       TYPE i,
           lw_cols        TYPE i,
           lw_col         TYPE i,
           lw_col_inc     TYPE i,
           lw_style_table TYPE i,
           lw_xml         TYPE string,
           lw_tblwidth    TYPE string,
           lw_merge       TYPE string,
           lw_string      TYPE string,
           lw_style       TYPE string,
           lw_stylep      TYPE string.
    FIELD-SYMBOLS <field> TYPE any.
    FIELD-SYMBOLS <field_style> TYPE ty_table_style_field.
    FIELD-SYMBOLS <line> TYPE any.
    CREATE DATA ls_content LIKE LINE OF content.
    ASSIGN ls_content->* TO <line>.
    IF sy-subrc NE 0.
      RETURN.
    ENDIF.

* count number of lines and columns of the table
    DESCRIBE TABLE content LINES lw_lines.
    IF lw_lines = 0.
      RETURN.
    ENDIF.
    DESCRIBE FIELD <line> TYPE lw_type COMPONENTS lw_cols.
    IF lw_cols = 0.
      RETURN.
    ENDIF.

* search if data table is simple or have style infos for each field
    ASSIGN COMPONENT 1 OF STRUCTURE <line> TO <field>.
    IF sy-subrc NE 0.
      RETURN.
    ENDIF.
    DESCRIBE FIELD <field> TYPE lw_type COMPONENTS lw_style_table.

* Write table properties
    CLEAR lw_xml.
    CONCATENATE '<w:tbl>'
                '<w:tblPr>'
                INTO lw_xml.

    " Styled table : define style
    IF style IS SUPPLIED AND NOT style IS INITIAL.
      READ TABLE mt_list_style WITH KEY type = c_type_table
                                        name = style
                               TRANSPORTING NO FIELDS.
      IF sy-subrc = 0.
        CONCATENATE lw_xml
                    '<w:tblStyle w:val="'
                    style
                    '"/>'
                    INTO lw_xml.
      ELSE.
        invalid_style = c_true.
      ENDIF.

* If defined, overwrite style table layout (no effect without table style defined)
      CLEAR lw_style.
      IF style_overwrite IS SUPPLIED.
        lw_tblwidth = style_overwrite-firstrow.
        CONDENSE lw_tblwidth NO-GAPS.
        CONCATENATE lw_style
                    ' w:firstRow="'
                    lw_tblwidth
                    '"'
                    INTO lw_style RESPECTING BLANKS.
        lw_tblwidth = style_overwrite-firstcol.
        CONDENSE lw_tblwidth NO-GAPS.
        CONCATENATE lw_style
                    ' w:firstColumn="'
                    lw_tblwidth
                    '"'
                    INTO lw_style RESPECTING BLANKS.
        lw_tblwidth = style_overwrite-nozebra.
        CONDENSE lw_tblwidth NO-GAPS.
        CONCATENATE lw_style
                    ' w:noHBand="'
                    lw_tblwidth
                    '"'
                    INTO lw_style RESPECTING BLANKS.
        lw_tblwidth = style_overwrite-novband.
        CONDENSE lw_tblwidth NO-GAPS.
        CONCATENATE lw_style
                    ' w:noVBand="'
                    lw_tblwidth
                    '"'
                    INTO lw_style RESPECTING BLANKS.
        lw_tblwidth = style_overwrite-lastrow.
        CONDENSE lw_tblwidth NO-GAPS.
        CONCATENATE lw_style
                    ' w:lastRow="'
                    lw_tblwidth
                    '"'
                    INTO lw_style RESPECTING BLANKS.
        lw_tblwidth = style_overwrite-lastcol.
        CONDENSE lw_tblwidth NO-GAPS.
        CONCATENATE lw_style
                    ' w:lastColumn="'
                    lw_tblwidth
                    '"'
                    INTO lw_style RESPECTING BLANKS.

        CONCATENATE lw_xml
                    '<w:tblLook'
                    lw_style
                    '/>'
                    INTO lw_xml RESPECTING BLANKS.
        CLEAR lw_style.
      ENDIF.
    ENDIF.

* Default not styled table : add border
    IF NOT style IS SUPPLIED.
      IF border = c_true.
        CONCATENATE lw_xml
                    '<w:tblBorders>'
                    '<w:top w:color="auto" w:space="0" w:sz="4" w:val="single"/>'
                    '<w:left w:color="auto" w:space="0" w:sz="4" w:val="single"/>'
                    '<w:bottom w:color="auto" w:space="0" w:sz="4" w:val="single"/>'
                    '<w:right w:color="auto" w:space="0" w:sz="4" w:val="single"/>'
                    '<w:insideH w:color="auto" w:space="0" w:sz="4" w:val="single"/>'
                    '<w:insideV w:color="auto" w:space="0" w:sz="4" w:val="single"/>'
                    '</w:tblBorders>'
                    INTO lw_xml.
      ENDIF.
    ENDIF.

* Define table width
    lw_tblwidth = tblwidth.
    CONDENSE lw_tblwidth NO-GAPS.
    IF tblwidth = 0.
* If no table width given, set it to "auto"
      CONCATENATE lw_xml
                  '<w:tblW w:w="'
                  lw_tblwidth
                  '" w:type="auto"/>'
                  '</w:tblPr>'
                  INTO lw_xml.
    ELSE.
      CONCATENATE lw_xml
                  '<w:tblW w:w="'
                  lw_tblwidth
                  '" w:type="dxa"/>'
                  '</w:tblPr>'
                  INTO lw_xml.
    ENDIF.

* Fill table content
    LOOP AT content INTO <line>.
      CONCATENATE lw_xml
                  '<w:tr>'
                  INTO lw_xml.
      lw_col = 1.
      DO lw_cols TIMES.
        lw_col_inc = 1.
*--Filling the cell
        IF lw_style_table = 0. "fields are plain text
          CONCATENATE lw_xml
                      '<w:tc>'
                      INTO lw_xml.

          ASSIGN COMPONENT lw_col OF STRUCTURE <line> TO <field>.
          lw_string = <field>.
          CALL METHOD write_text
            EXPORTING
              textline = lw_string
            IMPORTING
              virtual  = lw_string.
          CONCATENATE lw_xml
                      '<w:p>'
                      lw_string
                      '</w:p>'
                      INTO lw_xml.
        ELSE. " fields have ty_table_style_field structure, apply styles
          ASSIGN COMPONENT lw_col OF STRUCTURE <line> TO <field_style>.
          IF sy-subrc NE 0. "Occurs when merged cell
            EXIT. "exit do
          ENDIF.
          CONCATENATE lw_xml
                      '<w:tc>'
                      INTO lw_xml.

          CLEAR : lw_style, lw_stylep.
          IF NOT <field_style>-bgcolor IS INITIAL.
            CONCATENATE lw_style
                        '<w:shd w:fill="'
                        <field_style>-bgcolor
                        '"/>'
                        INTO lw_style.
          ENDIF.
          IF NOT <field_style>-valign IS INITIAL.
            CONCATENATE lw_style
                        '<w:vAlign w:val="'
                        <field_style>-valign
                        '"/>'
                        INTO lw_style.
          ENDIF.
          IF <field_style>-merge > 1.
            lw_col_inc = <field_style>-merge.
            lw_merge = <field_style>-merge.
            CONDENSE lw_merge NO-GAPS.
            CONCATENATE lw_style
                        '<w:gridSpan w:val="'
                        lw_merge
                        '"/>'
                        INTO lw_style.
          ENDIF.

          IF NOT lw_style IS INITIAL.
            CONCATENATE '<w:tcPr>' lw_style '</w:tcPr>' INTO lw_style.
          ENDIF.

          CLEAR lw_string.
          IF NOT <field_style>-xml IS INITIAL.
            CONCATENATE lw_xml
                        lw_style
                        <field_style>-xml
                        INTO lw_xml.
          ELSEIF NOT <field_style>-image_id IS INITIAL.
            CLEAR lw_string.
            CALL METHOD insert_image
              EXPORTING
                style        = <field_style>-line_style
                style_effect = <field_style>-line_style_effect
              IMPORTING
                virtual      = lw_string
              CHANGING
                id           = <field_style>-image_id.

            CONCATENATE lw_xml
                        lw_style
                        lw_string
                        INTO lw_xml.
          ELSE.
            CALL METHOD write_text
              EXPORTING
                textline     = <field_style>-textline
                style_effect = <field_style>-style_effect
                style        = <field_style>-style
              IMPORTING
                virtual      = lw_string.

            IF NOT <field_style>-line_style IS INITIAL
            OR NOT <field_style>-line_style_effect IS INITIAL.
              CALL METHOD _build_paragraph_style
                EXPORTING
                  style        = <field_style>-line_style
                  style_effect = <field_style>-line_style_effect
                IMPORTING
                  xml          = lw_stylep.
            ENDIF.

            CONCATENATE lw_xml
                        lw_style
                        '<w:p>'
                        lw_stylep
                        lw_string
                        '</w:p>'
                        INTO lw_xml.
          ENDIF.
        ENDIF.
        CONCATENATE lw_xml '</w:tc>' INTO lw_xml.
        lw_col = lw_col + lw_col_inc.
      ENDDO.
      CONCATENATE lw_xml '</w:tr>' INTO lw_xml.
    ENDLOOP.

    CONCATENATE lw_xml
                '</w:tbl>'
                INTO lw_xml.

    IF virtual IS SUPPLIED.
      virtual = lw_xml.
    ELSE.
      CONCATENATE mw_docxml
                  lw_xml
                  INTO mw_docxml.
    ENDIF.

  ENDMETHOD.                    "write_table

  METHOD write_newpage.
    write_break( breaktype = c_breaktype_page ).
  ENDMETHOD.                    "write_newpage

  METHOD write_toc.
    DATA : lw_default TYPE string,
           lw_content TYPE string.

* Classic TOC
    IF label IS INITIAL.
      lw_default = '--== Table of content - please refresh ==--'.
      lw_content = '\o "1-9"'.

* Table of content for specific label (table, figure, ...)
    ELSE.
      CONCATENATE '--== Table of '
                  label
                  ' - please refresh ==--'
                  INTO lw_default RESPECTING BLANKS.

      CALL METHOD _protect_label
        EXPORTING
          in  = label
        IMPORTING
          out = lw_content.

      CONCATENATE '\h \h \c "'
                  lw_content
                  '"'
                  INTO lw_content.
    ENDIF.

* If specified, use given default text
    IF NOT default IS INITIAL.
      lw_default = default.
    ENDIF.

* Write TOC
    CONCATENATE mw_docxml
                '<w:p>'
                '<w:r><w:fldChar w:fldCharType="begin"/></w:r>'
                '<w:r><w:instrText> TOC '
                lw_content
                ' </w:instrText></w:r>'
                '</w:p>'

* Add a default text for initial TOC value
                '<w:p>'
                '<w:pPr><w:jc w:val="center"/></w:pPr>'
                '<w:r>'
                '<w:fldChar w:fldCharType="separate"/></w:r>'
                '<w:r>'
                '<w:rPr><w:sz w:val="36"/><w:szCs w:val="36"/></w:rPr>'
                '<w:t>'
                lw_default
                '</w:t></w:r>'
                '</w:p>'

                '<w:p>'
                '<w:r><w:fldChar w:fldCharType="end"/></w:r>'
                '</w:p>'

                INTO mw_docxml RESPECTING BLANKS.
  ENDMETHOD.                    "write_toc

  METHOD write_note.
    DATA : ls_link_style_effect TYPE ty_character_style_effect,
           ls_line_style_effect TYPE ty_paragraph_style_effect,
           lw_style             TYPE string,
           lw_string            TYPE string,
           lw_id                TYPE string.

* Define a default style for link
    ls_link_style_effect = link_style_effect.
    IF link_style IS INITIAL AND ls_link_style_effect IS INITIAL.
      ls_link_style_effect-sup = c_true.
    ENDIF.

* Define a default style for footnote
    ls_line_style_effect = line_style_effect.
    IF line_style IS INITIAL AND ls_line_style_effect IS INITIAL.
      ls_line_style_effect-spacing_before = '0'.
      ls_line_style_effect-spacing_after = '0'.
      ls_line_style_effect-interline = 240.
    ENDIF.

* Create a new footnote
    CALL METHOD _create_note
      EXPORTING
        text               = text
        type               = type
        style              = style
        style_effect       = style_effect
        line_style         = line_style
        line_style_effect  = ls_line_style_effect
        link_style         = link_style
        link_style_effect  = ls_link_style_effect
      IMPORTING
        invalid_style      = invalid_style
        invalid_line_style = invalid_line_style
        id                 = lw_id.

    IF lw_id IS INITIAL.
      RETURN.
    ENDIF.

* Prepare style for the footnote link
    CALL METHOD _build_character_style
      EXPORTING
        style         = link_style
        style_effect  = ls_link_style_effect
      IMPORTING
        xml           = lw_style
        invalid_style = invalid_link_style.

* Now insert note in document
    IF type = c_notetype_foot.
      lw_string = '<w:footnoteReference w:id="'.
    ELSEIF type = c_notetype_end.
      lw_string = '<w:endnoteReference w:id="'.
    ENDIF.

    CONCATENATE mw_fragxml
                '<w:r>'
                lw_style
                lw_string
                lw_id
                '"/>'
                '</w:r>'
                INTO mw_fragxml.

  ENDMETHOD.                    "write_footnote

  METHOD write_comment.
    DATA : lw_file              TYPE string,
           lw_string            TYPE string,
           lw_id                TYPE string,
           lw_text              TYPE string,
           lw_xmlns             TYPE string,
           lw_head_style        TYPE string,
           lw_line_style        TYPE string,
           ls_head_style_effect TYPE ty_character_style_effect,
           lw_author            TYPE string,
           lw_initials          TYPE string.

    READ TABLE mo_zip->files WITH KEY name = 'word/comments.xml'
               TRANSPORTING NO FIELDS.
    IF sy-subrc = 0.
* If comment file exists, load the file
      CALL METHOD _get_zip_file
        EXPORTING
          filename = 'word/comments.xml'
        IMPORTING
          content  = lw_file.
    ELSE.
* If comments file doesnt exist, declare it and create it
* Add comments in content_types
      CALL METHOD _get_zip_file
        EXPORTING
          filename = '[Content_Types].xml'
        IMPORTING
          content  = lw_file.

      CONCATENATE '<Override'
                  ' ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml"'
                  ' PartName="/word/comments.xml"/></Types>'
                  INTO lw_string RESPECTING BLANKS.
      REPLACE '</Types>' WITH lw_string
              INTO lw_file.

      CALL METHOD _update_zip_file
        EXPORTING
          filename = '[Content_Types].xml'
          content  = lw_file.

* Add comments in relation file
      CALL METHOD _get_zip_file
        EXPORTING
          filename = 'word/_rels/document.xml.rels'
        IMPORTING
          content  = lw_file.

* Create comments relation ID
      DO.
        lw_id = sy-index.
        CONDENSE lw_id NO-GAPS.
        CONCATENATE 'rId' lw_id INTO lw_id.                 "#EC NOTEXT
        CONCATENATE 'Id="' lw_id '"' INTO lw_string.        "#EC NOTEXT
        FIND FIRST OCCURRENCE OF lw_string IN lw_file IGNORING CASE.
        IF sy-subrc NE 0.
          EXIT. "exit do
        ENDIF.
      ENDDO.

* Add relation
      CONCATENATE '<Relationship Target="comments.xml"'
                  ' Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments"'
                  ' Id="'
                  lw_id
                  '"/>'
                  '</Relationships>'
                  INTO lw_string RESPECTING BLANKS.
      REPLACE '</Relationships>' WITH lw_string INTO lw_file.

* Update relation file
      CALL METHOD _update_zip_file
        EXPORTING
          filename = 'word/_rels/document.xml.rels'
          content  = lw_file.

      CALL METHOD _get_xml_ns
        IMPORTING
          xml = lw_xmlns.

* Create empty comments file
      CONCATENATE '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
                  "cl_abap_char_utilities=>cr_lf
                  cl_abap_char_utilities=>newline
                  '<w:comments '
                  lw_xmlns
                  '>'
                  '</w:comments>'
                  INTO lw_file RESPECTING BLANKS.
    ENDIF.

* Search available comment id
    DO.
      "sy-index = sy-index + 4.
      lw_id = sy-index.
      CONDENSE lw_id NO-GAPS.
      CONCATENATE 'w:id="' lw_id '"' INTO lw_string.        "#EC NOTEXT
      FIND FIRST OCCURRENCE OF lw_string IN lw_file IGNORING CASE.
      IF sy-subrc NE 0.
        EXIT. "exit do
      ENDIF.
    ENDDO.

* Add blank at start of note
    lw_text = text.
    IF lw_text IS INITIAL OR lw_text(1) NE space.
      CONCATENATE space lw_text INTO lw_text RESPECTING BLANKS.
    ENDIF.

    CALL METHOD write_text
      EXPORTING
        textline      = lw_text
        style_effect  = style_effect
        style         = style
      IMPORTING
        virtual       = lw_text
        invalid_style = invalid_style.

* Define default style for comment head
    ls_head_style_effect = head_style_effect.
    IF ls_head_style_effect IS INITIAL AND head_style IS INITIAL.
      ls_head_style_effect-bold = c_true.
    ENDIF.

    CALL METHOD _build_character_style
      EXPORTING
        style        = head_style
        style_effect = ls_head_style_effect
      IMPORTING
        xml          = lw_head_style
        invalid_style = invalid_head_style.

    IF NOT line_style_effect IS INITIAL OR NOT line_style IS INITIAL.
      CALL METHOD _build_paragraph_style
        EXPORTING
          style         = line_style
          style_effect  = line_style_effect
        IMPORTING
          xml           = lw_line_style
          invalid_style = invalid_line_style.
    ENDIF.

* Define author property
    IF author IS INITIAL.
      lw_author = mw_author.
    ELSE.
      lw_author = author.
    ENDIF.

* Define initial property
    IF initials IS INITIAL.
      lw_initials = lw_author.
    ELSE.
      lw_initials = initials.
    ENDIF.

    CONCATENATE '<w:comment w:initials="'
                lw_initials
                '"'
                ' w:date="'
                datum(4)
                '-'
                datum+4(2)
                '-'
                datum+6(2)
                'T'
                uzeit(2)
                ':'
                uzeit+2(2)
                ':'
                uzeit+4(2)
                'Z"'
                ' w:author="'
                lw_author
                '"'
                ' w:id="'
                lw_id
                '">'
                '<w:p>'
                lw_line_style
                '<w:r>'
                lw_head_style
                '<w:annotationRef/>'
                '</w:r>'
                lw_text
                '</w:p>'
                '</w:comment>'
                '</w:comments>'
                INTO lw_string RESPECTING BLANKS.
    REPLACE '</w:comments>' WITH lw_string INTO lw_file.

    CALL METHOD _update_zip_file
      EXPORTING
        filename = 'word/comments.xml'
        content  = lw_file.

* Finally insert reference to comment in current text fragment
    CONCATENATE mw_fragxml
                '<w:r><w:commentReference w:id="'
                lw_id
                '"/></w:r>'
                INTO mw_fragxml.

  ENDMETHOD.                    "write_comment

  METHOD direct_draw.
    DATA : lw_texte  TYPE string,
           lw_style  TYPE string,
           lw_string TYPE string.

* Prepare text content
    IF text_xml IS NOT INITIAL.
      lw_texte = text_xml.
    ELSEIF text IS NOT INITIAL.
      CALL METHOD write_text
        EXPORTING
          textline   = text
          style      = text_style
          line_style = text_line_style
        IMPORTING
          virtual    = lw_texte.
    ENDIF.
    IF NOT lw_texte IS INITIAL.
      CONCATENATE '<v:textbox>'
                  '<w:txbxContent>'
                  lw_texte
                  '</w:txbxContent>'
                  '</v:textbox>'
                  INTO lw_texte.
    ENDIF.

* Prepare object style
    lw_string = posx.
    CONDENSE lw_string NO-GAPS.
    CONCATENATE ' style="'
                'position:absolute;'
                'margin-left:'
                lw_string
                unit
                ';'
                INTO lw_style RESPECTING BLANKS.

    lw_string = posy.
    CONDENSE lw_string NO-GAPS.
    CONCATENATE lw_style
                'margin-top:'
                lw_string
                unit
                ';'
                INTO lw_style RESPECTING BLANKS.
    lw_string = width.
    CONDENSE lw_string NO-GAPS.
    CONCATENATE lw_style
                'width:'
                lw_string
                unit
                ';'
                INTO lw_style RESPECTING BLANKS.
    lw_string = height.
    CONDENSE lw_string NO-GAPS.
    CONCATENATE lw_style
                'height:'
                lw_string
                unit
                ';'
                INTO lw_style RESPECTING BLANKS.
    lw_string = zindex.
    CONDENSE lw_string NO-GAPS.
    CONCATENATE lw_style
                'z-index:'
                lw_string
                ';"'
                INTO lw_style RESPECTING BLANKS.

    IF bg_color IS INITIAL.
      CONCATENATE lw_style
                  ' filled="f"'
                  INTO lw_style RESPECTING BLANKS.
    ELSE.
      CONCATENATE lw_style
                  ' fillcolor="'
                  bg_color
                  '"'
                  INTO lw_style RESPECTING BLANKS.
    ENDIF.

    IF border_width IS INITIAL.
      CONCATENATE lw_style
                  ' stroked="f"'
                  INTO lw_style RESPECTING BLANKS.
    ELSEIF NOT border_color IS INITIAL.
      CONCATENATE lw_style
                  ' strokecolor="'
                  border_color
                  '"'
                  INTO lw_style RESPECTING BLANKS.
    ENDIF.

    lw_string = border_width.
    CONDENSE lw_string NO-GAPS.
    CONCATENATE lw_style
                ' strokeweight="'
                lw_string
                unit
                '"'
                INTO lw_style RESPECTING BLANKS.

    CONCATENATE mw_fragxml
                '<w:r>'
                '<w:pict>'
                '<v:'
                type
                lw_style
                '>'
                lw_texte
                '</v:'
                type
                '>'
                '</w:pict>'
                '</w:r>'
                INTO mw_fragxml RESPECTING BLANKS.

  ENDMETHOD.                    "direct_draw

  METHOD draw_init.
    DATA : lw_style    TYPE string,
           lw_width    TYPE i,
           lw_string   TYPE string,
           lw_string_x TYPE string,
           lw_string_y TYPE string,
           lw_string_w TYPE string,
           lw_string_h TYPE string
           .

    CLEAR mw_fragxml.
    CLEAR lw_style.
    IF bgcolor IS SUPPLIED AND NOT bgcolor IS INITIAL.
      CONCATENATE lw_style
                  '<wpc:bg>'
                  '<a:solidFill>'
                  '<a:srgbClr val="'
                  bgcolor
                  '" />'
                  '</a:solidFill>'
                  '</wpc:bg>'
                  INTO lw_style.
    ENDIF.

    IF bdcolor IS SUPPLIED AND NOT bdcolor IS INITIAL
    AND bdwidth IS SUPPLIED AND NOT bdwidth IS INITIAL.
      lw_width = c_basesize * bdwidth.
      lw_string = lw_width.
      CONDENSE lw_string NO-GAPS.

      CONCATENATE lw_style
                  '<wpc:whole>'
                  '<a:ln w="'
                  lw_string
                  '">'
                  '<a:solidFill>'
                  '<a:srgbClr val="'
                  bdcolor
                  '" />'
                  '</a:solidFill>'
                  '</a:ln>'
                  '</wpc:whole>'
                  INTO lw_style.
    ENDIF.

    lw_string_x = lw_width = c_basesize * left.
    CONDENSE lw_string_x NO-GAPS.
    lw_string_y = lw_width = c_basesize * top.
    CONDENSE lw_string_y NO-GAPS.
    lw_string_w = lw_width = c_basesize * width.
    CONDENSE lw_string_w NO-GAPS.
    lw_string_h = lw_width = c_basesize * height.
    CONDENSE lw_string_h NO-GAPS.

    CONCATENATE '<w:r>'
                '<mc:AlternateContent>'
                '<mc:Choice Requires="wpc">'
                '<w:drawing>'
                '<wp:anchor distR="0" distL="0" distB="0" distT="0" allowOverlap="0" layoutInCell="1" locked="0" behindDoc="1" relativeHeight="0" simplePos="0">'
                '<wp:simplePos y="0" x="0"/>'
                '<wp:positionH relativeFrom="column">'
                '<wp:posOffset>'
                lw_string_x
                '</wp:posOffset>'
                '</wp:positionH>'
                '<wp:positionV relativeFrom="paragraph">'
                '<wp:posOffset>'
                lw_string_y
                '</wp:posOffset>'
                '</wp:positionV>'
                '<wp:extent cy="'
                lw_string_h
                '" cx="'
                lw_string_w
                '"/>'
                '<wp:wrapNone/>'
                '<wp:docPr name="Draw container" id="3"/>'
                '<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">'
                '<a:graphicData uri="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas">'
                '<wpc:wpc>'
                lw_style
                INTO mw_fragxml RESPECTING BLANKS.
  ENDMETHOD.                    "draw_init

  METHOD draw.
    DATA : lw_width    TYPE i,
           lw_string_x TYPE string,
           lw_string_y TYPE string,
           lw_string_w TYPE string,
           lw_string_h TYPE string,
           lw_style    TYPE string,
           lw_string   TYPE string,
           lw_color    TYPE string.

    lw_string_x = lw_width = c_basesize * left.
    CONDENSE lw_string_x NO-GAPS.
    lw_string_y = lw_width = c_basesize * top.
    CONDENSE lw_string_y NO-GAPS.
    lw_string_w = lw_width = c_basesize * width.
    CONDENSE lw_string_w NO-GAPS.
    lw_string_h = lw_width = c_basesize * height.
    CONDENSE lw_string_h NO-GAPS.

    CASE object.
      WHEN c_draw_image.
        invalid_image = c_false.
        CALL METHOD _load_image
          EXPORTING
            url = url
          CHANGING
            id  = id.
        IF id IS INITIAL.
          invalid_image = c_true.
          RETURN.
        ENDIF.
        CLEAR lw_style.
        IF bgcolor IS SUPPLIED AND NOT bgcolor IS INITIAL.
          CONCATENATE lw_style
                      '<a:solidFill>'
                      '<a:srgbClr val="'
                      bgcolor
                      '" />'
                      '</a:solidFill>'
                      INTO lw_style.
        ENDIF.


        IF bdcolor IS SUPPLIED AND NOT bdcolor IS INITIAL
        AND bdwidth IS SUPPLIED AND NOT bdwidth IS INITIAL.
          lw_width = c_basesize * bdwidth.
          lw_string = lw_width.
          CONDENSE lw_string NO-GAPS.

          CONCATENATE lw_style
                      '<a:ln w="'
                      lw_string
                      '">'
                      '<a:solidFill>'
                      '<a:srgbClr val="'
                      bdcolor
                      '" />'
                      '</a:solidFill>'
                      '</a:ln>'
                      INTO lw_style.
        ENDIF.

        CONCATENATE mw_fragxml
                    '<pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture">'
                    '<pic:nvPicPr>'
                    '<pic:cNvPr name="Image" id="1"/>'
                    '<pic:cNvPicPr>'
                    '<a:picLocks/>'
                    '</pic:cNvPicPr>'
                    '</pic:nvPicPr>'
                    '<pic:blipFill>'
                    '<a:blip r:embed="'
                    id
                    '">'
                    '</a:blip>'
                    '<a:stretch><a:fillRect/></a:stretch>'
                    '</pic:blipFill>'
                    '<pic:spPr>'
                    '<a:xfrm>'
                    '<a:off y="'
                    lw_string_y
                    '" x="'
                    lw_string_x
                    '"/>'
                    '<a:ext cy="'
                    lw_string_h
                    '" cx="'
                    lw_string_w
                    '"/>'
                    '</a:xfrm>'
                    '<a:prstGeom prst="rect"/>'
                    lw_style
                    '</pic:spPr>'
                    '</pic:pic>'
                    INTO mw_fragxml.

      WHEN c_draw_rectangle.
* Default : white rect with 1pt black border
        CLEAR lw_style.
        IF bgcolor IS SUPPLIED AND NOT bgcolor IS INITIAL.
          lw_color = bgcolor.
        ELSE.
          lw_color = c_color_white.
        ENDIF.
        CONCATENATE lw_style
                    '<a:solidFill>'
                    '<a:srgbClr val="'
                    lw_color
                    '" />'
                    '</a:solidFill>'
                    INTO lw_style.

        IF bdcolor IS SUPPLIED AND NOT bdcolor IS INITIAL.
          lw_color = bdcolor.
        ELSE.
          lw_color = c_color_black.
        ENDIF.
        IF bdwidth IS SUPPLIED AND NOT bdwidth IS INITIAL.
          lw_width = c_basesize * bdwidth.
        ELSE.
          lw_width = c_basesize.
        ENDIF.
        lw_string = lw_width.
        CONDENSE lw_string NO-GAPS.

        CONCATENATE lw_style
                    '<a:ln w="'
                    lw_string
                    '">'
                    '<a:solidFill>'
                    '<a:srgbClr val="'
                    lw_color
                    '" />'
                    '</a:solidFill>'
                    '</a:ln>'
                    INTO lw_style.

        CONCATENATE mw_fragxml
                    '<wps:wsp>'
                    '<wps:cNvPr name="Rectangle" id="1"/>'
                    '<wps:cNvSpPr/>'
                    '<wps:spPr>'
                    '<a:xfrm>'
                    '<a:off y="'
                    lw_string_y
                    '" x="'
                    lw_string_x
                    '"/>'
                    '<a:ext cy="'
                    lw_string_h
                    '" cx="'
                    lw_string_w
                    '"/>'
                    '</a:xfrm>'
                    '<a:prstGeom prst="rect"/>'
                    lw_style
                    '</wps:spPr>'
                    '<wps:bodyPr />'
                    '</wps:wsp>'
                    INTO mw_fragxml RESPECTING BLANKS.
    ENDCASE.
  ENDMETHOD.                    "draw

  METHOD draw_finalize.

    CONCATENATE mw_docxml
                '<w:p>'
                mw_fragxml
                '</wpc:wpc>'
                '</a:graphicData>'
                '</a:graphic>'
                '</wp:anchor>'
                '</w:drawing>'
                '</mc:Choice>'
                '<mc:Fallback>'
                '<w:t>Canvas graphic cannot be loaded (compatible only from Word 2010)</w:t>'
                '</mc:Fallback>'
                '</mc:AlternateContent>'
                '</w:r>'
                '</w:p>'
                INTO mw_docxml.
    CLEAR mw_fragxml.

  ENDMETHOD.                    "draw_finalize

  METHOD write_break.
    CASE breaktype.
      WHEN c_breaktype_line.
        IF virtual IS SUPPLIED.
          CONCATENATE virtual
                      '<w:r><w:br/></w:r>'
                      INTO virtual.
        ELSE.
          CONCATENATE mw_fragxml
                      '<w:r><w:br/></w:r>'
                      INTO mw_fragxml.
        ENDIF.
      WHEN c_breaktype_page.
        IF virtual IS SUPPLIED.
          CONCATENATE virtual
                      '<w:br w:type="page"/>'
                      INTO virtual.
        ELSE.
          CONCATENATE mw_fragxml
                      '<w:br w:type="page"/>'
                      INTO mw_fragxml.
        ENDIF.
      WHEN c_breaktype_section.
        IF virtual IS SUPPLIED.
          sy-subrc = 8.
          RETURN.
        ENDIF.
        CALL METHOD _write_section.
      WHEN c_breaktype_section_continuous.
        IF virtual IS SUPPLIED.
          sy-subrc = 8.
          RETURN.
        ENDIF.
        CALL METHOD _write_section.
        ms_section-continuous = c_true.
      WHEN OTHERS. "invalid breaktype
        sy-subrc = 8.
        RETURN.
    ENDCASE.
* Write line if required
* But also automatically except for simple break line
    IF write_line = c_true
    OR ( NOT write_line IS SUPPLIED AND breaktype NE c_breaktype_line ).
      CALL METHOD write_line.
    ENDIF.
  ENDMETHOD.                    "write_break

  METHOD _write_section.
    DATA : lw_size     TYPE string,
           lw_space    TYPE string,
           lw_substyle TYPE string.

    CLEAR mw_section_xml.

* In case of keep template content, first document section is the last
* template section
    IF NOT mw_tpl_section_xml IS INITIAL.
      mw_section_xml = mw_tpl_section_xml.
      CLEAR mw_tpl_section_xml.
      CLEAR ms_section.
      RETURN.
    ENDIF.

* Define header/footer
    IF NOT ms_section-header IS INITIAL.
      CONCATENATE mw_section_xml
                  '<w:headerReference w:type="default" r:id="'
                  ms_section-header
                  '"/>'
                  INTO mw_section_xml.
    ENDIF.
    IF NOT ms_section-footer IS INITIAL.
      CONCATENATE mw_section_xml
                  '<w:footerReference w:type="default" r:id="'
                  ms_section-footer
                  '"/>'
                  INTO mw_section_xml.
    ENDIF.
    IF NOT ms_section-header_first IS INITIAL.
      CONCATENATE mw_section_xml
                  '<w:headerReference w:type="first" r:id="'
                  ms_section-header_first
                  '"/>'
                  INTO mw_section_xml.
    ENDIF.
    IF NOT ms_section-footer_first IS INITIAL.
      CONCATENATE mw_section_xml
                  '<w:footerReference w:type="first" r:id="'
                  ms_section-footer_first
                  '"/>'
                  INTO mw_section_xml.
    ENDIF.

* Define page orientation
    IF ms_section-landscape = c_true.
      CONCATENATE mw_section_xml
                  '<w:pgSz w:w="16838" w:h="11906" w:orient="landscape"/>'
                  INTO mw_section_xml.
    ELSE.
      CONCATENATE mw_section_xml
                  '<w:pgSz w:w="11906" w:h="16838"/>'
                  INTO mw_section_xml.
    ENDIF.

* Border ?
    CLEAR lw_substyle.
    IF NOT ms_section-border_left-style IS INITIAL
    AND NOT ms_section-border_left-width IS INITIAL.
      lw_size = ms_section-border_left-width.
      CONDENSE lw_size NO-GAPS.
      lw_space = ms_section-border_left-space.
      CONDENSE lw_space NO-GAPS.
      CONCATENATE lw_substyle
                  '<w:left w:val="'
                  ms_section-border_left-style
                  '" w:sz="'
                  lw_size
                  '" w:space="'
                  lw_space
                  '" w:color="'
                  ms_section-border_left-color
                  '"/>'
                  INTO lw_substyle RESPECTING BLANKS.
    ENDIF.
    IF NOT ms_section-border_top-style IS INITIAL
    AND NOT ms_section-border_top-width IS INITIAL.
      lw_size = ms_section-border_top-width.
      CONDENSE lw_size NO-GAPS.
      lw_space = ms_section-border_top-space.
      CONDENSE lw_space NO-GAPS.
      CONCATENATE lw_substyle
                  '<w:top w:val="'
                  ms_section-border_top-style
                  '" w:sz="'
                  lw_size
                  '" w:space="'
                  lw_space
                  '" w:color="'
                  ms_section-border_top-color
                  '"/>'
                  INTO lw_substyle RESPECTING BLANKS.
    ENDIF.
    IF NOT ms_section-border_right-style IS INITIAL
    AND NOT ms_section-border_right-width IS INITIAL.
      lw_size = ms_section-border_right-width.
      CONDENSE lw_size NO-GAPS.
      lw_space = ms_section-border_right-space.
      CONDENSE lw_space NO-GAPS.
      CONCATENATE lw_substyle
                  '<w:right w:val="'
                  ms_section-border_right-style
                  '" w:sz="'
                  lw_size
                  '" w:space="'
                  lw_space
                  '" w:color="'
                  ms_section-border_right-color
                  '"/>'
                  INTO lw_substyle RESPECTING BLANKS.
    ENDIF.
    IF NOT ms_section-border_bottom-style IS INITIAL
    AND NOT ms_section-border_bottom-width IS INITIAL.
      lw_size = ms_section-border_bottom-width.
      CONDENSE lw_size NO-GAPS.
      lw_space = ms_section-border_bottom-space.
      CONDENSE lw_space NO-GAPS.
      CONCATENATE lw_substyle
                  '<w:bottom w:val="'
                  ms_section-border_bottom-style
                  '" w:sz="'
                  lw_size
                  '" w:space="'
                  lw_space
                  '" w:color="'
                  ms_section-border_bottom-color
                  '"/>'
                  INTO lw_substyle RESPECTING BLANKS.
    ENDIF.
    IF NOT lw_substyle IS INITIAL.
      CONCATENATE mw_section_xml
                  '<w:pgBorders w:offsetFrom="page">'
                  lw_substyle
                  '</w:pgBorders>'
                  INTO mw_section_xml RESPECTING BLANKS.
    ENDIF.

* Default section values / Standard page
    CONCATENATE mw_section_xml
                '<w:cols w:space="708"/>'
                '<w:docGrid w:linePitch="360"/>'
                '<w:pgMar w:top="1417" w:right="1417" w:bottom="1417" w:left="1417" w:header="708" w:footer="708" w:gutter="0"/>'
                INTO mw_section_xml.

    IF ms_section-continuous = c_true.
      CONCATENATE mw_section_xml
                  '<w:type w:val="continuous"/>'
                  INTO mw_section_xml.
    ENDIF.

    IF NOT ms_section-header_first IS INITIAL
    OR NOT ms_section-footer_first IS INITIAL.
      CONCATENATE mw_section_xml
                  '<w:titlePg/>'
                  INTO mw_section_xml.
    ENDIF.

    CONCATENATE '<w:sectPr>'
                mw_section_xml
                '</w:sectPr>'
                INTO mw_section_xml.

    CLEAR ms_section.

  ENDMETHOD.                    "write_section

  METHOD write_symbol.
    DATA lw_style_effect TYPE ty_character_style_effect.
    lw_style_effect-font = c_font_symbol.
    CALL METHOD write_text
      EXPORTING
        textline     = symbol
        style_effect = lw_style_effect.
  ENDMETHOD.                    "write_symbol

  METHOD write_headerfooter.
    DATA : lw_filename    TYPE string,
           lw_string      TYPE string,
           lw_file        TYPE string,
           lw_xml         TYPE string,
           lw_xmlns       TYPE string,
           lw_style       TYPE string,
           ls_list_object LIKE LINE OF mt_list_object.

    IF type NE c_type_header AND type NE c_type_footer.
      RETURN.
    ENDIF.

* Build header/footer xml fragment
    CALL METHOD write_text
      EXPORTING
        textline      = textline
        style_effect  = style_effect
        style         = style
      IMPORTING
        virtual       = lw_xml
        invalid_style = invalid_style.

    CLEAR lw_style.
    IF NOT line_style IS INITIAL OR NOT line_style_effect IS INITIAL.
      CALL METHOD _build_paragraph_style
        EXPORTING
          style         = line_style
          style_effect  = line_style_effect
        IMPORTING
          xml           = lw_style
          invalid_style = invalid_line_style.
    ENDIF.

    CALL METHOD _get_xml_ns
      IMPORTING
        xml = lw_xmlns.

    IF type = c_type_header.
      lw_string = 'hdr'.
    ELSE.
      lw_string = 'ftr'.
    ENDIF.
    CONCATENATE '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
                '<w:'
                lw_string
                lw_xmlns
                '>'
                '<w:p>'
                lw_style
                lw_xml
                '</w:p>'
                '</w:'
                lw_string
                '>'
                INTO lw_xml RESPECTING BLANKS.

* Search available header/footer name
    DO.
      lw_filename = sy-index.
      CONCATENATE 'word/'
                  type
                  lw_filename
                  '.xml'
                  INTO lw_filename.
      CONDENSE lw_filename NO-GAPS.

      READ TABLE mo_zip->files WITH KEY name = lw_filename
                 TRANSPORTING NO FIELDS.
      IF sy-subrc NE 0.
        EXIT. "exit do
      ENDIF.
    ENDDO.

* Add header/footer file into zip
    CALL METHOD _update_zip_file
      EXPORTING
        filename = lw_filename
        content  = lw_xml.

* Add content type exception for new header/footer
    CALL METHOD _get_zip_file
      EXPORTING
        filename = '[Content_Types].xml'
      IMPORTING
        content  = lw_file.
    CONCATENATE '<Override PartName="/'
                lw_filename
                '" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.'
                type
                '+xml"/>'
                '</Types>'
                INTO lw_string.
    REPLACE '</Types>' WITH lw_string INTO lw_file.

* Update content type file
    CALL METHOD _update_zip_file
      EXPORTING
        filename = '[Content_Types].xml'
        content  = lw_file.

* Get relation file
    CALL METHOD _get_zip_file
      EXPORTING
        filename = 'word/_rels/document.xml.rels'
      IMPORTING
        content  = lw_file.

* Create header/footer ID
    DO.
      id = sy-index.
      CONDENSE id NO-GAPS.
      CONCATENATE 'rId' id INTO id.                         "#EC NOTEXT
      CONCATENATE 'Id="' id '"' INTO lw_string.             "#EC NOTEXT
      FIND FIRST OCCURRENCE OF lw_string IN lw_file IGNORING CASE.
      IF sy-subrc NE 0.
        EXIT. "exit do
      ENDIF.
    ENDDO.

* Update object list
    ls_list_object-id = id.
    ls_list_object-type = type.
    ls_list_object-path = lw_filename.
    APPEND ls_list_object TO mt_list_object.

* Add relation
    lw_filename = lw_filename+5.
    CONCATENATE '<Relationship Id="'
                id
                '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/'
                type
                '" Target="'
                lw_filename
                '"/>'
                '</Relationships>'
                INTO lw_string.
    REPLACE '</Relationships>' WITH lw_string INTO lw_file.

* Update relation file
    CALL METHOD _update_zip_file
      EXPORTING
        filename = 'word/_rels/document.xml.rels'
        content  = lw_file.

    IF usenow_default = c_true AND type = c_type_header.
      CALL METHOD header_footer_direct_assign
        EXPORTING
          header = id.
    ELSEIF usenow_default = c_true AND type = c_type_footer.
      CALL METHOD header_footer_direct_assign
        EXPORTING
          footer = id.
    ENDIF.

    IF usenow_first = c_true AND type = c_type_header.
      CALL METHOD header_footer_direct_assign
        EXPORTING
          header_first = id.
    ELSEIF usenow_first = c_true AND type = c_type_footer.
      CALL METHOD header_footer_direct_assign
        EXPORTING
          footer_first = id.
    ENDIF.


  ENDMETHOD.                    "write_headerfooter

  METHOD set_title.
    set_properties( title = title ).
  ENDMETHOD.                    "set_title

  METHOD insert_custom_field.
    CONCATENATE mw_fragxml
                '<w:fldSimple w:instr=" DOCPROPERTY '
                field
                ' \* MERGEFORMAT "><w:r><w:rPr><w:b/></w:rPr><w:t>Please update field</w:t></w:r></w:fldSimple>'
                INTO mw_fragxml RESPECTING BLANKS.
  ENDMETHOD.                    "insert_custom_field

  METHOD insert_virtual_field.
    IF field_as_paragraph = c_false.
      CONCATENATE mw_fragxml
                  '<w:virtual>'
                  field
                  '</w:virtual>'
                  INTO mw_fragxml.
    ELSE.
      CONCATENATE mw_docxml
                  '<w:virtual>'
                  field
                  '</w:virtual>'
                  INTO mw_docxml.
    ENDIF.
  ENDMETHOD.                    "insert_virtual_field

  METHOD replace_virtual_field.
    DATA : lw_field TYPE string,
           lw_value TYPE string.
    CONCATENATE '<w:virtual>'
                field
                '</w:virtual>'
           INTO lw_field.

    IF value_as_xml = c_true.
      lw_value = value.
    ELSE.
      CALL METHOD write_text
        EXPORTING
          textline      = value
          style_effect  = style_effect
          style         = style
        IMPORTING
          virtual       = lw_value
          invalid_style = invalid_style.
    ENDIF.

    REPLACE lw_field IN mw_fragxml WITH lw_value IGNORING CASE.
    IF sy-subrc NE 0.
      REPLACE lw_field IN mw_docxml WITH lw_value IGNORING CASE.
    ENDIF.
  ENDMETHOD.                    "replace_virtual_field

  METHOD create_custom_field. "not managed
    DATA : lw_file   TYPE string,
           lw_string TYPE string,
           lw_id     TYPE string.

* If customproperties does not exist, create it
    READ TABLE mo_zip->files WITH KEY name = 'docProps/custom.xml'
               TRANSPORTING NO FIELDS.
    IF sy-subrc NE 0.

* Declare new file in relations
      CALL METHOD _get_zip_file
        EXPORTING
          filename = '_rels/.rels'
        IMPORTING
          content  = lw_file.
* search available id
      DO.
        lw_id = sy-index.
        CONDENSE lw_id NO-GAPS.
        CONCATENATE 'rId' lw_id INTO lw_id.                 "#EC NOTEXT
        CONCATENATE 'Id="' lw_id '"' INTO lw_string.        "#EC NOTEXT
        FIND lw_string IN lw_file IGNORING CASE.
        IF sy-subrc NE 0.
          EXIT. "exit do
        ENDIF.
      ENDDO.

      CONCATENATE '<Relationship Target="docProps/custom.xml"'
                  ' Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties"'
                  ' Id="'
                  lw_id
                  '"/></Relationships>'
                  INTO lw_string RESPECTING BLANKS.
      REPLACE '</Relationships>' IN lw_file WITH lw_string.
      CALL METHOD _update_zip_file
        EXPORTING
          filename = '_rels/.rels'
          content  = lw_file.

* Declare new file in content type
      CALL METHOD _get_zip_file
        EXPORTING
          filename = '[Content_Types].xml'
        IMPORTING
          content  = lw_file.
      REPLACE '</Types>' IN lw_file
              WITH '<Override ContentType="application/vnd.openxmlformats-officedocument.custom-properties+xml" PartName="/docProps/custom.xml"/></Types>'.
      CALL METHOD _update_zip_file
        EXPORTING
          filename = '[Content_Types].xml'
          content  = lw_file.

      CONCATENATE '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>'
                  '<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/custom-properties"'
                  ' xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">'
                  '</Properties>'
                  INTO lw_file RESPECTING BLANKS.
      CALL METHOD _update_zip_file
        EXPORTING
          filename = 'docProps/custom.xml'
          content  = lw_file.
    ENDIF.

* Open custom properties
    CALL METHOD _get_zip_file
      EXPORTING
        filename = 'docProps/custom.xml'
      IMPORTING
        content  = lw_file.

* Search available ID
    DO.
      IF sy-index = 1.
        CONTINUE.
      ENDIF.
      lw_id = sy-index.
      CONDENSE lw_id NO-GAPS.
      CONCATENATE 'pid="' lw_id '"' INTO lw_string.
      FIND lw_string IN lw_file IGNORING CASE.
      IF sy-subrc NE 0.
        EXIT. "exit do
      ENDIF.
    ENDDO.

* Add property
    CONCATENATE '<property fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"'
                ' pid="'
                lw_id
                '" name="'
                field
                '">'
                '<vt:lpwstr>'
                value
                '</vt:lpwstr>'
                '</property>'
                '</Properties>'
                INTO lw_string RESPECTING BLANKS.
    REPLACE '</Properties>' IN lw_file WITH lw_string.

* Update custopproperties file
    CALL METHOD _update_zip_file
      EXPORTING
        filename = 'docProps/custom.xml'
        content  = lw_file.
  ENDMETHOD.                    "create_custom_field

  METHOD create_character_style.
    DATA : lw_style      TYPE string,
           lw_file       TYPE string,
           lw_string     TYPE string,
           ls_list_style LIKE LINE OF mt_list_style.

* Build simple style internal name
    name = output_name.
    REPLACE ALL OCCURRENCES OF REGEX '[^A-Za-z0-9]' IN name WITH space.
    CONDENSE name NO-GAPS.

* Check style internal name is available
    READ TABLE mt_list_style WITH KEY type = c_type_character
                                      name = name
                             TRANSPORTING NO FIELDS.
    IF sy-subrc = 0.
      CLEAR name.
      RETURN.
    ENDIF.

* Build character style
    CALL METHOD _build_character_style
      EXPORTING
        style_effect = style_effect
      IMPORTING
        xml          = lw_style.

    IF style_ref IS SUPPLIED AND NOT style_ref IS INITIAL.
      READ TABLE mt_list_style WITH KEY type = c_type_character
                                        name = style_ref
                               TRANSPORTING NO FIELDS.
      IF sy-subrc = 0.
        CONCATENATE '<w:basedOn w:val="'
                    style_ref
                    '"/>'
                    INTO lw_string.
      ELSE.
        invalid_style = c_true.
      ENDIF.
    ENDIF.
    CONCATENATE '<w:style w:type="'
                c_type_character
                '" w:customStyle="1" w:styleId="'
                name
                '">'
                '<w:name w:val="'
                output_name
                '"/>'
                lw_string
                lw_style
                '</w:style>'
                '</w:styles>'
                INTO lw_style.

* Get styles file
    CALL METHOD _get_zip_file
      EXPORTING
        filename = 'word/styles.xml'
      IMPORTING
        content  = lw_file.

* Update style file content
    REPLACE '</w:styles>' IN lw_file WITH lw_style.

* Update zipped style file
    CALL METHOD _update_zip_file
      EXPORTING
        filename = 'word/styles.xml'
        content  = lw_file.

* Update style list
    CLEAR ls_list_style.
    ls_list_style-type = c_type_character.
    ls_list_style-name = name.
    APPEND ls_list_style TO mt_list_style.
  ENDMETHOD.                    "create_character_style

  METHOD create_paragraph_style.
    DATA : lw_style      TYPE string,
           lw_stylepr    TYPE string,
           lw_file       TYPE string,
           lw_string     TYPE string,
           ls_list_style LIKE LINE OF mt_list_style.

* Build simple style internal name
    name = output_name.
    REPLACE ALL OCCURRENCES OF REGEX '[^A-Za-z0-9]' IN name WITH space.
    CONDENSE name NO-GAPS.

* Check style internal name is available
    READ TABLE mt_list_style WITH KEY type = c_type_paragraph
                                      name = name
                             TRANSPORTING NO FIELDS.
    IF sy-subrc = 0.
      CLEAR name.
      RETURN.
    ENDIF.

* Build character style
    CALL METHOD _build_character_style
      EXPORTING
        style_effect = style_effect
      IMPORTING
        xml          = lw_style.

* Build paragraph style
    CALL METHOD _build_paragraph_style
      EXPORTING
        style_effect = line_style_effect
      IMPORTING
        xml          = lw_stylepr.

    IF style_ref IS SUPPLIED AND NOT style_ref IS INITIAL.
      READ TABLE mt_list_style WITH KEY type = c_type_paragraph
                                        name = style_ref
                               TRANSPORTING NO FIELDS.
      IF sy-subrc = 0.
        CONCATENATE '<w:basedOn w:val="'
                    style_ref
                    '"/>'
                    INTO lw_string.
      ELSE.
        invalid_style = c_true.
      ENDIF.
    ENDIF.
    CONCATENATE '<w:style w:type="'
                c_type_paragraph
                '" w:customStyle="1" w:styleId="'
                name
                '">'
                '<w:name w:val="'
                output_name
                '"/>'
                lw_string
                lw_stylepr
                lw_style
                '</w:style>'
                '</w:styles>'
                INTO lw_style.

* Get styles file
    CALL METHOD _get_zip_file
      EXPORTING
        filename = 'word/styles.xml'
      IMPORTING
        content  = lw_file.

* Update style file content
    REPLACE '</w:styles>' IN lw_file WITH lw_style.

* Update zipped style file
    CALL METHOD _update_zip_file
      EXPORTING
        filename = 'word/styles.xml'
        content  = lw_file.

* Update style list
    CLEAR ls_list_style.
    ls_list_style-type = c_type_paragraph.
    ls_list_style-name = name.
    APPEND ls_list_style TO mt_list_style.
  ENDMETHOD.                    "create_paragraph_style

  METHOD insert_attachment.
    DATA : lw_extension    TYPE string,
           lw_filex        TYPE xstring,
           lw_file         TYPE string,
           lw_filename     TYPE string,
           lw_string       TYPE string,
           lw_id           TYPE string,
           lw_url_begin(6) TYPE c,
           lw_ct_string    TYPE string,
           lw_rel_string   TYPE string,
           lw_style        TYPE string.

    invalid_image = c_false.
    invalid_file = c_false.

    IF url IS INITIAL AND file_content IS INITIAL.
      invalid_file = c_true.
      RETURN.
    ENDIF.

* Load file
    IF file_content IS INITIAL.
      CALL METHOD _load_file
        EXPORTING
          filename = url
        IMPORTING
          xcontent = lw_filex.
      IF lw_filex IS INITIAL.
        invalid_file = c_true.
        RETURN.
      ENDIF.
    ELSE.
      lw_filex = file_content.
    ENDIF.

* Get file extension
* For file from sapwr, get file extension from DB
    lw_url_begin = url.
    IF lw_url_begin = c_sapwr_prefix.
      SELECT SINGLE value INTO lw_extension
             FROM wwwparams
             WHERE relid = 'MI'
             AND objid = url+6
             AND name = 'fileextension'.
      IF NOT lw_extension IS INITIAL AND lw_extension(1) = '.'.
        lw_extension = lw_extension+1.
      ENDIF.
    ELSE.
* For other file, get file extension from filename
      FIND ALL OCCURRENCES OF '.' IN url MATCH OFFSET sy-fdpos.
      IF sy-subrc = 0.
        sy-fdpos = sy-fdpos + 1.
        lw_extension = url+sy-fdpos.
        TRANSLATE lw_extension TO LOWER CASE.
        IF lw_extension = 'jpeg'.
          lw_extension = 'jpg'.
        ENDIF.
      ENDIF.
    ENDIF.
    TRANSLATE lw_extension TO LOWER CASE.
    CASE lw_extension.
      WHEN 'pptx'.
        lw_ct_string = 'application/vnd.openxmlformats-officedocument.presentationml.presentation'.
        lw_rel_string = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/package'.
      WHEN 'ppt'.
        lw_ct_string = 'application/vnd.ms-powerpoint'.
        lw_rel_string = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/oleObject'.
      WHEN 'docx'.
        lw_ct_string = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'.
        lw_rel_string = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/package'.
      WHEN 'docm'.
        lw_ct_string = 'application/vnd.ms-word.document.macroEnabled.12'.
        lw_rel_string = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/package'.
      WHEN 'doc'.
        lw_ct_string = 'application/msword'.
        lw_rel_string = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/oleObject'.
      WHEN 'xlsx'.
        lw_ct_string = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'.
        lw_rel_string = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/package'.
      WHEN 'xls'.
        lw_ct_string = 'application/vnd.ms-excel'.
        lw_rel_string = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/oleObject'.
      WHEN OTHERS.
        invalid_file = c_true.
        RETURN.
    ENDCASE.

* Search available file name
    DO.
      lw_filename = sy-index.
      CONDENSE lw_filename NO-GAPS.
      CONCATENATE 'word/embeddings/attached' lw_filename '.' lw_extension
                  INTO lw_filename.
      READ TABLE mo_zip->files WITH KEY name = lw_filename
                 TRANSPORTING NO FIELDS.
      IF sy-subrc NE 0.
        EXIT. "exit do
      ENDIF.
    ENDDO.

* Add file in ZIP
    CALL METHOD mo_zip->add
      EXPORTING
        name    = lw_filename
        content = lw_filex.

* Get file extension list
    CALL METHOD _get_zip_file
      EXPORTING
        filename = '[Content_Types].xml'
      IMPORTING
        content  = lw_file.

* Add attachment exception in content type file
    CONCATENATE '<Override PartName="/'
                lw_filename
                '" ContentType="'
                lw_ct_string
                '"/></Types>'
                      INTO lw_string.
    REPLACE '</Types>' WITH  lw_string
            INTO lw_file.

* Update file extension list
    CALL METHOD _update_zip_file
      EXPORTING
        filename = '[Content_Types].xml'
        content  = lw_file.

* Get relation file
    CALL METHOD _get_zip_file
      EXPORTING
        filename = 'word/_rels/document.xml.rels'
      IMPORTING
        content  = lw_file.

* Create file ID
    DO.
      lw_id = sy-index.
      CONDENSE lw_id NO-GAPS.
      CONCATENATE 'rId' lw_id INTO lw_id.                   "#EC NOTEXT
      CONCATENATE 'Id="' lw_id '"' INTO lw_string.          "#EC NOTEXT
      FIND FIRST OCCURRENCE OF lw_string IN lw_file IGNORING CASE.
      IF sy-subrc NE 0.
        EXIT. "exit do
      ENDIF.
    ENDDO.

* Add relation
    lw_filename = lw_filename+5.
    CONCATENATE '<Relationship Target="'
                lw_filename
                '" Type="'
                lw_rel_string
                '" Id="'
                lw_id
                '"/>'
                '</Relationships>'
                INTO lw_string.
    REPLACE '</Relationships>' WITH lw_string INTO lw_file.

* Update relation file
    CALL METHOD _update_zip_file
      EXPORTING
        filename = 'word/_rels/document.xml.rels'
        content  = lw_file.

* Load icon
    CALL METHOD _load_image
      EXPORTING
        url = url_img
*      IMPORTING
*       imgres_x = lw_imgres_x
*       imgres_y = lw_imgres_y
      CHANGING
        id  = id_img.
    IF id_img IS INITIAL.
      invalid_image = c_true.
      RETURN.
    ENDIF.

* Search next id for some useless mandatory word attributes
    mw_attach = mw_attach + 1.
    lw_string = mw_attach.
    CONDENSE lw_string NO-GAPS.

* Everything fine, build xml fragment
    CONCATENATE '<w:r>'
                "lw_style
                '<w:object>'
                '<v:shape id="_x0000_'
                lw_string
                '" type="#_x0000_t75" o:ole="">'
                '<v:imagedata r:id="'
                id_img
                '" o:title=""/>'
                '</v:shape>'
                '<o:OLEObject Type="Embed" ProgID="Package" DrawAspect="Icon" ShapeID="_x0000_'
                lw_string
                '" ObjectID="_10000'
                lw_string
                '" r:id="'
                lw_id
                '"/>'
                '</w:object>'
                '</w:r>'
                INTO virtual RESPECTING BLANKS.

    IF as_paragraph = c_true.
* Add paragraphe style
      IF NOT style IS INITIAL OR NOT style_effect IS INITIAL.
        CALL METHOD _build_paragraph_style
          EXPORTING
            style         = style
            style_effect  = style_effect
          IMPORTING
            xml           = lw_style
            invalid_style = invalid_style.
      ENDIF.

      CONCATENATE '<w:p>'
                  lw_style
                  virtual
                  '</w:p>'
                  INTO virtual.
    ENDIF.

* Insert attachment in document
    IF NOT virtual IS SUPPLIED.
      IF as_paragraph = c_true.
        CONCATENATE mw_docxml
                    virtual
                    INTO mw_docxml.
      ELSE.
        CONCATENATE mw_fragxml
                    virtual
                    INTO mw_fragxml.
      ENDIF.
    ENDIF.
  ENDMETHOD. "insert_attachment


  METHOD insert_image.
    DATA : lw_string   TYPE string,
           lw_imgres_x TYPE i,
           lw_imgres_y TYPE i.
    DATA : lw_scalex    TYPE f,
           lw_scaley    TYPE f,
           lw_scale_max TYPE i,
           lw_scale     TYPE i,
           lw_x         TYPE i,
           lw_y         TYPE i,
           lw_x_string  TYPE string,
           lw_y_string  TYPE string,
           lw_style     TYPE string,
           lw_type_on   TYPE string,
           lw_type_in   TYPE string,
           lw_type_off  TYPE string,
           lw_posx      TYPE string,
           lw_posy      TYPE string,
           lw_margin    TYPE string.

    invalid_image = c_false.
    CALL METHOD _load_image
      EXPORTING
        url          = url
        file_content = file_content
      IMPORTING
        imgres_x     = lw_imgres_x
        imgres_y     = lw_imgres_y
      CHANGING
        id           = id.
    IF id IS INITIAL.
      invalid_image = c_true.
      RETURN.
    ENDIF.

* Calculate image scale
    IF ms_section-landscape = c_true.
* Max X in landscape : 8877300
* Max Y in landscape : 5743575 "could be less... depend of header/footer
      lw_scalex = 8877300 / lw_imgres_x.
      lw_scaley = 5762625 / lw_imgres_y.
    ELSE.
* Max X in portrait : 5762625
* Max Y in portrait : 8886825 "could be less... depend of header/footer
      lw_scalex = 5762625 / lw_imgres_x.
      lw_scaley = 8886825 / lw_imgres_y.
    ENDIF.
    IF lw_scalex < lw_scaley.
      lw_scale_max = floor( lw_scalex ).
    ELSE.
      lw_scale_max = floor( lw_scaley ).
    ENDIF.
* Image is smaler than page
    IF lw_scale_max GT 9525.
      lw_scale = 9525. "no zoom
      IF zoom IS SUPPLIED.
        lw_scale = lw_scale * zoom.
        IF lw_scale GT lw_scale_max.
          lw_scale = lw_scale_max.
        ENDIF.
      ENDIF.
    ELSE.
* Image is greater than page
      lw_scale = lw_scale_max.
      IF zoom IS SUPPLIED AND zoom < 1.
        lw_scale_max = 9525 * zoom.
        IF lw_scale_max LT lw_scale.
          lw_scale = lw_scale_max.
        ENDIF.
      ENDIF.
    ENDIF.
    lw_x = lw_imgres_x * lw_scale.
    lw_y = lw_imgres_y * lw_scale.
    lw_x_string = lw_x.
    CONDENSE lw_x_string NO-GAPS.
    lw_y_string = lw_y.
    CONDENSE lw_y_string NO-GAPS.

* Finally insert image in document
    ADD 1 TO mw_imgmaxid.
    lw_string = mw_imgmaxid.
    CONDENSE lw_string NO-GAPS.

    lw_margin = margin.
    CONDENSE lw_margin NO-GAPS.

    CASE type.
      WHEN c_imgtype_inline.
        CONCATENATE '<wp:inline distT="'
                    lw_margin
                    '" distB="'
                    lw_margin
                    '" distL="'
                    lw_margin
                    '" distR="'
                    lw_margin
                    '">'
                    INTO lw_type_on.
        lw_type_in = ''.
        lw_type_off = '</wp:inline>'.

      WHEN c_imgtype_float OR c_imgtype_over OR c_imgtype_behind.
        IF type = c_imgtype_behind.
          lw_type_on = '1'.
        ELSE.
          lw_type_on = '0'.
        ENDIF.
        IF posx CO '0123456789-'.
          CONCATENATE '<wp:posOffset>' posx '</wp:posOffset>' INTO lw_posx.
        ELSE.
          CONCATENATE '<wp:align>' posx '</wp:align>' INTO lw_posx.
        ENDIF.
        IF posy CO '0123456789-'.
          CONCATENATE '<wp:posOffset>' posy '</wp:posOffset>' INTO lw_posy.
        ELSE.
          CONCATENATE '<wp:align>' posy '</wp:align>' INTO lw_posy.
        ENDIF.

        CONCATENATE '<wp:anchor distT="'
                    lw_margin
                    '" distB="'
                    lw_margin
                    '" distL="'
                    lw_margin
                    '" distR="'
                    lw_margin
                    '" behindDoc="'
                    lw_type_on
                    '" allowOverlap="1"'
                    ' simplePos="0" locked="0" layoutInCell="1" relativeHeight="251658240" >'
                    '<wp:simplePos x="0" y="0"/>'
                    '<wp:positionH relativeFrom="column">'
                    lw_posx
                    '</wp:positionH>'
                    '<wp:positionV relativeFrom="paragraph">'
                    lw_posy
                    '</wp:positionV>'
                    INTO lw_type_on RESPECTING BLANKS.

        IF type = c_imgtype_float.
          lw_type_in = '<wp:wrapSquare wrapText="bothSides"/>'.
        ELSE.
          lw_type_in = '<wp:wrapNone/>'.
        ENDIF.

        lw_type_off = '</wp:anchor>'.
    ENDCASE.
* Prepare image insertion xml fragment
    CONCATENATE
    '<w:r>'
    '<w:drawing>'
    lw_type_on
    '<wp:extent cx="'
    lw_x_string
    '" cy="'
    lw_y_string
    '"/>'
    lw_type_in
    '<wp:docPr id="'
    lw_string "mw_imgmaxid
    '" name=""/>'
    '<wp:cNvGraphicFramePr/>'
    '<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">'
    '<a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture">'
    '<pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture">'
    '<pic:nvPicPr>'
    '<pic:cNvPr id="0" name=""/>'
    '<pic:cNvPicPr/>'
    '</pic:nvPicPr>'
    '<pic:blipFill>'
    '<a:blip r:embed="'
    id
    '"/>'
    '<a:stretch>'
    '<a:fillRect/>'
    '</a:stretch>'
    '</pic:blipFill>'
    '<pic:spPr>'
    '<a:xfrm>'
    '<a:off x="0" y="0"/>'
    '<a:ext cx="'
    lw_x_string
    '" cy="'
    lw_y_string
    '"/>'
    '</a:xfrm>'
    '<a:prstGeom prst="rect">'
    '<a:avLst/>'
    '</a:prstGeom>'
    '</pic:spPr>'
    '</pic:pic>'
    '</a:graphicData>'
    '</a:graphic>'
    lw_type_off
    '</w:drawing>'
    '</w:r>'
    INTO virtual.

    IF as_paragraph = c_true.
* Add paragraphe style
      IF NOT style IS INITIAL OR NOT style_effect IS INITIAL.
        CALL METHOD _build_paragraph_style
          EXPORTING
            style         = style
            style_effect  = style_effect
          IMPORTING
            xml           = lw_style
            invalid_style = invalid_style.
      ENDIF.

      CONCATENATE '<w:p>'
                  lw_style
                  virtual
                  '</w:p>'
                  INTO virtual.
    ENDIF.

* Insert image in document
    IF NOT virtual IS SUPPLIED.
      IF as_paragraph = c_true.
        CONCATENATE mw_docxml
                    virtual
                    INTO mw_docxml.
      ELSE.
        CONCATENATE mw_fragxml
                    virtual
                    INTO mw_fragxml.
      ENDIF.
    ENDIF.
  ENDMETHOD.                    "insert_image

  METHOD set_properties.
    DATA : lw_xml    TYPE string,
           lw_string TYPE string.

* Get prperties file
    CALL METHOD _get_zip_file
      EXPORTING
        filename = 'docProps/core.xml'
      IMPORTING
        content  = lw_xml.

    IF title IS SUPPLIED.
* Replace existing title by new one
      CALL METHOD _protect_string
        EXPORTING
          in  = title
        IMPORTING
          out = lw_string.
      CONCATENATE '<dc:title>'
                  lw_string
                  '</dc:title>'
                  INTO lw_string.
      REPLACE FIRST OCCURRENCE OF REGEX '<dc:title>(.*)</dc:title>' IN lw_xml WITH lw_string.
      IF sy-subrc NE 0.
        REPLACE FIRST OCCURRENCE OF REGEX '<dc:title\s*/>' IN lw_xml WITH lw_string.
      ENDIF.
* If no title property found, add it at end of xml
      IF sy-subrc NE 0.
        CONCATENATE lw_string
                    '</cp:coreProperties>'
                    INTO lw_string.
        REPLACE '</cp:coreProperties>'
                WITH lw_string
                INTO lw_xml.
      ENDIF.
    ENDIF.

    IF object IS SUPPLIED.
* Replace existing object by new one
      CALL METHOD _protect_string
        EXPORTING
          in  = object
        IMPORTING
          out = lw_string.
      CONCATENATE '<dc:subject>'
                  lw_string
                  '</dc:subject>'
                  INTO lw_string.
      REPLACE FIRST OCCURRENCE OF REGEX '<dc:subject>(.*)</dc:subject>' IN lw_xml WITH lw_string.
      IF sy-subrc NE 0.
        REPLACE FIRST OCCURRENCE OF REGEX '<dc:subject\s*/>' IN lw_xml WITH lw_string.
      ENDIF.
* If no object property found, add it at end of xml
      IF sy-subrc NE 0.
        CONCATENATE lw_string
                    '</cp:coreProperties>'
                    INTO lw_string.
        REPLACE '</cp:coreProperties>'
                WITH lw_string
                INTO lw_xml.
      ENDIF.
    ENDIF.

    IF author IS SUPPLIED.
* Replace existing author by new one
      CALL METHOD _protect_string
        EXPORTING
          in  = author
        IMPORTING
          out = mw_author.
      CONCATENATE '<dc:creator>'
                  mw_author
                  '</dc:creator>'
                  INTO lw_string.
      REPLACE FIRST OCCURRENCE OF REGEX '<dc:creator>(.*)</dc:creator>' IN lw_xml WITH lw_string.
      IF sy-subrc NE 0.
        REPLACE FIRST OCCURRENCE OF REGEX '<dc:creator\s*/>' IN lw_xml WITH lw_string.
      ENDIF.
* If no author property found, add it at end of xml
      IF sy-subrc NE 0.
        CONCATENATE lw_string
                    '</cp:coreProperties>'
                    INTO lw_string.
        REPLACE '</cp:coreProperties>'
                WITH lw_string
                INTO lw_xml.
      ENDIF.
* Set also last modified property
      CALL METHOD _protect_string
        EXPORTING
          in  = author
        IMPORTING
          out = lw_string.
      CONCATENATE '<cp:lastModifiedBy>'
                  lw_string
                  '</cp:lastModifiedBy>'
                  INTO lw_string.
      REPLACE FIRST OCCURRENCE OF REGEX '<cp:lastModifiedBy>(.*)</cp:lastModifiedBy>' IN lw_xml WITH lw_string.
      IF sy-subrc NE 0.
        REPLACE FIRST OCCURRENCE OF REGEX '<cp:lastModifiedBy\s*/>' IN lw_xml WITH lw_string.
      ENDIF.
* If no lastmodified property found, add it at end of xml
      IF sy-subrc NE 0.
        CONCATENATE lw_string
                    '</cp:coreProperties>'
                    INTO lw_string.
        REPLACE '</cp:coreProperties>'
                WITH lw_string
                INTO lw_xml.
      ENDIF.
    ENDIF.

    IF creationdate IS SUPPLIED.
* Replace existing creation date by new one
      CONCATENATE '<dcterms:created xsi:type="dcterms:W3CDTF">'
                  creationdate(4)
                  '-'
                  creationdate+4(2)
                  '-'
                  creationdate+6(2)
                  'T'
                  creationtime(2)
                  ':'
                  creationtime+2(2)
                  ':'
                  creationtime+2(2)
                  'Z'
                  '</dcterms:created>'
                  INTO lw_string.
      REPLACE FIRST OCCURRENCE OF REGEX '<dcterms:created(.*)</dcterms:created>' IN lw_xml WITH lw_string.
      IF sy-subrc NE 0.
        REPLACE FIRST OCCURRENCE OF REGEX '<dcterms:created\s*/>' IN lw_xml WITH lw_string.
      ENDIF.
* If no creation date property found, add it at end of xml
      IF sy-subrc NE 0.
        CONCATENATE lw_string
                    '</cp:coreProperties>'
                    INTO lw_string.
        REPLACE '</cp:coreProperties>'
                WITH lw_string
                INTO lw_xml.
      ENDIF.
* Replace also last modification date by new one
      CONCATENATE '<dcterms:modified xsi:type="dcterms:W3CDTF">'
                  creationdate(4)
                  '-'
                  creationdate+4(2)
                  '-'
                  creationdate+6(2)
                  'T'
                  creationtime(2)
                  ':'
                  creationtime+2(2)
                  ':'
                  creationtime+2(2)
                  'Z'
                  '</dcterms:modified>'
                  INTO lw_string.
      REPLACE FIRST OCCURRENCE OF REGEX '<dcterms:modified(.*)</dcterms:modified>' IN lw_xml WITH lw_string.
      IF sy-subrc NE 0.
        REPLACE FIRST OCCURRENCE OF REGEX '<dcterms:modified\s*/>' IN lw_xml WITH lw_string.
      ENDIF.
* If no modification date property found, add it at end of xml
      IF sy-subrc NE 0.
        CONCATENATE lw_string
                    '</cp:coreProperties>'
                    INTO lw_string.
        REPLACE '</cp:coreProperties>'
                WITH lw_string
                INTO lw_xml.
      ENDIF.
    ENDIF.

    IF description IS SUPPLIED.
* Replace existing description by new one
      CALL METHOD _protect_string
        EXPORTING
          in  = description
        IMPORTING
          out = lw_string.
      CONCATENATE '<dc:description>'
                  lw_string
                  '</dc:description>'
                  INTO lw_string.
      REPLACE FIRST OCCURRENCE OF REGEX '<dc:description>(.*)</dc:description>' IN lw_xml WITH lw_string.
      IF sy-subrc NE 0.
        REPLACE FIRST OCCURRENCE OF REGEX '<dc:description\s*/>' IN lw_xml WITH lw_string.
      ENDIF.
* If no description property found, add it at end of xml
      IF sy-subrc NE 0.
        CONCATENATE lw_string
                    '</cp:coreProperties>'
                    INTO lw_string.
        REPLACE '</cp:coreProperties>'
                WITH lw_string
                INTO lw_xml.
      ENDIF.
    ENDIF.

    IF category IS SUPPLIED.
* Replace existing category by new one
      CALL METHOD _protect_string
        EXPORTING
          in  = category
        IMPORTING
          out = lw_string.
      CONCATENATE '<cp:category>'
                  lw_string
                  '</cp:category>'
                  INTO lw_string.
      REPLACE FIRST OCCURRENCE OF REGEX '<cp:category>(.*)</cp:category>' IN lw_xml WITH lw_string.
      IF sy-subrc NE 0.
        REPLACE FIRST OCCURRENCE OF REGEX '<cp:category\s*/>' IN lw_xml WITH lw_string.
      ENDIF.
* If no category property found, add it at end of xml
      IF sy-subrc NE 0.
        CONCATENATE lw_string
                    '</cp:coreProperties>'
                    INTO lw_string.
        REPLACE '</cp:coreProperties>'
                WITH lw_string
                INTO lw_xml.
      ENDIF.
    ENDIF.

    IF keywords IS SUPPLIED.
* Replace existing keywords by new one
      CALL METHOD _protect_string
        EXPORTING
          in  = keywords
        IMPORTING
          out = lw_string.
      CONCATENATE '<cp:keywords>'
                  lw_string
                  '</cp:keywords>'
                  INTO lw_string.
      REPLACE FIRST OCCURRENCE OF REGEX '<cp:keywords>(.*)</cp:keywords>' IN lw_xml WITH lw_string.
      IF sy-subrc NE 0.
        REPLACE FIRST OCCURRENCE OF REGEX '<cp:keywords\s*/>' IN lw_xml WITH lw_string.
      ENDIF.
* If no keywords property found, add it at end of xml
      IF sy-subrc NE 0.
        CONCATENATE lw_string
                    '</cp:coreProperties>'
                    INTO lw_string.
        REPLACE '</cp:coreProperties>'
                WITH lw_string
                INTO lw_xml.
      ENDIF.
    ENDIF.

    IF status IS SUPPLIED.
* Replace existing status by new one
      CALL METHOD _protect_string
        EXPORTING
          in  = status
        IMPORTING
          out = lw_string.
      CONCATENATE '<cp:contentStatus>'
                  lw_string
                  '</cp:contentStatus>'
                  INTO lw_string.
      REPLACE FIRST OCCURRENCE OF REGEX '<cp:contentStatus>(.*)</cp:contentStatus>' IN lw_xml WITH lw_string.
      IF sy-subrc NE 0.
        REPLACE FIRST OCCURRENCE OF REGEX '<cp:contentStatus\s*/>' IN lw_xml WITH lw_string.
      ENDIF.
* If no status property found, add it at end of xml
      IF sy-subrc NE 0.
        CONCATENATE lw_string
                    '</cp:coreProperties>'
                    INTO lw_string.
        REPLACE '</cp:coreProperties>'
                WITH lw_string
                INTO lw_xml.
      ENDIF.
    ENDIF.

    IF revision IS SUPPLIED.
* Replace existing status by new one
      lw_string = revision.
      CONDENSE lw_string NO-GAPS.
      CONCATENATE '<cp:revision>'
                  lw_string
                  '</cp:revision>'
                  INTO lw_string.
      REPLACE FIRST OCCURRENCE OF REGEX '<cp:revision>(.*)</cp:revision>' IN lw_xml WITH lw_string.
      IF sy-subrc NE 0.
        REPLACE FIRST OCCURRENCE OF REGEX '<cp:revision\s*/>' IN lw_xml WITH lw_string.
      ENDIF.
* If no revision property found, add it at end of xml
      IF sy-subrc NE 0.
        CONCATENATE lw_string
                    '</cp:coreProperties>'
                    INTO lw_string.
        REPLACE '</cp:coreProperties>'
                WITH lw_string
                INTO lw_xml.
      ENDIF.
    ENDIF.

* Update properties file
    CALL METHOD _update_zip_file
      EXPORTING
        filename = 'docProps/core.xml'
        content  = lw_xml.

  ENDMETHOD.                    "set_properties

  METHOD set_params.
    DATA : lw_xml   TYPE string,
           lw_xmlns TYPE string.

* Define orientation
    IF orientation = c_orient_landscape.
      ms_section-landscape = c_true.
    ENDIF.

* Define Border
    IF border_left IS SUPPLIED.
      ms_section-border_left = border_left.
    ENDIF.
    IF border_top IS SUPPLIED.
      ms_section-border_top = border_top.
    ENDIF.
    IF border_right IS SUPPLIED.
      ms_section-border_right = border_right.
    ENDIF.
    IF border_bottom IS SUPPLIED.
      ms_section-border_bottom = border_bottom.
    ENDIF.

* Hide spellcheck for this document
    IF nospellcheck NE c_false.
      CALL METHOD _get_zip_file
        EXPORTING
          filename = 'word/settings.xml'
        IMPORTING
          content  = lw_xml.
* File doesnt exist ? create it !
      IF lw_xml IS INITIAL.
        CALL METHOD _get_xml_ns
          IMPORTING
            xml = lw_xmlns.
        CONCATENATE '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
                    '<w:settings '
                    lw_xmlns
                    '>'
                    '</w:settings>'
                    INTO lw_xml RESPECTING BLANKS.
      ENDIF.
      FIND 'hideSpellingErrors' IN lw_xml.
      IF sy-subrc NE 0.
        REPLACE FIRST OCCURRENCE OF '</w:settings>'
                IN lw_xml
                WITH '<w:hideSpellingErrors/></w:settings>'
                IGNORING CASE.
      ENDIF.
      FIND 'hideGrammaticalErrors' IN lw_xml.
      IF sy-subrc NE 0.
        REPLACE FIRST OCCURRENCE OF '</w:settings>'
                IN lw_xml
                WITH '<w:hideGrammaticalErrors/></w:settings>'
                IGNORING CASE.
      ENDIF.
      CALL METHOD _update_zip_file
        EXPORTING
          filename = 'word/settings.xml'
          content  = lw_xml.
    ENDIF.
  ENDMETHOD.                    "set_params

  METHOD save.
    DATA : lt_data_tab TYPE STANDARD TABLE OF x255,
           lw_lraw     TYPE x255,
           lw_docx     TYPE xstring,
           lw_xlen     TYPE i,
           lw_count    TYPE i,
           lw_off      TYPE i,
           lw_mod      TYPE i.

    CALL METHOD get_docx_file
      IMPORTING
        xcontent = lw_docx.

* Convert docx xString to xTable(255)
    REFRESH lt_data_tab.
    CLEAR   lw_off.
    lw_xlen  = xstrlen( lw_docx ).
    lw_count = lw_xlen DIV 255.
    DO lw_count TIMES.
      lw_lraw = lw_docx+lw_off(255).
      lw_off = lw_off + 255.
      APPEND lw_lraw TO lt_data_tab.
    ENDDO.
    lw_mod = lw_xlen MOD 255.
    IF lw_mod > 0.
      lw_lraw = lw_docx+lw_off(lw_mod).
      APPEND lw_lraw TO lt_data_tab.
    ENDIF.
    CLEAR lw_docx.

* Save document on server
    IF local = c_false.
      OPEN DATASET url FOR OUTPUT IN BINARY MODE.
      IF sy-subrc NE 0.
* Error opening the file
        MESSAGE 'Cannot create remote file' TYPE 'E'.
        RETURN.
      ENDIF.
      LOOP AT lt_data_tab INTO lw_lraw.
        IF lw_xlen > 255.
          lw_mod = 255.
        ELSE.
          lw_mod = lw_xlen.
        ENDIF.
        TRANSFER lw_lraw TO url LENGTH lw_mod.
        lw_xlen = lw_xlen - lw_mod.
      ENDLOOP.
      CLOSE DATASET url.

* Save document on local computer
    ELSEIF local = c_true.
      CALL METHOD cl_gui_frontend_services=>gui_download
        EXPORTING
          bin_filesize            = lw_xlen
          filename                = url
          filetype                = 'BIN'
          confirm_overwrite       = 'X'
        CHANGING
          data_tab                = lt_data_tab
        EXCEPTIONS
          file_write_error        = 1
          no_batch                = 2
          gui_refuse_filetransfer = 3
          invalid_type            = 4
          no_authority            = 5
          unknown_error           = 6
          header_not_allowed      = 7
          separator_not_allowed   = 8
          filesize_not_allowed    = 9
          header_too_long         = 10
          dp_error_create         = 11
          dp_error_send           = 12
          dp_error_write          = 13
          unknown_dp_error        = 14
          access_denied           = 15
          dp_out_of_memory        = 16
          disk_full               = 17
          dp_timeout              = 18
          file_not_found          = 19
          dataprovider_exception  = 20
          control_flush_error     = 21
          not_supported_by_gui    = 22
          error_no_gui            = 23
          OTHERS                  = 24.
* Error on save
      IF sy-subrc NE 0.
        MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno.
      ENDIF.
    ENDIF.

  ENDMETHOD.                    "save

  METHOD get_docx_file.
    DATA lw_xmlns TYPE string.

    CLEAR xcontent.

* Add final section info
    CALL METHOD _write_section.
    CONCATENATE mw_docxml mw_section_xml INTO mw_docxml.

    CALL METHOD _get_xml_ns
      IMPORTING
        xml = lw_xmlns.

* Add complete xml header
    CONCATENATE
    '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
    '<w:document '
    lw_xmlns
    '>'
    '<w:body>'
    mw_docxml
    '</w:body></w:document>'
    INTO mw_docxml RESPECTING BLANKS.

* Add custom body into docx
    CALL METHOD _update_zip_file
      EXPORTING
        filename = 'word/document.xml'
        content  = mw_docxml.

* Get final docx
    CALL METHOD mo_zip->save
      RECEIVING
        zip = xcontent.

  ENDMETHOD.                    "get_docx_file

  METHOD header_footer_direct_assign.
    invalid_header = c_false.
    invalid_footer = c_false.
    IF header IS SUPPLIED.
      sy-subrc = 0.
      IF NOT header IS INITIAL.
        READ TABLE mt_list_object WITH KEY type = c_type_header
                                           id = header
                                  TRANSPORTING NO FIELDS.
      ENDIF.
      IF sy-subrc = 0.
        ms_section-header = header.
      ELSE.
        invalid_header = c_true.
      ENDIF.
    ENDIF.
    IF footer IS SUPPLIED.
      sy-subrc = 0.
      IF NOT footer IS INITIAL.
        READ TABLE mt_list_object WITH KEY type = c_type_footer
                                           id = footer
                                  TRANSPORTING NO FIELDS.
      ENDIF.
      IF sy-subrc = 0.
        ms_section-footer = footer.
      ELSE.
        invalid_footer = c_true.
      ENDIF.
    ENDIF.
    IF header_first IS SUPPLIED.
      sy-subrc = 0.
      IF NOT header_first IS INITIAL.
        READ TABLE mt_list_object WITH KEY type = c_type_header
                                           id = header_first
                                  TRANSPORTING NO FIELDS.
      ENDIF.
      IF sy-subrc = 0.
        ms_section-header_first = header_first.
      ELSE.
        invalid_header = c_true.
      ENDIF.
    ENDIF.
    IF footer_first IS SUPPLIED.
      sy-subrc = 0.
      IF NOT footer_first IS INITIAL.
        READ TABLE mt_list_object WITH KEY type = c_type_footer
                                           id = footer_first
                                  TRANSPORTING NO FIELDS.
      ENDIF.
      IF sy-subrc = 0.
        ms_section-footer_first = footer_first.
      ELSE.
        invalid_footer = c_true.
      ENDIF.
    ENDIF.
  ENDMETHOD.                    "header_footer_direct_assign

  METHOD get_list_style.
    style_list[] = mt_list_style[].
  ENDMETHOD.                    "get_list_style

  METHOD get_list_image.
    DATA ls_list_object LIKE LINE OF mt_list_object.
    REFRESH image_list.
    LOOP AT mt_list_object INTO ls_list_object WHERE type = c_type_image.
      APPEND ls_list_object TO image_list.
    ENDLOOP.
  ENDMETHOD.                    "get_list_image

  METHOD get_list_headerfooter.
    DATA ls_list_object LIKE LINE OF mt_list_object.
    REFRESH headerfooter_list.
    LOOP AT mt_list_object INTO ls_list_object
         WHERE type = c_type_header OR type = c_type_footer.
      APPEND ls_list_object TO headerfooter_list.
    ENDLOOP.
  ENDMETHOD.                    "get_list_headerfooter

  METHOD insert_xml_fragment.
    CONCATENATE mw_fragxml xml INTO mw_fragxml.
  ENDMETHOD.                    "insert_xml_fragment

  METHOD insert_xml.
    CONCATENATE mw_docxml xml INTO mw_docxml.
  ENDMETHOD.                    "insert_xml

  METHOD _get_zip_file.
    DATA : lw_xmlx        TYPE xstring,
           lw_xmlx_length TYPE i,
           lt_xmlx        TYPE STANDARD TABLE OF x255.

* Get zipped file
    CALL METHOD mo_zip->get
      EXPORTING
        name    = filename
      IMPORTING
        content = lw_xmlx
      EXCEPTIONS
        OTHERS  = 2.

* Transform xstring to string in 2 steps
    CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
      EXPORTING
        buffer        = lw_xmlx
      IMPORTING
        output_length = lw_xmlx_length
      TABLES
        binary_tab    = lt_xmlx.

    CALL FUNCTION 'SCMS_BINARY_TO_STRING'
      EXPORTING
        input_length = lw_xmlx_length
      IMPORTING
        text_buffer  = content
      TABLES
        binary_tab   = lt_xmlx.

  ENDMETHOD.                    "_get_zip_file

  METHOD _update_zip_file.
    DATA : lw_docx TYPE xstring.

* File content string => xstring
    CALL FUNCTION 'SCMS_STRING_TO_XSTRING'
      EXPORTING
        text   = content
      IMPORTING
        buffer = lw_docx.

* If target file already exist, remove it
    READ TABLE mo_zip->files WITH KEY name = filename
               TRANSPORTING NO FIELDS.
    IF sy-subrc = 0.
      CALL METHOD mo_zip->delete
        EXPORTING
          name = filename.
    ENDIF.

* Add modified file into zip
    CALL METHOD mo_zip->add
      EXPORTING
        name    = filename
        content = lw_docx.

  ENDMETHOD.                    "_update_zip_file

  METHOD _load_file.
    DATA : lt_data_tab     TYPE STANDARD TABLE OF x255,
           lw_length       TYPE i,
           lw_url_begin(6) TYPE c.
    DATA : lt_query  TYPE TABLE OF w3query,
           ls_query  TYPE w3query,
           lt_html   TYPE TABLE OF w3html,
           lt_mime   TYPE TABLE OF w3mime,
           lw_return TYPE w3param-ret_code,
           lw_type   TYPE w3param-cont_type.

    CLEAR xcontent.
    lw_url_begin = filename.

* Load image
    IF lw_url_begin = c_sapwr_prefix.
* For SAPWR, read file from DB
      ls_query-name = '_OBJECT_ID'.
      ls_query-value = filename+6.
      APPEND ls_query TO lt_query.
      CALL FUNCTION 'WWW_GET_MIME_OBJECT'
        TABLES
          query_string   = lt_query
          html           = lt_html
          mime           = lt_mime
        CHANGING
          return_code    = lw_return
          content_type   = lw_type
          content_length = lw_length
        EXCEPTIONS
          OTHERS         = 3.
      IF sy-subrc NE 0.
        RETURN.
      ENDIF.

* Convert xchar table to xstring
      CALL FUNCTION 'SCMS_BINARY_TO_XSTRING'
        EXPORTING
          input_length = lw_length
        IMPORTING
          buffer       = xcontent
        TABLES
          binary_tab   = lt_mime
        EXCEPTIONS
          OTHERS       = 2.
    ELSE.
* For others, read file
      CALL METHOD cl_gui_frontend_services=>gui_upload
        EXPORTING
          filename   = filename
          filetype   = 'BIN'
        IMPORTING
          filelength = lw_length
        CHANGING
          data_tab   = lt_data_tab
        EXCEPTIONS
          OTHERS     = 19.
      IF sy-subrc NE 0.
        RETURN.
      ENDIF.
      CALL FUNCTION 'SCMS_BINARY_TO_XSTRING'
        EXPORTING
          input_length = lw_length
        IMPORTING
          buffer       = xcontent
        TABLES
          binary_tab   = lt_data_tab
        EXCEPTIONS
          OTHERS       = 2.
    ENDIF.
    IF sy-subrc NE 0.
      RETURN.
    ENDIF.
  ENDMETHOD.                    "_load_file

  METHOD _load_image.
    DATA : lw_filex        TYPE xstring,
           lw_filename     TYPE string,
           lw_string       TYPE string,
           lw_file         TYPE string,
           lw_url_begin(6) TYPE c,
           ls_list_object  LIKE LINE OF mt_list_object.

    CLEAR : extension, imgres_x, imgres_y.

* For existing image (ID given), just get img_res and extension.
    IF NOT id IS INITIAL.
      READ TABLE mt_list_object WITH KEY type = c_type_image
                                         id = id
                                INTO ls_list_object.
      IF sy-subrc NE 0.
        CLEAR id.
        RETURN.
      ENDIF.
      IF extension IS SUPPLIED.
        FIND ALL OCCURRENCES OF '.' IN ls_list_object-path
             MATCH OFFSET sy-fdpos.
        IF sy-subrc = 0.
          sy-fdpos = sy-fdpos + 1.
          extension = ls_list_object-path+sy-fdpos.
          TRANSLATE extension TO LOWER CASE.
          IF extension = 'jpeg'.
            extension = 'jpg'.
          ENDIF.
        ENDIF.
      ENDIF.
      IF imgres_x IS SUPPLIED OR imgres_y IS SUPPLIED.
        CALL METHOD mo_zip->get
          EXPORTING
            name    = ls_list_object-path
          IMPORTING
            content = lw_filex
          EXCEPTIONS
            OTHERS  = 2.
        IF sy-subrc = 0.
          CALL METHOD cl_fxs_image_info=>determine_info
            EXPORTING
              iv_data = lw_filex
            IMPORTING
              ev_xres = imgres_x
              ev_yres = imgres_y.
        ENDIF.
      ENDIF.
      RETURN.
    ENDIF.

* For new image, load image
    lw_url_begin = url.
    TRANSLATE lw_url_begin TO UPPER CASE.
* For image from sapwr, get file extension from DB
    IF lw_url_begin = c_sapwr_prefix.
      SELECT SINGLE value INTO extension
             FROM wwwparams
             WHERE relid = 'MI'
             AND objid = url+6
             AND name = 'fileextension'.
      IF NOT extension IS INITIAL AND extension(1) = '.'.
        extension = extension+1.
      ENDIF.
    ELSE.
* For other image, get file extension from filename
      FIND ALL OCCURRENCES OF '.' IN url MATCH OFFSET sy-fdpos.
      IF sy-subrc = 0.
        sy-fdpos = sy-fdpos + 1.
        extension = url+sy-fdpos.
        TRANSLATE extension TO LOWER CASE.
        IF extension = 'jpeg'.
          extension = 'jpg'.
        ENDIF.
      ENDIF.
    ENDIF.
* Cannot add image other than jpg/png/gif
    IF extension  NE 'jpg'
    AND extension NE 'png'
    AND extension NE 'gif'.
      RETURN.
    ENDIF.

* Load image
    IF file_content IS INITIAL.
      CALL METHOD _load_file
        EXPORTING
          filename = url
        IMPORTING
          xcontent = lw_filex.
      IF lw_filex IS INITIAL.
        RETURN.
      ENDIF.
    ELSE.
      lw_filex = file_content.
    ENDIF.

* Get image resolution
    IF imgres_x IS SUPPLIED OR imgres_y IS SUPPLIED.
      CALL METHOD cl_fxs_image_info=>determine_info
        EXPORTING
          iv_data = lw_filex
        IMPORTING
          ev_xres = imgres_x
          ev_yres = imgres_y.
    ENDIF.

* Search available image name
    DO.
      lw_filename = sy-index.
      CONDENSE lw_filename NO-GAPS.
      CONCATENATE 'word/media/image' lw_filename '.' extension
                  INTO lw_filename.
      READ TABLE mo_zip->files WITH KEY name = lw_filename
                 TRANSPORTING NO FIELDS.
      IF sy-subrc NE 0.
        EXIT. "exit do
      ENDIF.
    ENDDO.

* Add image in ZIP
    CALL METHOD mo_zip->add
      EXPORTING
        name    = lw_filename
        content = lw_filex.

* Get file extension list
    CALL METHOD _get_zip_file
      EXPORTING
        filename = '[Content_Types].xml'
      IMPORTING
        content  = lw_file.

* Search if file extension exist
    CONCATENATE 'extension="' extension '"' INTO lw_string.
    FIND FIRST OCCURRENCE OF lw_string IN lw_file IGNORING CASE.
    IF sy-subrc NE 0.
* If extension is not yet declared, it's time !
      CASE extension.
        WHEN 'jpg'.
          REPLACE '</Types>' WITH '<Default ContentType="image/jpeg" Extension="jpg"/></Types>'
                  INTO lw_file.
        WHEN 'png'.
          REPLACE '</Types>' WITH '<Default ContentType="image/png" Extension="png"/></Types>'
                  INTO lw_file.
        WHEN 'gif'.
          REPLACE '</Types>' WITH '<Default ContentType="image/gif" Extension="gif"/></Types>'
                  INTO lw_file.
      ENDCASE.

* Update file extension list
      CALL METHOD _update_zip_file
        EXPORTING
          filename = '[Content_Types].xml'
          content  = lw_file.
    ENDIF.

* Get relation file
    CALL METHOD _get_zip_file
      EXPORTING
        filename = 'word/_rels/document.xml.rels'
      IMPORTING
        content  = lw_file.

* Create Image ID
    DO.
      id = sy-index.
      CONDENSE id NO-GAPS.
      CONCATENATE 'rId' id INTO id.                         "#EC NOTEXT
      CONCATENATE 'Id="' id '"' INTO lw_string.             "#EC NOTEXT
      FIND FIRST OCCURRENCE OF lw_string IN lw_file IGNORING CASE.
      IF sy-subrc NE 0.
        EXIT. "exit do
      ENDIF.
    ENDDO.

* Update object list
    CLEAR ls_list_object.
    ls_list_object-id = id.
    ls_list_object-type = c_type_image.
    ls_list_object-path = lw_filename.
    APPEND ls_list_object TO mt_list_object.

* Add relation
    lw_filename = lw_filename+5.
    CONCATENATE '<Relationship Target="'
                lw_filename
                '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Id="'
                id
                '"/>'
                '</Relationships>'
                INTO lw_string.
    REPLACE '</Relationships>' WITH lw_string INTO lw_file.

* Update relation file
    CALL METHOD _update_zip_file
      EXPORTING
        filename = 'word/_rels/document.xml.rels'
        content  = lw_file.
  ENDMETHOD.                    "_load_image

  METHOD _create_note.
    DATA : lw_filename   TYPE string,
           lw_string     TYPE string,
           lw_file       TYPE string,
           lw_link_style TYPE string,
           lw_line_style TYPE string,
           lw_text       TYPE string,
           lw_xmlns      TYPE string,
           lw_id         TYPE string.

* Search if notes file exist
    IF type = c_notetype_foot.
      lw_filename = 'word/footnotes.xml'.
    ELSEIF type = c_notetype_end.
      lw_filename = 'word/endnotes.xml'.
    ELSE.
      RETURN.
    ENDIF.

    READ TABLE mo_zip->files WITH KEY name = lw_filename
               TRANSPORTING NO FIELDS.
    IF sy-subrc = 0.
* If foot/end notes exists, load the file
      CALL METHOD _get_zip_file
        EXPORTING
          filename = lw_filename
        IMPORTING
          content  = lw_file.
    ELSE.
* If footnotes doesnt exist, declare it and create it
* Add footnotes in content_types
      CALL METHOD _get_zip_file
        EXPORTING
          filename = '[Content_Types].xml'
        IMPORTING
          content  = lw_file.

      IF type = c_notetype_foot.
        CONCATENATE '<Override'
                    ' ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml"'
                    ' PartName="/word/footnotes.xml"/></Types>'
                    INTO lw_string RESPECTING BLANKS.
      ELSEIF type = c_notetype_end.
        CONCATENATE '<Override'
                    ' ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml"'
                    ' PartName="/word/endnotes.xml"/></Types>'
                    INTO lw_string RESPECTING BLANKS.
      ENDIF.
      REPLACE '</Types>' WITH lw_string
              INTO lw_file.

      CALL METHOD _update_zip_file
        EXPORTING
          filename = '[Content_Types].xml'
          content  = lw_file.

* Add footnotes in relation file
      CALL METHOD _get_zip_file
        EXPORTING
          filename = 'word/_rels/document.xml.rels'
        IMPORTING
          content  = lw_file.

* Create footnotes relation ID
      DO.
        lw_id = sy-index.
        CONDENSE lw_id NO-GAPS.
        CONCATENATE 'rId' lw_id INTO lw_id.                 "#EC NOTEXT
        CONCATENATE 'Id="' lw_id '"' INTO lw_string.        "#EC NOTEXT
        FIND FIRST OCCURRENCE OF lw_string IN lw_file IGNORING CASE.
        IF sy-subrc NE 0.
          EXIT. "exit do
        ENDIF.
      ENDDO.

* Add relation
      IF type = c_notetype_foot.
        CONCATENATE '<Relationship Target="footnotes.xml"'
                    ' Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes"'
                    ' Id="'
                    lw_id
                    '"/>'
                    '</Relationships>'
                    INTO lw_string RESPECTING BLANKS.
      ELSEIF type = c_notetype_end.
        CONCATENATE '<Relationship Target="endnotes.xml"'
                    ' Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/endnotes"'
                    ' Id="'
                    lw_id
                    '"/>'
                    '</Relationships>'
                    INTO lw_string RESPECTING BLANKS.
      ENDIF.
      REPLACE '</Relationships>' WITH lw_string INTO lw_file.

* Update relation file
      CALL METHOD _update_zip_file
        EXPORTING
          filename = 'word/_rels/document.xml.rels'
          content  = lw_file.

      CALL METHOD _get_xml_ns
        IMPORTING
          xml = lw_xmlns.

* Create notes file
      IF type = c_notetype_foot.
        CONCATENATE '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
                    '<w:footnotes '
                    lw_xmlns
                    '>'
                    '<w:footnote w:id="-1" w:type="separator">'
                    '<w:p>'
                    '<w:pPr><w:spacing w:lineRule="auto" w:line="240" w:after="0"/></w:pPr>'
                    '<w:r><w:separator/></w:r>'
                    '</w:p>'
                    '</w:footnote>'
                    '<w:footnote w:id="0" w:type="continuationSeparator">'
                    '<w:p>'
                    '<w:pPr><w:spacing w:lineRule="auto" w:line="240" w:after="0"/></w:pPr>'
                    '<w:r><w:continuationSeparator/></w:r>'
                    '</w:p>'
                    '</w:footnote>'
                    '</w:footnotes>'
                    INTO lw_file RESPECTING BLANKS.
      ELSEIF type = c_notetype_end.
        CONCATENATE '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
                    '<w:endnotes '
                    lw_xmlns
                    '>'
                    '<w:endnote w:id="-1" w:type="separator">'
                    '<w:p>'
                    '<w:pPr><w:spacing w:lineRule="auto" w:line="240" w:after="0"/></w:pPr>'
                    '<w:r><w:separator/></w:r>'
                    '</w:p>'
                    '</w:endnote>'
                    '<w:endnote w:id="0" w:type="continuationSeparator">'
                    '<w:p>'
                    '<w:pPr><w:spacing w:lineRule="auto" w:line="240" w:after="0"/></w:pPr>'
                    '<w:r><w:continuationSeparator/></w:r>'
                    '</w:p>'
                    '</w:endnote>'
                    '</w:endnotes>'
                    INTO lw_file RESPECTING BLANKS.
      ENDIF.
    ENDIF.

* Search available note id
    DO.
      id = sy-index.
      CONDENSE id NO-GAPS.
      CONCATENATE 'w:id="' id '"' INTO lw_string.           "#EC NOTEXT
      FIND FIRST OCCURRENCE OF lw_string IN lw_file IGNORING CASE.
      IF sy-subrc NE 0.
        EXIT. "exit do
      ENDIF.
    ENDDO.

* Add blank at start of note
    lw_text = text.
    IF lw_text IS INITIAL OR lw_text(1) NE space.
      CONCATENATE space lw_text INTO lw_text RESPECTING BLANKS.
    ENDIF.

    CALL METHOD write_text
      EXPORTING
        textline      = lw_text
        style_effect  = style_effect
        style         = style
      IMPORTING
        virtual       = lw_text
        invalid_style = invalid_style.

    IF NOT link_style_effect IS INITIAL OR NOT link_style IS INITIAL.
      CALL METHOD _build_character_style
        EXPORTING
          style        = link_style
          style_effect = link_style_effect
        IMPORTING
          xml          = lw_link_style.
    ENDIF.

    IF NOT line_style_effect IS INITIAL OR NOT line_style IS INITIAL.
      CALL METHOD _build_paragraph_style
        EXPORTING
          style         = line_style
          style_effect  = line_style_effect
        IMPORTING
          xml           = lw_line_style
          invalid_style = invalid_line_style.
    ENDIF.

* Add note
    IF type = c_notetype_foot.
      CONCATENATE '<w:footnote w:id="'
                  id
                  '">'
                  '<w:p>'
                  lw_line_style
                  '<w:r>'
                  lw_link_style
                  '<w:footnoteRef/>'
                  '</w:r>'
                  lw_text
                  '</w:p>'
                  '</w:footnote>'
                  '</w:footnotes>'
                  INTO lw_string RESPECTING BLANKS.
      REPLACE FIRST OCCURRENCE OF '</w:footnotes>' IN lw_file WITH lw_string.
    ELSEIF type = c_notetype_end.
      CONCATENATE '<w:endnote w:id="'
                  id
                  '">'
                  '<w:p>'
                  lw_line_style
                  '<w:r>'
                  lw_link_style
                  '<w:endnoteRef/>'
                  '</w:r>'
                  lw_text
                  '</w:p>'
                  '</w:endnote>'
                  '</w:endnotes>'
                  INTO lw_string RESPECTING BLANKS.
      REPLACE FIRST OCCURRENCE OF '</w:endnotes>' IN lw_file WITH lw_string.
    ENDIF.

* Update footnotes file
    CALL METHOD _update_zip_file
      EXPORTING
        filename = lw_filename
        content  = lw_file.

  ENDMETHOD.                    "_create_footnote

  METHOD _protect_string.
    out = in.
    REPLACE ALL OCCURRENCES OF '&' IN out WITH '&amp;'.
    REPLACE ALL OCCURRENCES OF '<' IN out WITH '&lt;'.
    REPLACE ALL OCCURRENCES OF '>' IN out WITH '&gt;'.
    REPLACE ALL OCCURRENCES OF '"' IN out WITH '&quot;'.
  ENDMETHOD.                    "_protect_string

  METHOD _protect_label.
    out = in.
    TRANSLATE out USING ' _'.
  ENDMETHOD.                    "_protect_label

  METHOD _build_character_style.
    DATA : lw_string   TYPE string,
           lw_char6(6) TYPE c,
           lw_intsize  TYPE i.

    CLEAR xml.

    IF style_effect IS SUPPLIED.
      IF NOT style_effect-color IS INITIAL.
        CONCATENATE xml
                    '<w:color w:val="'
                    style_effect-color
                    '"/>'
                    INTO xml.
      ENDIF.

      IF NOT style_effect-bgcolor IS INITIAL.
        CONCATENATE xml
                    '<w:shd w:val="clear" w:color="auto" w:fill="'
                    style_effect-bgcolor
                    '"/>'
                    INTO xml.
      ENDIF.

      IF style_effect-bold = c_true.
        CONCATENATE xml
                    '<w:b/>'
                    INTO xml.
      ENDIF.

      IF style_effect-italic = c_true.
        CONCATENATE xml
                    '<w:i/>'
                    INTO xml.
      ENDIF.

      IF style_effect-underline = c_true.
        CONCATENATE xml
                    '<w:u w:val="single"/>'
                    INTO xml.
      ENDIF.

      IF style_effect-strike = c_true.
        CONCATENATE xml
                    '<w:strike/>'
                    INTO xml.
      ENDIF.

      IF style_effect-caps = c_true.
        CONCATENATE xml
                    '<w:caps/>'
                    INTO xml.
      ENDIF.

      IF style_effect-smallcaps = c_true.
        CONCATENATE xml
                    '<w:smallCaps/>'
                    INTO xml.
      ENDIF.

      IF NOT style_effect-highlight IS INITIAL.
        CONCATENATE xml
                    '<w:highlight w:val="'
                    style_effect-highlight
                    '"/>'
                    INTO xml.
      ENDIF.

      IF NOT style_effect-spacing IS INITIAL AND style_effect-spacing CO '0123456789 -'.
        IF style_effect-spacing GT 0.
          lw_string = style_effect-spacing.
          CONDENSE lw_string NO-GAPS.
        ELSE.
          lw_string = - style_effect-spacing.
          CONDENSE lw_string NO-GAPS.
          CONCATENATE '-' lw_string INTO lw_string.
        ENDIF.
        CONCATENATE xml
                    '<w:spacing w:val="'
                    lw_string
                    '"/>'
                    INTO xml.
      ENDIF.

      IF style_effect-size IS NOT INITIAL.
        lw_intsize = style_effect-size * 2.
        lw_char6 = lw_intsize.
        CONDENSE lw_char6 NO-GAPS.
        CONCATENATE xml
                    '<w:sz w:val="'
                    lw_char6
                    '"/>'
                    '<w:szCs w:val="'
                    lw_char6
                    '"/>'
                    INTO xml.
      ENDIF.

      IF style_effect-sup = c_true.
        CONCATENATE xml
                    '<w:vertAlign w:val="superscript"/>'
                    INTO xml.
      ELSEIF style_effect-sub = c_true.
        CONCATENATE xml
                    '<w:vertAlign w:val="subscript"/>'
                    INTO xml.
      ENDIF.

      IF NOT style_effect-font IS INITIAL.
        CONCATENATE xml
                    '<w:rFonts w:ascii="'
                    style_effect-font
                    '" w:hAnsi="'
                    style_effect-font
                    '"/>'
                    INTO xml.
      ENDIF.
    ENDIF.

    IF style IS SUPPLIED AND style IS NOT INITIAL.
      READ TABLE mt_list_style WITH KEY type = c_type_character
                                        name = style
                               TRANSPORTING NO FIELDS.
      IF sy-subrc = 0.
        CONCATENATE xml
                    '<w:rStyle w:val="'
                    style
                    '"/>'
                    INTO xml.
      ELSE.
        invalid_style = c_true.
      ENDIF.
    ENDIF.

    IF NOT xml IS INITIAL.
      CONCATENATE '<w:rPr>'
                  xml
                  '</w:rPr>'
                  INTO xml.
    ENDIF.
  ENDMETHOD.                    "_build_character_style

  METHOD _build_paragraph_style.
    DATA : lw_substyle TYPE string,
           lw_size     TYPE string,
           lw_space    TYPE string,
           lw_indent   TYPE string.

    IF style IS SUPPLIED AND NOT style IS INITIAL.
      READ TABLE mt_list_style WITH KEY type = c_type_paragraph
                                        name = style
                               TRANSPORTING NO FIELDS.
      IF sy-subrc = 0.
        CONCATENATE xml
                    '<w:pStyle w:val="'
                    style
                    '"/>'
                    INTO xml.
      ELSE.
        invalid_style = c_true.
      ENDIF.
    ENDIF.

    IF style_effect-break_before = c_true.
      CONCATENATE xml
                  '<w:pageBreakBefore/>'
                  INTO xml.
    ENDIF.

    IF NOT style_effect-hierarchy_level IS INITIAL.
      lw_size = style_effect-hierarchy_level - 1.
      CONDENSE lw_size NO-GAPS.
      CONCATENATE xml
                  '<w:outlineLvl w:val="'
                  lw_size
                  '"/>'
                  INTO xml.
    ENDIF.

    IF NOT style_effect-alignment IS INITIAL.
      CONCATENATE xml
                  '<w:jc w:val="'
                  style_effect-alignment
                  '"/>'
                  INTO xml.
    ENDIF.

    IF NOT style_effect-bgcolor IS INITIAL.
      CONCATENATE xml
                  '<w:shd w:val="clear" w:color="auto" w:fill="'
                  style_effect-bgcolor
                  '"/>'
                  INTO xml.
    ENDIF.

    CLEAR lw_substyle.
    IF style_effect-spacing_before_auto = c_true.
      lw_substyle = ' w:beforeAutospacing="1"'.
    ELSEIF NOT style_effect-spacing_before IS INITIAL AND style_effect-spacing_before CO '0123456789 '.
      lw_space = style_effect-spacing_before.
      CONDENSE lw_space NO-GAPS.
      CONCATENATE lw_substyle
                  ' w:beforeAutospacing="0" w:before="'
                  lw_space
                  '"'
                  INTO lw_substyle RESPECTING BLANKS.
    ENDIF.

    IF style_effect-spacing_after_auto = c_true.
      CONCATENATE lw_substyle
                  ' w:afterAutospacing="1"'
                  INTO lw_substyle RESPECTING BLANKS.
    ELSEIF NOT style_effect-spacing_after IS INITIAL AND style_effect-spacing_after CO '0123456789 '.
      lw_space = style_effect-spacing_after.
      CONDENSE lw_space NO-GAPS.
      CONCATENATE lw_substyle
                  ' w:afterAutospacing="0" w:after="'
                  lw_space
                  '"'
                  INTO lw_substyle RESPECTING BLANKS.
    ENDIF.

    IF NOT style_effect-interline IS INITIAL.
      lw_space = style_effect-interline.
      CONDENSE lw_space NO-GAPS.
      CONCATENATE lw_substyle
                  ' w:line="'
                  lw_space
                  '"'
                  INTO lw_substyle RESPECTING BLANKS.
    ENDIF.

    IF NOT lw_substyle IS INITIAL.
      CONCATENATE xml
                  '<w:spacing '
                  lw_substyle
                  '/>'
                  INTO xml RESPECTING BLANKS.
    ENDIF.

    CLEAR lw_substyle.
    IF NOT style_effect-leftindent IS INITIAL AND style_effect-leftindent CO '0123456789 '.
      lw_indent = style_effect-leftindent.
      CONDENSE lw_indent NO-GAPS.
      CONCATENATE ' w:left="'
                  lw_indent
                  '"'
                  INTO lw_substyle RESPECTING BLANKS.
    ENDIF.

    IF NOT style_effect-rightindent IS INITIAL AND style_effect-rightindent CO '0123456789 '.
      lw_indent = style_effect-rightindent.
      CONDENSE lw_indent NO-GAPS.
      CONCATENATE lw_substyle
                  ' w:right="'
                  lw_indent
                  '"'
                  INTO lw_substyle RESPECTING BLANKS.
    ENDIF.

    IF NOT style_effect-firstindent IS INITIAL AND style_effect-firstindent CO '-0123456789 '.
      IF style_effect-firstindent < 0.
        lw_indent = - style_effect-firstindent.
        CONDENSE lw_indent NO-GAPS.
        CONCATENATE lw_substyle
                    ' w:hanging="'
                    lw_indent
                    '"'
                    INTO lw_substyle RESPECTING BLANKS.
      ELSE.
        lw_indent = style_effect-firstindent.
        CONDENSE lw_indent NO-GAPS.
        CONCATENATE lw_substyle
                    ' w:firstLine="'
                    lw_indent
                    '"'
                    INTO lw_substyle RESPECTING BLANKS.
      ENDIF.
    ENDIF.

    IF NOT lw_substyle IS INITIAL.
      CONCATENATE xml
                  '<w:ind '
                  lw_substyle
                  '/>'
                  INTO xml RESPECTING BLANKS.
    ENDIF.

* Borders
    CLEAR lw_substyle.
    IF NOT style_effect-border_left-style IS INITIAL
    AND NOT style_effect-border_left-width IS INITIAL.
      lw_size = style_effect-border_left-width.
      CONDENSE lw_size NO-GAPS.
      lw_space = style_effect-border_left-space.
      CONDENSE lw_space NO-GAPS.
      CONCATENATE lw_substyle
                  '<w:left w:val="'
                  style_effect-border_left-style
                  '" w:sz="'
                  lw_size
                  '" w:space="'
                  lw_space
                  '" w:color="'
                  style_effect-border_left-color
                  '"/>'
                  INTO lw_substyle RESPECTING BLANKS.
    ENDIF.
    IF NOT style_effect-border_top-style IS INITIAL
    AND NOT style_effect-border_top-width IS INITIAL.
      lw_size = style_effect-border_top-width.
      CONDENSE lw_size NO-GAPS.
      lw_space = style_effect-border_top-space.
      CONDENSE lw_space NO-GAPS.
      CONCATENATE lw_substyle
                  '<w:top w:val="'
                  style_effect-border_top-style
                  '" w:sz="'
                  lw_size
                  '" w:space="'
                  lw_space
                  '" w:color="'
                  style_effect-border_top-color
                  '"/>'
                  INTO lw_substyle RESPECTING BLANKS.
    ENDIF.
    IF NOT style_effect-border_right-style IS INITIAL
    AND NOT style_effect-border_right-width IS INITIAL.
      lw_size = style_effect-border_right-width.
      CONDENSE lw_size NO-GAPS.
      lw_space = style_effect-border_right-space.
      CONDENSE lw_space NO-GAPS.
      CONCATENATE lw_substyle
                  '<w:right w:val="'
                  style_effect-border_right-style
                  '" w:sz="'
                  lw_size
                  '" w:space="'
                  lw_space
                  '" w:color="'
                  style_effect-border_right-color
                  '"/>'
                  INTO lw_substyle RESPECTING BLANKS.
    ENDIF.
    IF NOT style_effect-border_bottom-style IS INITIAL
    AND NOT style_effect-border_bottom-width IS INITIAL.
      lw_size = style_effect-border_bottom-width.
      CONDENSE lw_size NO-GAPS.
      lw_space = style_effect-border_bottom-space.
      CONDENSE lw_space NO-GAPS.
      CONCATENATE lw_substyle
                  '<w:bottom w:val="'
                  style_effect-border_bottom-style
                  '" w:sz="'
                  lw_size
                  '" w:space="'
                  lw_space
                  '" w:color="'
                  style_effect-border_bottom-color
                  '"/>'
                  INTO lw_substyle RESPECTING BLANKS.
    ENDIF.
    IF NOT lw_substyle IS INITIAL.
      CONCATENATE xml
                  '<w:pBdr>'
                  lw_substyle
                  '</w:pBdr>'
                  INTO xml RESPECTING BLANKS.
    ENDIF.
* Add section info if required
    IF NOT mw_section_xml IS INITIAL.
      CONCATENATE xml
                  mw_section_xml
                  INTO xml.
      CLEAR mw_section_xml.
    ENDIF.

    IF NOT xml IS INITIAL.
      CONCATENATE '<w:pPr>'
                  xml
                  '</w:pPr>'
                  INTO xml.
    ENDIF.

  ENDMETHOD.                    "_build_paragraph_style

  METHOD _get_xml_ns.
    CLEAR xml.
    CONCATENATE
                ' xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas"'
                ' xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"'
                ' xmlns:o="urn:schemas-microsoft-com:office:office"'
                ' xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"'
                ' xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math"'
                ' xmlns:v="urn:schemas-microsoft-com:vml"'
                ' xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing"'
                ' xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"'
                ' xmlns:w10="urn:schemas-microsoft-com:office:word"'
                ' xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"'
                ' xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml"'
                ' xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup"'
                ' xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk"'
                ' xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml"'
                ' xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape"'
                ' mc:Ignorable="w14 wp14" '                 "#EC NOTEXT
                INTO xml RESPECTING BLANKS.
  ENDMETHOD.                    "_get_xml_ns
ENDCLASS.                    "cl_word IMPLEMENTATION

Program ZDEMO_DOCX: Test Creation docx

ABAP Code

**----------------------------------------------------------------------*
* Demo program to show how to use the class CL_WORD (DOCX version)
* Author  : S. Hermann
* Date    : 27.11.2015
*---------------------------------------------------------------------*
* Generate DOCX document
* Strongly recommanded to use template because empty sap docx document
* dont have any style embed
*
* To update toc & custom fields, you have to update manually, or add a
* macro in your template :
* Private Sub Document_Open()
*   ActiveDocument.Fields.Update
* End Sub
*---------------------------------------------------------------------*
* Enhancement list (TODO) :
* - Create table style
* - Manage bullets
*---------------------------------------------------------------------*

REPORT  zdemo_docx.

include zcl_word.

START-OF-SELECTION.
  DATA : o_doc TYPE REF TO cl_word,
         s_char_style TYPE cl_word=>ty_character_style_effect,
         s_para_style TYPE cl_word=>ty_paragraph_style_effect.

  CREATE OBJECT o_doc
    EXPORTING
      tpl = 'C:\test_word_ole\template.dotx'
    .      "keep_tpl_content = cl_word=>c_true

  CALL METHOD o_doc->write_text
    EXPORTING
      textline   = 'Sample usage of CL_WORD'
      line_style = 'Titre'. "cl_word=>c_style_title.

  CALL METHOD o_doc->write_text
    EXPORTING
      textline   = 'Write text and apply style effect'
      line_style = 'Titre1'. "cl_word=>c_style_title1.

  CALL METHOD o_doc->write_text
    EXPORTING
      textline   = 'There is a simple text paragraph with the "Normal" style applied, without any special effect.'
      line_style = cl_word=>c_style_normal.

  CLEAR s_char_style.
  s_char_style-color = cl_word=>c_color_blue.
  CALL METHOD o_doc->write_text
    EXPORTING
      textline     = 'Now there is a text, written in blue.'
      style_effect = s_char_style.

  CALL METHOD o_doc->write_note
    EXPORTING
      text = 'Please note that the blue is not red'.

  CLEAR s_char_style.
  s_char_style-color = cl_word=>c_color_black.
  CALL METHOD o_doc->write_text
    EXPORTING
      textline     = 'And another in black. Note that without the line_style parameter, all texts will be in the same paragraph.'
      style_effect = s_char_style.

  CALL METHOD o_doc->write_comment
    EXPORTING
      text = 'this is a comment'
      initials = 'SHN'.

* You could close mannually paragraph with write_line
  CALL METHOD o_doc->write_line.

  CLEAR s_char_style.
  s_char_style-color = cl_word=>c_color_green.
  CALL METHOD o_doc->write_text
    EXPORTING
      textline     = 'Now there is a text, written in green.'
      style_effect = s_char_style.

  CALL METHOD o_doc->write_note
    EXPORTING
      text = 'And the green is not yellow'.

  CALL METHOD o_doc->write_text
    EXPORTING
      textline = 'Text go back to default color if no color specified mannually'.
  CALL METHOD o_doc->write_line.

  CALL METHOD o_doc->write_text
    EXPORTING
      textline   = 'Now write a paragraph directly from write_text (line_style)'
      line_style = cl_word=>c_style_normal.


  CALL METHOD o_doc->write_text
    EXPORTING
      textline = 'You could play with:'.

  CLEAR s_char_style.
  s_char_style-italic = cl_word=>c_true.
  CALL METHOD o_doc->write_text
    EXPORTING
      textline     = 'italic,'
      style_effect = s_char_style.

  CLEAR s_char_style.
  s_char_style-bold = cl_word=>c_true.
  CALL METHOD o_doc->write_text
    EXPORTING
      textline     = 'bold,'
      style_effect = s_char_style.

  CLEAR s_char_style.
  s_char_style-underline = cl_word=>c_true.
  CALL METHOD o_doc->write_text
    EXPORTING
      textline     = 'underline.'
      style_effect = s_char_style.

  CALL METHOD o_doc->write_line
    EXPORTING
      style = cl_word=>c_style_normal.

  CALL METHOD o_doc->write_text
    EXPORTING
      textline = 'You can add line break'.

* Add a break line
  o_doc->write_break( ).

  CLEAR s_char_style.
  s_char_style-size = 18.
  s_char_style-font = cl_word=>c_font_comic.
  CALL METHOD o_doc->write_text
    EXPORTING
      textline     = 'Or play also with font name or font size'
      style_effect = s_char_style.

  CALL METHOD o_doc->write_line.

  CALL METHOD o_doc->write_text
    EXPORTING
      textline   = 'Custom field'
      line_style = 'Titre2'. "cl_word=>c_style_title2.

  o_doc->write_text( 'You could add custom field : ' ).
  o_doc->insert_custom_field( 'Billy_the_Kid' ).
  o_doc->write_text( '. Field value could be define later in the program process' ).
  o_doc->write_line( ).

* Insert a page break
  o_doc->write_break( breaktype = cl_word=>c_breaktype_page ).

* You could use header/footer embbed in template
  CALL METHOD o_doc->header_footer_direct_assign
    EXPORTING
      header       = 'rId8'
      header_first = 'rId11'
      footer       = 'rId10'
      footer_first = 'rId12'.

* Now add table
  CALL METHOD o_doc->write_text
    EXPORTING
      textline   = 'Table section'
      line_style = 'Titre1'. "cl_word=>c_style_title1.

  CALL METHOD o_doc->write_text
    EXPORTING
      textline   = 'A styled table (defined in the template)'
      line_style = 'Titre2'. "cl_word=>c_style_title2.


  DATA : BEGIN OF ls_carac,
         f1(80) TYPE c,
         f2(80) TYPE c,
       END OF ls_carac,
       lt_carac LIKE TABLE OF ls_carac.

  ls_carac-f1 = 'Header col 1'.
  ls_carac-f2 = 'Header col 2'.
  APPEND ls_carac TO lt_carac.

  ls_carac-f1 = 'C1'.
  ls_carac-f2 = 'V1'.
  APPEND ls_carac TO lt_carac.

  ls_carac-f1 = 'C2'.
  ls_carac-f2 = 'V2'.
  APPEND ls_carac TO lt_carac.

  ls_carac-f1 = 'C3'.
  ls_carac-f2 = 'V3'.
  APPEND ls_carac TO lt_carac.

  CALL METHOD o_doc->write_table
    EXPORTING
      content = lt_carac
      style   = 'Trameclaire-Accent1'.

  CALL METHOD o_doc->write_text
    EXPORTING
      textline   = 'Basic table with style effect inside'
      line_style = cl_word=>c_style_title2.


  DATA : BEGIN OF ls_cle,
           f1 TYPE cl_word=>ty_table_style_field,
           f2 TYPE cl_word=>ty_table_style_field,
         END OF ls_cle,
         lt_cle LIKE TABLE OF ls_cle.

  CLEAR ls_cle.
  ls_cle-f1-textline = 'blue'.
  ls_cle-f1-bgcolor = cl_word=>c_color_blue.
  ls_cle-f1-style_effect-bold = cl_word=>c_true.
  ls_cle-f1-line_style_effect-alignment = cl_word=>c_align_left.
  ls_cle-f1-valign = cl_word=>c_valign_top.
  ls_cle-f2-textline = 'turquoise'.
  ls_cle-f2-bgcolor = cl_word=>c_color_turquoise.
  APPEND ls_cle TO lt_cle.
  CLEAR ls_cle.
  ls_cle-f1-textline = 'brightgreen'.
  ls_cle-f1-bgcolor = cl_word=>c_color_brightgreen.
  ls_cle-f1-style_effect-italic = cl_word=>c_true.
  ls_cle-f1-line_style_effect-alignment = cl_word=>c_align_center.
  ls_cle-f1-valign = cl_word=>c_valign_middle.
  ls_cle-f2-style_effect-underline = cl_word=>c_true.
  ls_cle-f2-textline = 'pink'.
  ls_cle-f2-bgcolor = cl_word=>c_color_pink.
  APPEND ls_cle TO lt_cle.
  CLEAR ls_cle.
  ls_cle-f1-textline = 'red'.
  ls_cle-f1-bgcolor = cl_word=>c_color_red.
  ls_cle-f1-line_style_effect-alignment = cl_word=>c_align_right.
  ls_cle-f1-valign = cl_word=>c_valign_bottom.
  ls_cle-f2-style_effect-bold = cl_word=>c_true.
  ls_cle-f2-textline = 'yellow'.
  ls_cle-f2-bgcolor = cl_word=>c_color_yellow.
  APPEND ls_cle TO lt_cle.
  CLEAR ls_cle.
  ls_cle-f1-textline = 'darkblue'.
  ls_cle-f1-bgcolor = cl_word=>c_color_darkblue.
  ls_cle-f1-style_effect-bold = cl_word=>c_true.
  ls_cle-f1-line_style_effect-alignment = cl_word=>c_align_justify.
  ls_cle-f2-textline = 'teal'.
  ls_cle-f2-bgcolor = cl_word=>c_color_teal.
  APPEND ls_cle TO lt_cle.
  CLEAR ls_cle.
  ls_cle-f1-textline = 'green'.
  ls_cle-f1-bgcolor = cl_word=>c_color_green.
  ls_cle-f1-style_effect-size = 40.
  ls_cle-f2-style_effect-bold = cl_word=>c_true.
  ls_cle-f2-textline = 'violet'.
  ls_cle-f2-bgcolor = cl_word=>c_color_violet.
  APPEND ls_cle TO lt_cle.
  CLEAR ls_cle.
  ls_cle-f1-textline = 'darkred'.
  ls_cle-f1-bgcolor = cl_word=>c_color_darkred.
  ls_cle-f1-style_effect-color = cl_word=>c_color_yellow.
  ls_cle-f2-style_effect-bold = cl_word=>c_true.
  ls_cle-f2-textline = 'darkyellow'.
  ls_cle-f2-bgcolor = cl_word=>c_color_darkyellow.
  ls_cle-f2-style_effect-font = cl_word=>c_font_comic.
  APPEND ls_cle TO lt_cle.
  CLEAR ls_cle.
  ls_cle-f1-textline = 'gray'.
  ls_cle-f1-bgcolor = cl_word=>c_color_gray.
  ls_cle-f1-style_effect-bold = cl_word=>c_true.
  ls_cle-f2-style_effect-bold = cl_word=>c_true.
  ls_cle-f2-textline = 'lightgray'.
  ls_cle-f2-bgcolor = cl_word=>c_color_lightgray.
  APPEND ls_cle TO lt_cle.
  CLEAR ls_cle.
  ls_cle-f1-textline = '2 merged cells'.
  ls_cle-f1-merge = 2.
  APPEND ls_cle TO lt_cle.
  CALL METHOD o_doc->write_table
    EXPORTING
      content  = lt_cle
      tblwidth = 9300.


* Insert a page break
  o_doc->write_break( breaktype = cl_word=>c_breaktype_page ).

  CALL METHOD o_doc->write_text
    EXPORTING
      textline   = 'Insert image'
      line_style = 'Titre2'. "cl_word=>c_style_title2.

  CALL METHOD o_doc->insert_image( url = 'C:\test_word_ole\image1.jpg' ).

* Insert a page break
  o_doc->write_break( breaktype = cl_word=>c_breaktype_page ).

  CALL METHOD o_doc->write_text
    EXPORTING
      textline   = 'Write table of content'
      line_style = 'Titre1'. "cl_word=>c_style_title1.

  CALL METHOD o_doc->write_toc.


clear s_char_style.
s_char_style-label = 'labelled content'.
  CALL METHOD o_doc->write_text
    EXPORTING
      textline   = 'Label ##LABEL##: This is a label'
      style_effect = s_char_style
      line_style = 'Normal'.

  CALL METHOD o_doc->write_text
    EXPORTING
      textline   = 'Label ##LABEL##: This is another'
      style_effect = s_char_style
      line_style = 'Normal'.

* Insert a page break
  o_doc->write_break( breaktype = cl_word=>c_breaktype_page ).

  CALL METHOD o_doc->write_text
    EXPORTING
      textline   = 'Write table of content for label "labelled content"'
      line_style = 'Titre1'. "cl_word=>c_style_title1.

  CALL METHOD o_doc->write_toc
    exporting
      label = 'labelled content'.


* Here we define the value of the custom field inserted before !
  o_doc->create_custom_field( field = 'Billy_the_Kid' value = 'Billy was my hero' ).


* To change orientation of a part of the doc (and not of the entire doc)
* You have to insert a break type "section"
  o_doc->write_break( breaktype = cl_word=>c_breaktype_section ).

* After the break, you could change the orientation
  CALL METHOD o_doc->set_params
    EXPORTING
      orientation = cl_word=>c_orient_landscape.

  CALL METHOD o_doc->write_text
    EXPORTING
      textline = 'Canvas and landscape'.

  CALL METHOD o_doc->write_note
    EXPORTING
      text = 'An endnote on a title'
      type = cl_word=>c_notetype_end.

  CALL METHOD o_doc->write_line
    EXPORTING
      style = 'Titre1'. "cl_word=>c_style_title1.

* Draw the "paperboard" where you will draw some objects
  CALL METHOD o_doc->draw_init
    EXPORTING
      left   = 0
      top    = 0
      width  = 685
      height = 350.

* Draw rectangles...
  CALL METHOD o_doc->draw
    EXPORTING
      object = cl_word=>c_draw_rectangle
      left   = 436
      top    = 45
      width  = 47
      height = 12.

  CALL METHOD o_doc->draw
    EXPORTING
      object = cl_word=>c_draw_rectangle
      left   = 40
      top    = 25
      width  = 47
      height = 12.

* Draw images...
  CALL METHOD o_doc->draw
    EXPORTING
      object = cl_word=>c_draw_image
      url    = 'C:\test_word_ole\image2.jpg'
      left   = 230
      top    = 70
      width  = 47
      height = 12.

  CALL METHOD o_doc->draw
    EXPORTING
      object = cl_word=>c_draw_image
      url    = 'C:\test_word_ole\image3.png'
      left   = 20
      top    = 50
      width  = 600
      height = 300.

* Mandatory method at end of draw
  CALL METHOD o_doc->draw_finalize.

* Switch back to portrait
  o_doc->write_break( breaktype = cl_word=>c_breaktype_section ).
  CALL METHOD o_doc->set_params
    EXPORTING
      orientation = cl_word=>c_orient_portrait.

  CALL METHOD o_doc->write_text
    EXPORTING
      textline   = 'Table with image inserted'
      line_style = 'Titre1'. "cl_word=>c_style_title1.

refresh lt_cle.
clear ls_cle.
  ls_cle-f1-textline = 'The second column contain the previously used image called "Variant"'.
  ls_cle-f2-textline = ''.
  ls_cle-f2-image_id = 'rId17'.
  APPEND ls_cle TO lt_cle.
  CALL METHOD o_doc->write_table
    EXPORTING
      content  = lt_cle.

  CALL METHOD o_doc->write_text
    EXPORTING
      textline   = 'End of the doc'
      line_style = 'Titre1'. "cl_word=>c_style_title1.


* You could save the doc
  o_doc->save( 'C:\test_word_ole\test_doc.docx' ).