Range.InsertXML using Transform - vba

I am using Range.InsertXML to add formatted content to an existing MS Word document.
AFAICT, the method .InsertXML takes parameters XML as a string and XLST (Transform) as a variant; however the documentation for .InsertXML is lacking in that it does not provide an example that includes the use of Transform.
In responding to this question: Range.InsertXML throws when inserting Open XML Cindy Meister linked to an excellent resource that explains lotd about Words XML and how to use it, but I still can't find any examples where Transform is used.
What I am trying to do is the same as in this question: XSLT create table with dynamic number of rows and columns (but using VBA).
From that question this is the modified code that I expected to work:
Sub testInsertXML()
Dim strXML As String
Dim XSLT As Variant
strXML = "<Movies><Genre name=""Action""><Movie><Name>Crash</Name><Released>2005</Released></Movie></Genre><Genre name=""Drama""><Movie><Name>The Departed</Name><Released>2006</Released></Movie><Movie><Name>The Pursuit of Happyness</Name><Released>2006</Released></Movie></Genre><Genre name=""Comedy""><Movie><Name>The Bucket List</Name><Released>2007</Released></Movie></Genre></Movies>"
XSLT = "<xsl:stylesheet version=""1.0"" xmlns:xsl=""http://www.w3.org/1999/XSL/Transform"" xmlns:w=""http://schemas.microsoft.com/office/word/2003/wordml""><xsl:output indent=""yes""/><xsl:template match=""/""><w:document><w:body><w:tbl><w:tr><xsl:for-each select=""/Movies/Genre""><w:tc><w:p><w:r><w:t><xsl:value-of select=""#name""/></w:t></w:r></w:p></w:tc></xsl:for-each></w:tr><!-- Movies? --><xsl:call-template name=""movies-row""><xsl:with-param name=""i"" select=""1""></xsl:with-param></xsl:call-template></w:tbl></w:body></w:document></xsl:template><xsl:template name=""movies-row""><xsl:param name=""i""/><w:tr><xsl:for-each select=""/Movies/Genre""><w:tc><w:p><w:r><w:t><xsl:choose><xsl:when test=""count(Movie) >= $i""><xsl:value-of select=""concat(Movie[$i]/Name, ' (', Movie[$i]/Released, ')')""/></xsl:when><xsl:otherwise><!-- empty cell --></xsl:otherwise></xsl:choose></w:t></w:r></w:p></w:tc></xsl:for-each></w:tr><xsl:if test=""/Movies/Genre[count(Movie) > $i]"">" & _
"<xsl:call-template name=""movies-row""><xsl:with-param name=""i"" select=""$i + 1""/></xsl:call-template></xsl:if></xsl:template></xsl:stylesheet>"
Selection.Range.InsertXML strXML, XSLT
End Sub
I have added double quotes to the strings (so that the quotes appear correctly when using debug.print) AND I have added additional tags as described here.
I think I'm not setting up the Transform correctly. If the Transform is set to vbnullstring everything works. Otherwise I get an error:
XML markup cannot be inserted in the specified location
Basically, as an answer to this question, I'd like a minimal example showing how to use .InsertXML using Transform.

Working solution:
The original source for the xml appears to be from this learn.microsoft.com site. Incidentally, there are also some instructions on how to create an XSLT directly from a MS Word document (well, albeit manually).
At any rate, it's easiest to just to the transform and insert as two separate steps, like this:
Sub xmlTest()
Dim xData As MSXML2.DOMDocument60
Dim xTnsf As MSXML2.DOMDocument60
Dim xOutp As MSXML2.DOMDocument60
Set xData = New MSXML2.DOMDocument60
Set xTnsf = New MSXML2.DOMDocument60
Set xOutp = New MSXML2.DOMDocument60
xData.Load "C:\Temp\MyMovies.xml"
xTnsf.Load "C:\Temp\MyMovies.xslt"
xData.transformNodeToObject xTnsf, xOutp
ThisDocument.Range.InsertXML xOutp.XML
Debug.Print xOutp.XML
End Sub

Related

How to avoid runtime error 5152 when trying to add a URL as shape to a Word document

