cancel
Showing results for 
Search instead for 
Did you mean: 

Office control ms_word - runmacro, calling a macro with parameters

radek_pavlu
Explorer
0 Kudos

How can I call word macro with parameters?

I have ms word document with macro, for example:

Sub Makro1(ByVal bookmark As String, ByVal text As String)

'

' Makro1 Makro

'

Selection.GoTo What:=wdGoToBookmark, Name:=bookmark

Selection.TypeText text:=text

ActiveDocument.Bookmarks(bookmark).Delete

.

.

.

End Sub

I try call this macro from webdynpro:

TRY.

CALL METHOD

wd_this->document->IF_IOS_APPLICATIONPROPERTIES~RUNMACRO

EXPORTING

NAME = 'Makro1("z1","test")'

ERRORINFORMATION = co_error_getcontent_stru

  • IMPORTING

  • RESULT =

.

CATCH CX_IOS_APPLICATIONPROPERTIES .

CATCH CX_IOS_COMMUNICATIONWRAPPER .

CATCH CX_IOS_ENVIRONMENT .

ENDTRY.

This macro cannot call. )

Calling a macro without parameters is ok.

Sub Makro2()

Call Makro1("z1", "test")

End Sub

CALL METHOD

wd_this->document->IF_IOS_APPLICATIONPROPERTIES~RUNMACRO

EXPORTING

NAME = 'Makro2'

ERRORINFORMATION = co_error_getcontent_stru

  • IMPORTING

  • RESULT =

Greats Radek

Accepted Solutions (0)

Answers (1)

Answers (1)

Former Member
0 Kudos

Hi Radek, sorry for reopening an old post, but have you found any documentation regarding the macro name parameter? It looks it accepts some sort of special figures:

e.g. '.macroname" looks for the macro in user's default template location.

Is there anything the DocumentName attribute of the office control does with this parameter?

radek_pavlu
Explorer
0 Kudos

Hi Jozef, I not found any documentation. I solved this problem by using format DOCX. DOCX is XML format. http://msdn.microsoft.com/en-us/library/documentformat.openxml.wordprocessing(v=office.14).aspx

Former Member
0 Kudos

Thanks for reply. We are using .DOTX (in fact it does not matter if .docx or .dotx is used). But we could not manage how to create the document, show it to the user, enable him to make changes etc.

