Dynamo to word file?

As I recall you can directly read and write rtf files with Dynamo (including formatting), which can be read by word without issue. Just means you have to change from .doc to .rtf.

I used to do the prep work for all my field reports this way. Read the template, last report’s stuff to follow up on, weather on site, and a few other external bits. Then emailed myself the file as Pages on iOS reads that format just as well, and then would add new photos via camera and notes by voice to text as I walked the site. Got back to the office, proof read it and would have it sent out inside an hour.

1 Like

Hello
another solution using Microsoft.Office.Interop.Word

import sys
import clr
import System

clr.AddReference("Microsoft.Office.Interop.Word")
import Microsoft.Office.Interop.Word as Word

clr.AddReference("System.Runtime.InteropServices")
import System.Runtime.InteropServices

    
def find_replace(objRng, search_txt, replce_txt):
	missing = System.Type.Missing
	replaceAll = Word.WdReplace.wdReplaceAll
	objRng.Find.Execute(
	search_txt, # search text
	True, # match case
	missing, # match whole word
	missing, # match wildcards
	missing, # match sounds like
	missing, # match all word forms
	True, # forward?
	Word.WdFindWrap.wdFindContinue, # wrap enum
	missing, # format?
	replce_txt, # replace with
	replaceAll, # replace enum
	missing, # match Kashida (arabic letters)
	missing, # diatrics?
	missing, # match other Arabic letters
	missing # support right to left
	)
    
def doc_replace_text(source_filename, tokens, values):
	global errors

	word_application = Word.ApplicationClass()
	word_application.visible = True
	document = word_application.Documents.Open(source_filename)
	#Find and Replace Process
	for _find, _replace in zip(tokens,values):
		for myStoryRange  in document.StoryRanges:
			find_replace(myStoryRange , _find, _replace)
			try:
				while myStoryRange.NextStoryRange is not  None:
					q = myStoryRange.NextStoryRange 
					find_replace(q, _find, _replace)
			except:
				import traceback
				errors.append(traceback.format_exc())
			#Find and replace in TextBox(shapes)
			try:	
				for shape in document.Shapes:
					initialText = shape.TextFrame
					if initialText.HasText:
						rangeobj = initialText.TextRange 
						find_replace(rangeobj, _find, _replace)

			except:
				import traceback
				errors.append(traceback.format_exc())
								

filename = IN[0]
tokens = IN[1]
values = IN[2]
errors = []    
word_application = Word.ApplicationClass()
word_application.Quit()
word_application = None
doc_replace_text(filename, tokens, values) 

OUT = errors
13 Likes

wow, this seems like a solution to me :heart_eyes: :heart_eyes:

that is an efficient way, amazing logics :ok_hand:

I am using your code @c.poupin , and it is working fine, except for one thing. When i put a string with more than 255 characters it crash and return an error about the excess of length of the string.
I am trying to solve but i can’t fix it. Anybody know how to solve it? Thanks

Can you post a screenshot of the error? It’s the path’s length of the file?

@c.poupin
Is because the string has more than 255 characters, and I want to write more than 255 characters by string.

The errror: Advertencia: IronPythonEvaluator.Error en la operación EvaluateIronPythonScript.
Traceback (most recent call last):
File “”, line 71, in
File “”, line 43, in doc_replace_text
File “”, line 16, in find_replace
EnvironmentError: System.Runtime.InteropServices.COMException (0x800A16DE): El parámetro de la cadena es demasiado largo.
en Microsoft.Scripting.Interpreter.MethodInfoCallInstruction.InvokeInstance(Object instance, Object args)
en Microsoft.Scripting.Interpreter.DynamicInstructionN.Run(InterpretedFrame frame)
en Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)
en Microsoft.Scripting.Interpreter.LightLambda.Run4[T0,T1,T2,T3,TRet](T0 arg0, T1 arg1, T2 arg2, T3 arg3)
en IronPython.Compiler.PythonCallTargets.OriginalCallTarget3(PythonFunction function, Object arg0, Object arg1, Object arg2)
en System.Dynamic.UpdateDelegates.UpdateAndExecute5[T0,T1,T2,T3,T4,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
en Microsoft.Scripting.Interpreter.DynamicInstruction6.Run(InterpretedFrame frame) en Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame) en Microsoft.Scripting.Interpreter.LightLambda.Run4[T0,T1,T2,T3,TRet](T0 arg0, T1 arg1, T2 arg2, T3 arg3) en IronPython.Compiler.PythonCallTargets.OriginalCallTarget3(PythonFunction function, Object arg0, Object arg1, Object arg2) en System.Dynamic.UpdateDelegates.UpdateAndExecute5[T0,T1,T2,T3,T4,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4) en Microsoft.Scripting.Interpreter.DynamicInstruction6.Run(InterpretedFrame frame)
en Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)
en Microsoft.Scripting.Interpreter.LightLambda.Run2[T0,T1,TRet](T0 arg0, T1 arg1)
en IronPython.Compiler.PythonScriptCode.RunWorker(CodeContext ctx)
en Microsoft.Scripting.Hosting.ScriptSource.Execute(ScriptScope scope)
en DSIronPython.IronPythonEvaluator.EvaluateIronPythonScript(String code, IList bindingNames, IList bindingValues)