I am trying to place a QR code generated through an API (api.qrserver,com) in a Word table using VBA. For certain reasons, the option of simply using "DisplayBarcode" is not possible.
This is the call to the API:
sURL = "https://api.qrserver.com/v1/create-qr-code/?data=" & UTF8_URL_Encode(VBA.Replace(QR_Value, " ", "+")) & "&size=240x240"
It seems to work well. I tried with a GET command and retrieved a string that - as I interpret - contains the QR code in png format.
Now, when I try to add the picture as a shape using
Set objGrafik = ActiveDocument.Shapes.AddPicture(sURL, True)
the call fails with runtime error 5152. As far as I could determine until now, the Addpicture method expects a pure filename and does not allow any of the following characters: /|?*<>:".
I also tried to store the GET result in an object variable:
Set oQRCode = http.responseText
but there I get the error "object required".
Research on the internet regarding a solution to either make the URL assignment work or to store the result as a picture didn't retrieve any useful results. Thanks in advance for your support
I am not sure that any of the ways you could insert something into Word (e.g. Shapes.AddPicture, InlineShapes.AddPicture, Range.InsertFile etc. will let you do that from any old https Url, although it seems to work for some Urls.
However, as it happens, you can use an INCLUDEPICTURE field to do it. FOr example
{ INCLUDEPICTURE https://api.qrserver.com/v1/create-qr-code/?data=Test&size=100x100 }
Here's some sample VBA to do that
Sub insertqrcode()
Dim sUrl As String
Dim f As Word.Field
Dim r1 As Word.Range
Dim r2 As Word.Range
' This is just a test - plug your own Url in.
sUrl = "https://api.qrserver.com/v1/create-qr-code/?data=abcdef&size=100x100"
' Pick an empty test cell in a table
Set r1 = ActiveDocument.Tables(1).Cell(5, 4).Range
' We have to remove the end of cell character to add the field to the range
' Simplify
Set r2 = r1.Duplicate
r2.Collapse WdCollapseDirection.wdCollapseStart
Set f = r2.Fields.Add(r2, WdFieldType.wdFieldIncludePicture, sUrl, False)
' If you don't want the field code any more, do this
f.Unlink
' check that we have a new inline shape that we could work with more
' if necessary
Debug.Print r1.InlineShapes.count
Set f = Nothing
Set r2 = Nothing
Set r1 = Nothing
End Sub
Using INCLUDEPICTURE works even on the current version of Mac Word (although I haven't tested that specific piece of VBA on Mac).
The only other way I have seen to do it uses WinHTTP to get the result from the web service, ADODB to stream it out to a local disk file, then AddPicture or whatever to include that file, so much more complicated and won't work on Mac either.

replace inlineshapes using word VBA

I have a word document with many images in it and I wish to be able to select a file path (in which the new images are located and numbered i.e. 1 to 100) to then replace the existing images with the new images.
I have read a few posts of others retrieving the properties of existing inlineshapes to achieve this, but I have also read people succeeding with the following method which seems much simpler (I just haven't been able to get the code working fully yet). Currently the code will run and replace the last image perfectly fine, but then stops with error '438' - Object doesn't support this property or method when it tries to replace the second last image.
The code is as follows:
Sub Replace_images()
Dim rg As Word.Range
Dim i As Long
Dim doc As Word.Document
Dim path As String
'Ensure pictures are numbered with no leading zeros (in folder) & are .jpg
path = "C:\filepathtopictures\"
Set doc = ActiveDocument
For i = doc.InlineShapes.Count To 1 Step -1
Set rg = doc.InlineShapes(i).Range
doc.InlineShapes(i).Delete
rg = doc.InlineShapes.AddPicture(path & i & ".jpg", False, True, rg)
Next i
End Sub
I don't understand how the using the addpicture doesn't work for the next image when nothing has change from the last image. If someone could please explain why it doesn't work or tell me what needs to be changed that would be great!

Issue with pdf docs not showing up

We recently wrote some code for a client using the Aspose.pdf library, on my system the pdf in question opened fine and most of the merge fields were filled in (we don't have the exact list of merge fields that they do).
They're telling me that on their system, some documents take 2-4 mins to open while others don't open at all.
What could be a possible cause of the document not opening at all?
My code is below:
' Load form
Dim doc As Aspose.Pdf.Document = New Aspose.Pdf.Document(sTemplateDir & sDocName)
'Get names of form fields
Dim fields As Aspose.Pdf.InteractiveFeatures.Forms.Field() = doc.Form.Fields
Dim sField As String
Dim field As Aspose.Pdf.InteractiveFeatures.Forms.Field
If fields.Length > 0 Then
For Each field In fields
'Get name of field
sField = field.FullName
'If the merge field isn't valid then we'll just leave it and assume its a fill-in
If nMergeCol.Contains(sField) And Not IsNothing(sField) Then
field.Value = nMergeCol.Item(sField)
End If
Next
End If
This has been resolved! As we suspected, it was a problem with the client's Javascript within the pdf file. The problem was within the calculations the absolute value was being used (name.value). Once this was switched to the relative value (this.event.value) the pdf file began behaving correctly with the AsPose code.

Create, fill and resize WordTable before inserting it to instance

I am currently working on an application that gets data out of a database and puts it into an open word instance.
Currently it's doing the following steps:
Find open Word Instance (if multiple opened user can select)
Dim oDocs As Word.Document =
WordApplication.Application.Documents(filepath)
Create a table on a bookmark in the Word Instance
Try
WordTable = oDocs.Tables.Add(oDocs.Bookmarks.Item("NameOfBookmark").Range, DataTable.Rows.Count + 1, DataTable.Columns.Count)
Catch ex As Exception
WordTable = oDocs.Tables.Add(oDocs.Application.Selection.Range, DataTable.Rows.Count + 1, DataTable.Columns.Count)
End Try
Fill the table, when it's already in the word instance
Looping for each row and cell -> In this loop is happening a lot, but it's working and doesn't matter for the question so I will not put the code inside here
I know the speed can be so slow because of the stuff happening in the part where it fills the table, but i do not think it's too much.
My Problem is the speed of that. While this all is working fine, it takes years to execute. You can see every Cell being filled in the opened Word-Document. My thoughts for a solution is to find a way to create that WordTable in my VB application and only insert the finished Word-Table into the Word instance, but I can't find a way to do so.
Is there a way to do that? If yes, please tell me how!
TL:DR
Can I completly create and fill and resize a WordTable in my VB application before inserting it into an opened WordInstance? If yes, how?
EDIT
Bibadia just gave the perfect Answer!
I will give you my full working Code now - It only creates a table in the word-instance. You have to format it later in your application.
Dim oDocs As Word.Document = WordApplication.Application.Documents(filepath)
Dim strTable As String = ""
Dim isFirst As Boolean = True
Dim intColumns As Integer = DataTable.Columns.Count
Dim intRows As Integer = DataTable.Rows.Count
For Each column As DataColumn In DataTable.Columns
If Not isFirst Then
strTable &= ";"
End If
strTable &= column.ColumnName
isFirst = False
Next
For Each row As DataRow In DataTable.Rows
For Each column As DataColumn In DataTable.Columns
strTable &= ";" & row.Item(column)
Next
Next
Dim rng As Word.Range
rng = oDocs.Application.Selection.Range
rng.Text = strTable
Dim WordTable As Word.Table = rng.ConvertToTable(NumRows:=intRows + 1, NumColumns:=intColumns, Separator:=Word.WdSeparatorType.wdSeparatorColon)
Three things you could try:
Insert the data as plain text using delimiters that do not appear in your data (e.g. vbTab and vbCr), then use the ConvertToTable method of the range object. You will need to apply formatting after that.
Build the table using WordProcessingML and insert it using the InsertXML method of a Range object. It is up to you how much formatting you attempt to describe using the XML - personally, I would start by inserting the simplest possible table pre-filled with data, then apply formatting using the object model if that is not also too slow.
Use the InsertDatabase method of the Range. But you will need to be able to access your database using a method Word can work with (e.g. OLE DB or ODBC), so you will probably need a .odc file (or DSN) to make it work, which typically makes distribution of a solution harder. It may also be difficult to prevent security information from being stored in the .docx or .odc/DSN.
THere is an article here that provides some code for method (1) and more information about applying formatting.

Sample HTML placed in a variable

I can retrieve the contents of a webpage into a variable like:
x = objIE.Document.body.outerHTML
But during development (data parse), I don’t want to keep pulling up a live site and instead just want to store some sample html in a format that can be placed in a variable. The issue is that trying to directly place a raw html sample into a variable creates errors and would require going through and escaping quotes etc. What’s an easy way to put place a large block of sample html in a variable?
You could read the html into a string variable using IO.StreamReader
Dim docStuff As String
Dim strReader As IO.StreamReader
Dim strWriter As IO.StreamWriter
strReader = New IO.StreamReader(Application.StartupPath & "/me.html")
docStuff = strReader.ReadToEnd
strReader.Close()
then write it back out using IO.StreamWriter
strWriter = New IO.StreamWriter(Application.StartupPath & "/changedCode.html")
strWriter.Write(docStuff)
strWriter.Close()
I have never had issues with this method with escape characters, etc. I pull it in, then I can look at the code, make changes, etc. Works like a charm.