Our scenario is pretty complicated, as we need to fill many parts of it dynamically (tables, formatting, CR_LF's, line-feeds, tabulators...).

Once you did find a way, what exactly did you do? Did you find a simple way to fill .docx?

radek_pavlu
Explorer
0 Kudos

Our scenario is:

1. Read template from BDS. In template .docx are empty bookmarks.

2. Fill bookmarks with data, all in xml. We can fill text, font, color, formating, tabs, line-feeds, word tables, etc. All is in abap, xml.

3. Filled docx display in abap webdynpro with officecontrol. User can edit this document.

4. Get the edited document from officecontrol after click at Save in our WD application.

5. Check if some bookmarks have not changed values. If all is ok, store edited document in CRM.

Former Member
0 Kudos

Thank you very much for the explanation. But I need to ask you how do you parse and edit .docx as a plain xml file in ABAP?

An .docx is a zipped file consisting of many xml files. So to be able to do what you wrote, you will first need to:

1. download the file from some location (I believe it does not matter whether the file is in the MIME repository = my case, in BDS = your case, some content server, stored using GOS or whatever). You always will get the binary. Correct?

2. unzip the file (which you only can do using a third-party tool, installed on the server, or is there another way?). By unzipping, you will get a couple of files, that you need to store somewhere (the application server or whatever). Correct?

3. then you have got some files, which you can parse and look for the bookmarks, fill your text, be it whatever needed. I'm not very at-home about the whole structure of the xml-s belonging to .docx, but probably you can use a bookmark to fill "real data" that will be shown.

4. store all the files back to a place, where you are able to call another system-command to zip the files to the .docx file type. Correct?

5. upload where needed (once you again can have the binary, it should be possible to store it anywhere).

So please, is this your approach? Or did you find a simplier way of doing this? Unfortunately, I'm on a ECC 6.0 with not the most actual support packs without the possibility to do anything about it .

radek_pavlu
Explorer
0 Kudos

1. Yes. Download from some location as binary file.

2. Unzip by class CL_ABAP_ZIP. For filling bookmarks i need file 'word/document.xml'.

  CL_ABAP_ZIP->GET

Unzipped files are only in memory.

3. For parsing and filling bookmarks i use classes and interfaces cl_ixml, if_ixml*

* create a DOM from the xstring
  lr_ixml = cl_ixml=>create( ).
  lr_stream_factory = lr_ixml->create_stream_factory( ).
  lr_istream  = lr_stream_factory->create_istream_xstring( iv_docx ).
  lr_document = lr_ixml->create_document( ).
  gr_document = lr_document.
  lr_parser = lr_ixml->create_parser( document       = lr_document
                                      istream        = lr_istream
                                      stream_factory = lr_stream_factory ).
  CALL METHOD LR_PARSER->ADD_PRESERVE_SPACE_ELEMENT
    RECEIVING
      RVAL   = lv_rval.
  IF lv_rval <> abap_true.
* Err in parser XML
  ENDIF.
CALL METHOD LR_PARSER->SET_XML_SPACE_AWARE.
IF lv_rval <> abap_true.
* Err in parser XML

ENDIF.

* Finding and filling bookmarks
  lr_root     = gr_document->get_root_element( ).
* All elements "w:bookmarkStart"
  lr_elements = LR_ROOT->GET_ELEMENTS_BY_TAG_NAME(
*      DEPTH     = 0
      NAME      = 'bookmarkStart'
      NAMESPACE = 'w'
  ).
  lr_itr_bookmarkStart = lr_elements->create_iterator( ).
  DO.
    lr_node = lr_itr_bookmarkStart->get_next( ).
    IF lr_node IS INITIAL. EXIT. ENDIF.
    lr_attributes = lr_node->get_attributes( ).
* Read attribute "w:name" of element "w:bookmarkStart"
    lr_attribute = lr_attributes->get_named_item(
      NAME      = 'name'
      NAMESPACE = 'w'
    ).
    lv_name = lr_attribute->get_value( ).
    if iv_name <> lv_name. continue. endif.
* Element "w:fldSimple" was found, iv_name = Name of bookmark to fill
    lv_bookmark_found = abap_true.
    lr_node_bookmarkStart = lr_node.
* Read attribute "w:id" of element "w:bookmarkStart"
    lr_attribute = lr_attributes->get_named_item(
      NAME      = 'id'
      NAMESPACE = 'w'
    ).
    lv_id_start = lr_attribute->get_value( ).

clear lv_id_end.

* .... work with parser

* find element w:bookmarkEnd for relevant w:bookmarkStart, bookmarkEnd with the same id as bookmarkStart

        lr_node_next = lr_node->get_next( ).
        do.
          IF lr_node_next IS INITIAL. EXIT. ENDIF.
          lr_node = lr_node_next.
          lv_name = lr_node->get_name( ).
          if lv_name = 'bookmarkEnd'.
            lr_attributes = lr_node->get_attributes( ).
* Read attribute "w:id" of element "w:bookmarkEnd"
            lr_attribute = lr_attributes->get_named_item(
              NAME      = 'id'
              NAMESPACE = 'w'
            ).
            lv_id_end = lr_attribute->get_value( ).
          endif.
          if lv_id_start = lv_id_end.
            lr_node_parent = lr_node->get_parent( ).
            lr_node_bookmarkEnd = lr_node.
            er_node_bookmarkEnd = lr_node.
            exit.
          else.
            lr_node_next = lr_node->get_next( ).
* remove elements between bookmarkStart and bookmarkEnd

            lr_node->remove_node( ).
          endif.
        enddo.
* insert new elements between boomarkStart and bookmarkEnd

CALL METHOD me->XML_RUN_TEXT_ADD

          EXPORTING

            IR_PARENT = lr_node_parent

            IR_REF    = lr_node_bookmarkEnd

            IV_VALUE  = iv_value

          IMPORTING

            EV_OK     = lv_bookmark_ok.

  EXIT.

ENDDO.

For example, XML with bookmark:

<w:bookmarkStart w:id="0" w:name="testing123"/>
<w:r>
<w:t>This is sentence two.</w:t>
</w:r>
</w:p>
<w:p>
<w:r>
<w:t xml:space="preserve">This </w:t>
</w:r>
<w:bookmarkEnd w:id="0"/>

4. replace file 'word/document.xml' in zip (.docx) with class CL_ABAP_ZIP.

CL_ABAP_ZIP->DELETE

CL_ABAP_ZIP->ADD


5. You can store result where you need.

6. You can use for editing by users classic dynpro with container with word.

Former Member
0 Kudos

Thank you very much. I will need to play a bit with the parser, but you were very helpful. Would give you some points if I could, but warm words are sometimes more than points .

radek_pavlu
Explorer
0 Kudos


Work with parser is simply. Its repeating use of few methods of interfaces if_ixml* (if_ixml_node, if_ixml_element, if_ixml_node_list, IF_IXML_NODE_COLLECTION, if_ixml_node_iterator, IF_IXML_NAMED_NODE_MAP, IF_IXML_ATTRIBUTE). Structure of docx xml you can find at MSDN or you cat create word document, save it, rename .docx->.zip, open file word/document.xml in internet explorer.

Example of creating xml element with attributes:

* FONT=val
* create child "w:rFonts"
            lr_element = gr_document->create_element(
                         name      = 'rFonts'
                         namespace = 'w' ).
            lr_child_prop ?= lr_element.
            lr_attribute = gr_document->create_attribute(
                         name      = 'ascii'
                         namespace = 'w' ).
            lr_prop_attrib ?= lr_attribute.
            lv_rc = lr_prop_attrib->SET_VALUE( value = 'Arial' ).
            lr_attributes = lr_child_prop->get_attributes( ).
            lv_rc = LR_ATTRIBUTES->SET_NAMED_ITEM( node = lr_prop_attrib ).
            lr_attribute = gr_document->create_attribute(
                         name      = 'hAnsi'
                         namespace = 'w' ).
            lr_prop_attrib ?= lr_attribute.
            lv_rc = lr_prop_attrib->SET_VALUE( value = 'Arial' ).
            lv_rc = LR_ATTRIBUTES->SET_NAMED_ITEM( node = lr_prop_attrib ).
            lr_attribute = gr_document->create_attribute(
                         name      = 'cs'
                         namespace = 'w' ).
            lr_prop_attrib ?= lr_attribute.
            lv_rc = lr_prop_attrib->SET_VALUE( value = 'Arial' ).
            lv_rc = LR_ATTRIBUTES->SET_NAMED_ITEM( node = lr_prop_attrib ).
            lv_rc = lr_child_Pr->append_child( lr_child_prop ).

XML:

* Text properties:
* <w:r>
*   <w:rPr>
*     <w:rFonts w:ascii="Arial" w:hAnsi="Arial" w:cs="Arial" />
*     <w:sz w:val="44" />
*     <w:szCs w:val="44" />
*     <w:b />
*     <w:imprint />
*     <w:lang w:val="en-ca" />
*   </w:rPr>
* </w:r>

If You don't set text properties in bookmark, word use text properties from parent paragraph. In most cases is sufficient set text "value" in bookmark.

Former Member
0 Kudos

You are right the whole parsing part should be easy. But in the process, where user should be able to change the document content and you want to parse the text, this is not that easy at all. Bookmars are maybe suitable a little bit as a replacement of real merge-fields. But only one-way there, not the other way out.

radek_pavlu
Explorer
0 Kudos

Warning: Mergefield has different xml in office 2007 and office 2010. Bookmark has same xml in office 2007 and 2010.

In our scenario we are using boht, bookmarks and mergefields. But in mergefields we must find, if template was created by office 2007 or 2010.

Former Member
0 Kudos

Hi Radek Pavlu

Your code is very useful for work with DOCX.

Can you provide code for fill MergeField or FormField in DOCX?

Thank you