try this method

import sys
import clr
import System
import time
clr.AddReference("Microsoft.Office.Interop.Word")
import Microsoft.Office.Interop.Word as Word

clr.AddReference("System.Runtime.InteropServices")
import System.Runtime.InteropServices

def find_replace(objRng, search_txt, replce_txt):
	missing = System.Type.Missing
	replaceAll = Word.WdReplace.wdReplaceAll
	if len(replce_txt) < 250:
		objRng.Find.Execute(
		search_txt, # search text
		True, # match case
		missing, # match whole word
		missing, # match wildcards
		missing, # match sounds like
		missing, # match all word forms
		True, # forward?
		Word.WdFindWrap.wdFindContinue, # wrap enum
		missing, # format?
		replce_txt, # replace with
		replaceAll, # replace enum
		missing, # match Kashida (arabic letters)
		missing, # diatrics?
		missing, # match other Arabic letters
		missing # support right to left
		)
	else:
		i =0
		flag = True
		while flag and i < 20: # prevent infiniteloop
			copyRange = objRng.Duplicate 
			flag = copyRange.Find.Execute(
			search_txt, # search text
			missing, # match case
			missing, # match whole word
			missing, # match wildcards
			missing, # match sounds like
			missing, # match all word forms
			missing, # forward?
			missing, # wrap enum
			missing, # format?
			missing, # replace with
			missing, # replace enum
			missing, # match Kashida (arabic letters)
			missing, # diatrics?
			missing, # match other Arabic letters
			missing # support right to left
			)
			if not flag:
				break
			else:
				copyRange.Text = replce_txt
			i += 1
	  
def doc_replace_text(source_filename, tokens, values):
	global errors
	word_application = Word.ApplicationClass()
	word_application.visible = True
	document = word_application.Documents.Open(source_filename)
	#time.sleep(2)
	#Find and Replace Process
	for _find, _replace in zip(tokens,values):
		for myStoryRange  in document.StoryRanges:
			find_replace(myStoryRange , _find, _replace)
			try:
				while myStoryRange.NextStoryRange is not  None:
					q = myStoryRange.NextStoryRange 
					find_replace(q, _find, _replace)
			except:
				import traceback
				errors.append(traceback.format_exc())
			#Find and replace in TextBox(shapes)
			try:	
				for shape in document.Shapes:
					initialText = shape.TextFrame
					if initialText.HasText:
						rangeobj = initialText.TextRange 
						find_replace(rangeobj, _find, _replace)

			except:
				import traceback
				errors.append(traceback.format_exc())
								

filename = IN[0]
tokens = IN[1]
values = IN[2]
errors = []    
word_application = Word.ApplicationClass()
word_application.Quit()
word_application = None
doc_replace_text(filename, tokens, values) 

OUT = errors
5 Likes

Perfect. Working. Great the loop inside de function! :slight_smile:

1 Like

@c.poupin a query if I wanted to edit more than a single word how could I do it, what am I doing wrong

Hello @Christhian
try like this with this code

1 Like

As always thank you very much, sometimes the solution is simple.

1 Like

@c.poupin I apologize that there will not be a new thread for this question but if I wanted to change several words at the same time, would it be this way?

just add a for loop

import sys
import clr
import System
import time
clr.AddReference("Microsoft.Office.Interop.Word")
import Microsoft.Office.Interop.Word as Word

clr.AddReference("System.Runtime.InteropServices")
import System.Runtime.InteropServices

def find_replace(objRng, search_txt, replce_txt):
	missing = System.Type.Missing
	replaceAll = Word.WdReplace.wdReplaceAll
	if len(replce_txt) < 250:
		objRng.Find.Execute(
		search_txt, # search text
		True, # match case
		missing, # match whole word
		missing, # match wildcards
		missing, # match sounds like
		missing, # match all word forms
		True, # forward?
		Word.WdFindWrap.wdFindContinue, # wrap enum
		missing, # format?
		replce_txt, # replace with
		replaceAll, # replace enum
		missing, # match Kashida (arabic letters)
		missing, # diatrics?
		missing, # match other Arabic letters
		missing # support right to left
		)
	else:
		i =0
		flag = True
		while flag and i < 20: # prevent infiniteloop
			copyRange = objRng.Duplicate 
			flag = copyRange.Find.Execute(
			search_txt, # search text
			missing, # match case
			missing, # match whole word
			missing, # match wildcards
			missing, # match sounds like
			missing, # match all word forms
			missing, # forward?
			missing, # wrap enum
			missing, # format?
			missing, # replace with
			missing, # replace enum
			missing, # match Kashida (arabic letters)
			missing, # diatrics?
			missing, # match other Arabic letters
			missing # support right to left
			)
			if not flag:
				break
			else:
				copyRange.Text = replce_txt
			i += 1
	  
def doc_replace_text(source_filename, tokens, values):
	global errors
	word_application = Word.ApplicationClass()
	word_application.visible = True
	document = word_application.Documents.Open(source_filename)
	#time.sleep(2)
	#Find and Replace Process
	for _find, _replace in zip(tokens,values):
		for myStoryRange  in document.StoryRanges:
			find_replace(myStoryRange , _find, _replace)
			try:
				while myStoryRange.NextStoryRange is not  None:
					q = myStoryRange.NextStoryRange 
					find_replace(q, _find, _replace)
			except:
				import traceback
				errors.append(traceback.format_exc())
			#Find and replace in TextBox(shapes)
			try:	
				for shape in document.Shapes:
					initialText = shape.TextFrame
					if initialText.HasText:
						rangeobj = initialText.TextRange 
						find_replace(rangeobj, _find, _replace)

			except:
				import traceback
				errors.append(traceback.format_exc())
								
toList = lambda x : x if hasattr(x, '__iter__') else [x]
lst_filenames = toList(IN[0])
tokens = IN[1]
values = IN[2]
errors = []    
word_application = Word.ApplicationClass()
word_application.Quit()
word_application = None
for filename in lst_filenames:
	doc_replace_text(filename, tokens, values) 

OUT = errors
2 Likes

As always thank you very much @c.poupin , you make it look so simple

Hello @c.poupin , I tried using your python script in dynamo but it can’t read my Word document. Do you have experience with the following issue/warning?
"FileNotFoundException: Unable to find assembly ‘Microsoft.Office.Interop.Word’

I can’t find a solution for this issue. I’m new to Dynamo and have no experience with python.
Sorry for reviving this topic…
Thanks in advance.

This may be a shot in the dark… But I’ve found quite a few things don’t work in C-Python.

Try changing your node back to Iron Python and see if that helps.

2 Likes

Also looks like another possible MSOffice update breaking their interop. Try the online repair tool which you can find via google search.

@Alien That was the solution, thanks a lot!

1 Like

Code updated for Net Core 8 compatibility (with IronPython 2 or 3)

# import sys
import clr
import System
import time

clr.AddReference("System.Runtime.InteropServices")
import System.Runtime.InteropServices


class WordlEnum:
    """
    values Enum from API Interop Mircrosoft Doc , to avoid import lib in clr for Enum 
    """
    Word_WdReplace_wdReplaceAll = 2
    Word_WdReplace_wdReplaceNone = 0
    Word_WdReplace_wdReplaceOne = 1
    Word_WdFindWrap_wdFindContinue = 1

def find_replace(objRng, search_txt, replce_txt):
    # missing = System.Type.Missing
    missing = None
    if len(replce_txt) < 250:
        objRng.Find.Execute(
        search_txt, # search text
        True, # match case
        missing, # match whole word
        missing, # match wildcards
        missing, # match sounds like
        missing, # match all word forms
        True, # forward?
        WordlEnum.Word_WdFindWrap_wdFindContinue, # wrap enum
        missing, # format?
        replce_txt, # replace with
        WordlEnum.Word_WdReplace_wdReplaceAll, # replace enum
        missing, # match Kashida (arabic letters)
        missing, # diatrics?
        missing, # match other Arabic letters
        missing # support right to left
        )
    else:
        i =0
        flag = True
        while flag and i < 20: # prevent infiniteloop
            copyRange = objRng.Duplicate 
            flag = copyRange.Find.Execute(
            search_txt, # search text)
            missing, # match case
            missing, # match whole word
            missing, # match wildcards
            missing, # match sounds like
            missing, # match all word forms
            missing, # forward?
            missing, # wrap enum
            missing, # format?
            missing, # replace with
            missing, # replace enum
            missing, # match Kashida (arabic letters)
            missing, # diatrics?
            missing, # match other Arabic letters
            missing # support right to left
            )
            if not flag:
                break
            else:
                copyRange.Text = replce_txt
            i += 1
      
def doc_replace_text(source_filename, tokens, values):
    global errors
    # word_application = Word.ApplicationClass()
    word_application = System.Activator.CreateInstance(System.Type.GetTypeFromProgID("Word.Application", True))
    word_application.visible = True
    document = word_application.Documents.Open(source_filename)
    #time.sleep(2)
    #Find and Replace Process
    for _find, _replace in zip(tokens,values):
        for myStoryRange  in document.StoryRanges:
            find_replace(myStoryRange , _find, _replace)
            try:
                while myStoryRange.NextStoryRange is not  None:
                    q = myStoryRange.NextStoryRange 
                    find_replace(q, _find, _replace)
            except:
                import traceback
                errors.append(traceback.format_exc())
            #Find and replace in TextBox(shapes)
            try:    
                for shape in document.Shapes:
                    initialText = shape.TextFrame
                    if initialText.HasText:
                        rangeobj = initialText.TextRange 
                        find_replace(rangeobj, _find, _replace)

            except:
                import traceback
                errors.append(traceback.format_exc())
                                
toList = lambda x : x if hasattr(x, '__iter__') else [x]
lst_filenames = toList(IN[0])
tokens = IN[1]
values = IN[2]
errors = []    
word_application = System.Activator.CreateInstance(System.Type.GetTypeFromProgID("Word.Application", True))
word_application.Quit()
word_application = None
for filename in lst_filenames:
    doc_replace_text(filename, tokens, values) 

OUT = errors
import sys
import clr
import System
import time

clr.AddReference("System.Runtime.InteropServices")
import System.Runtime.InteropServices


class WordlEnum:
    """
    values Enum from API Interop Mircrosoft Doc , to avoid import lib in clr for Enum 
    """
    Word_WdReplace_wdReplaceAll = 2
    Word_WdReplace_wdReplaceNone = 0
    Word_WdReplace_wdReplaceOne = 1
    Word_WdFindWrap_wdFindContinue = 1

def find_replace(objRng, search_txt, replce_txt):
    # missing = System.Type.Missing
    missing = None
    if len(replce_txt) < 250:
        objRng.Find.Execute(
        search_txt, # search text
        True, # match case
        missing, # match whole word
        missing, # match wildcards
        missing, # match sounds like
        missing, # match all word forms
        True, # forward?
        WordlEnum.Word_WdFindWrap_wdFindContinue, # wrap enum
        missing, # format?
        replce_txt, # replace with
        WordlEnum.Word_WdReplace_wdReplaceAll, # replace enum
        missing, # match Kashida (arabic letters)
        missing, # diatrics?
        missing, # match other Arabic letters
        missing # support right to left
        )
    else:
        i =0
        flag = True
        while flag and i < 20: # prevent infiniteloop
            copyRange = objRng.Duplicate 
            flag = copyRange.Find.Execute(
            search_txt, # search text)
            missing, # match case
            missing, # match whole word
            missing, # match wildcards
            missing, # match sounds like
            missing, # match all word forms
            missing, # forward?
            missing, # wrap enum
            missing, # format?
            missing, # replace with
            missing, # replace enum
            missing, # match Kashida (arabic letters)
            missing, # diatrics?
            missing, # match other Arabic letters
            missing # support right to left
            )
            if not flag:
                break
            else:
                copyRange.Text = replce_txt
            i += 1
      
def doc_replace_text(source_filename, tokens, values):
    global errors
    # word_application = Word.ApplicationClass()
    word_application = System.Activator.CreateInstance(System.Type.GetTypeFromProgID("Word.Application", True))
    word_application.visible = True
    document = word_application.Documents.Open(source_filename)
    #time.sleep(2)
    #Find and Replace Process
    for _find, _replace in zip(tokens,values):
        for myStoryRange  in document.StoryRanges:
            find_replace(myStoryRange , _find, _replace)
            try:
                while myStoryRange.NextStoryRange is not  None:
                    q = myStoryRange.NextStoryRange 
                    find_replace(q, _find, _replace)
            except:
                import traceback
                errors.append(traceback.format_exc())
            #Find and replace in TextBox(shapes)
            try:    
                for shape in document.Shapes:
                    initialText = shape.TextFrame
                    if initialText.HasText:
                        rangeobj = initialText.TextRange 
                        find_replace(rangeobj, _find, _replace)

            except:
                import traceback
                errors.append(traceback.format_exc())
                                
toList = lambda x : x if hasattr(x, '__iter__') else [x]
lst_filenames = toList(IN[0])
tokens = IN[1]
values = IN[2]
errors = []    
word_application = System.Activator.CreateInstance(System.Type.GetTypeFromProgID("Word.Application", True))
word_application.Quit()
word_application = None
for filename in lst_filenames:
    doc_replace_text(filename, tokens, values) 

OUT = errors
2 Likes