Save InlineShape picture to file in Word VBA using WordXML - vba

Reviewing this question Save InlineShape picture to file in Word VBA, the accepted answer works fine for our case but the saved png doesn't have the size used in the document and simply has the full original size.
As in the original question, the alternative that uses ADODB generates some artifacts in the file (white spaces) that I couldn't fix in code (don't know what causes them).
The HTML or the zip ways don't work for me because I have to keep track of each picture's name and location in the document (not that is important for this question but is a constrain of the process, I save those names in a CSV that also relates to the table in which appears the picture).
The way I used to do it was to copy the inlineshape range as a picture and save the clipboard content to a file with a powershell command run from within the vba code, but the antivirus where flagging the code and didn't let it to go through (I can't touch the antivirus, as is a macro for the client to execute)
I have searched but can't find a way to save in a folder the picture (inline shapes) with the size used in the document.
The saved picture could be in png, jpg or bmp.
Edit: I believe that the trick is in how the range is created from the Inline Shape's Range
Dim r As Range
Set r = shp.Range.Duplicate
r.Start = r.Start - 1
r.End = r.End + 1
I don't know if it is possible that when the creation of the duplicated range it could respect the configuration of the original object

Related

PowerPoint: Repeat text on every slide

I'm working on creating a PowerPoint template for daily class presentations. In the template I'd like to have a hunk of text that is prominently displayed on the first slide and which repeats at the bottom of the subsequent slides at the bottom in a smaller size. The text will change every day.
The ideas I've had so far:
Use a text field. As far as I can tell, PowerPoint doesn't have anything like a text field that can be dynamically set.
Use a footer - this works and I can modify the master to get the look I want, but I'd really like to be picking up the value of the text from the first page so that edits would be automatically applied and to save the initial step of setting the footer.
Using VBA - I'd be willing to give this a shot, but I've never used VBA and the learning curve seems steep so it would be nice to know if the idea is feasible in VBA.
Can this be done? How would you approach it?
Here's an example of what I'm hoping to be able to do. Ideally the solution would work on both the Mac (2013) and Windows (2016) version of PowerPoint.
You can connect your presentation with an excel file. And running the code in the ppt would pull out the text in the excel file and update the titles instantly.
Create a presentation with a textbox with some temporary text. Put the below code in ppt. save as pptm.
Sub AddMotionPath()
Dim Temp As String
Excel.Application.Workbooks.Open ("D:\Users\Desktop\Book1.xlsx") ' update the path of the excel file
Workbooks("Book1.xlsx").Activate 'activate the file
For p = 1 To 4
Temp = Workbooks("Book1.xlsx").Worksheets("Sheet1").Range("B" & p + 1).Value ' Column B has the titles
ActivePresentation.Slides(p).Shapes(1).TextFrame.TextRange.Text = Temp ' this updates the titles from excel to ppt slide
Next
Excel.Application.Workbooks("Book1.xlsx").Close False 'closes the excel file
End Sub
Let me know if this works for you. You can update the excel file and run the macro in ppt. The text in the slides will be updated automatically.

Change Path to Picture Links in Excel

I have manually pasted a large number of linked pictures into a 2010 Excel spreadsheet using insert picture -> select picture location --> link to file. These pictures are part of a report. I update the pictures using R each quarter, and my report automatically updates. Perfect.
I now need to change the directory where the plots are kept, and need to update the links. As there are around 200 of them (its a big report), I want to do this in VBA. Whilst I can loop through the pictures ok (ActiveSheet.Pictures), I can't seem to find the links/address. Any idea how I can see the underlying file location so I might change it - the reference has to be stored somewhere (note - these don't seem to be stored as hyperlinks).
Any idea how I can see the underlying file location so I might change it - the reference has to be stored somewhere
Create a new folder
Paste a copy of the .xlsx or .xlsm excel file
Uncompress the file with a zip tool (i'm using 7-Zip)
Delete the .xlsx or .xlsm file (optional)
Now we have all the component parts of the original file as plain text xml files and folders
Inside the folder xl\drawings\ _rels there are files named as drawing2.xml.rels, drawing3.xml.rels, ...
It seems that each file corresponds to a sheet and stores the paths to images in this format:
Target="file:///C:\Users\myusername\Documents\MyImageFolder\My%20Image%20Name.png"
Change the paths with a text editor
Compress all the contents of the folder to a .zip
Change the extension to the original .xlsx or .xlsm
These steps could be automated with VBA, AutoIt, etc., here some references:
An example with AutoIt and 7-zip
http://www.jkp-ads.com/Articles/Excel2007FileFormat.asp
http://www.jkp-ads.com/Articles/Excel2007FileFormat02.asp
Ron de Bruin zip examples with VBA
Read and change multiple XML files in Excel (2007) VBA
Excel uses the Formula Bar as the link in this case, just the same as it would link between ranges in two different worksheets. When I select a linked picture, the formula below populates in the formula bar:
=[TrialWB.xlsm]Sheet1!$C$3:$E$6
You can access the Shape's formula using the code below and inserting your picture's specific name:
ActiveSheet.Pictures("Picture Name").Formula = "=[TrialWB.xlsm]Sheet1!$C$4:$E$6"
In updating the links, you'll have to change the file path in the formula. This might look like:
ActiveSheet.Pictures("Picture Name").Formula = "='C:\Reports2015\[TrialWB.xlsm]Sheet1'!$C$4:$E$6"
changing to
ActiveSheet.Pictures("Picture Name").Formula = "='C:\Reports2016\[TrialWB.xlsm]Sheet1'!$C$4:$E$6"
This question may be of some further assistance for accessing formulas:
Excel: create image from cell range
And here's a useful Microsoft page for formula file path editing: https://support.office.com/en-us/article/Create-an-external-reference-link-to-a-cell-range-in-another-workbook-c98d1803-dd75-4668-ac6a-d7cca2a9b95f

How to choose a different header image in MS Word 2013 using a macro/button

I'd like to create a Word stationary template with ability to cycle through different colored logos in its header. My company uses a logo in five different colors and I would like to create a single template with a button that would allow me to cycle through the different colored logos every time I create a new document from this template. Can this be done, perhaps with a little VBA?
Edit:
After working with an answer from Olle Sjögren I've come up with the following working script:
Option Explicit
Public imgCounter As Integer
Sub cycle_logos()
Dim I As Variant
Dim logoColors(4) As String
logoColors(0) = "logo_magenta.png"
logoColors(1) = "logo_teal.png"
logoColors(2) = "logo_orange.png"
logoColors(3) = "logo_red.png"
logoColors(4) = "logo_grayscale.png"
For Each I In logoColors
ActiveDocument.StoryRanges(wdPrimaryHeaderStory).ShapeRange.Item(I).Visible = msoFalse
Next I
imgCounter = imgCounter + 1
If imgCounter = 5 Then imgCounter = 0
ActiveDocument.StoryRanges(wdPrimaryHeaderStory).ShapeRange.Item(logoColors(imgCounter)).Visible = msoTrue
End Sub
It is worth mentioning how I came up with the image names, since I didn't find a way to do this from inside Word. I renamed the template extension to zip and unzipped it. In the extracted files I opened word\header2.xml (this may vary, depending on the position in the document) and edited the nodes containing the names of pictures, i.e. <wp:docPr/>, e.g.:
<wp:docPr name="Picture 1" id="1"/>
became:
<wp:docPr name="logo_magenta.png" id="1"/>
etc. I then replaced the XML file in the ZIP with my edited version and changed the extension back to dotm.
As mentioned, there are several ways to do this. I would suggest storing the images outside of the template, otherwise all documents based on the templates would include all logo images, even if they are not shown, making the documents bigger than they need to be. That approach makes installing the template a bit harder, since you would have to copy the images to the clients as well as the template file.
To answer your question regarding addressing the images in the header - you can address then through the correct story range object. In my example I assume that they are in the primary header. To tell the different images apart, you can use the Name property or the index in the Item collection:
ThisDocument.StoryRanges(wdPrimaryHeaderStory).ShapeRange.Item("Picture 1").Visible = msoTrue
ThisDocument.StoryRanges(wdPrimaryHeaderStory).ShapeRange.Item(1).Visible = msoFalse

Convert text to image in Microsoft Word

I have a large book written in Microsoft Word and want to create a macro that will find all text using a predefined style and convert that text to an inline image. This text will be in Arabic and generally no longer than 4-5 lines. Is this possible?
UPDATE: Here's an example to show what I'm referring to:
I want to replace that entire line in Arabic with an image (as if I cropped this attached image to only include the Arabic and then replaced the line in Arabic with the image).
The reason I want a macro or script to do this is because there are hundreds of such lines and updating them one by one is cumbersome plus that will make modifications difficult later on.
UPDATE2: I found an interesting option here: http://windowssecrets.com/forums/showthread.php/31344-Convert-Text-to-an-Image-of-Text-in-VBA-(Office-2000-Sr1a)
It looks like you can cut a piece of text and then "Paste Special" as an image. So if there's a way to automate that that might work.
This is not an answer although I hope it will grow into a community answer. At the moment it is an exploration of what is required to solve the problem.
I know from the discussion when this question was posted on Super User that Abdullah wishes to publish his book on Kindle. So the question is really about how to get a document in English and Arabic ready for publication as an e-Book.
The Kindle does not support Arabic. The number of languages it does support is slowly increasing but there is no evidence I can find that Amazon has plans to add Arabic in the foreseeable future.
The format behind an Amazon e-Book is a cut down version of HTML. If a Word document containing Arabic letters is exported to HTML, the Arabic letters are included as character entities; for example: “ﭐ &#amp;64337; ﭒ ﭓ”. Importing the original Word or the HTML version to Kindle, results in the leading bits being discarded so these characters are displayed as P, Q, R and S instead of “ﭐ ﭑ ﭒ ﭓ (Alef Wasla isolated form, Alef Wasla final form, Beeh Wasla isolated form and Beeh Wasla final form).
I have tried Abdullah’s idea of saving some Arabic letters in a PNG file and creating an HTML file containing <p> … </p> <img src= “Arabic.png” > <p> … </p>. The appearance of this file on my Kindle 2 is perfectly acceptable so this has the potential to be a solution. The question is: how can the necessary conversions be performed?
We need to extract each Arabic string from either the Word document or its HTML equivalent and import it into a program that can convert them to PNG files.
The only way that I know of automating this would be to copy each string to a slide within PowerPoint. With PowerPoint’s SaveAs option it is possible to save each slide as a separate PNG file. The slides are named: SLIDE1.PNG, SLIDE2.PNG, SLIDE3.PNG and so on in sequence which would allow a macro to relate the results to the original strings. It would then be possible to replace the Arabic strings in the HTML file with the image elements. None of this would be too difficult to automate but there is a problem with the slides all being the size of the PowerPoint page. The page could be made smallish but what we need is for each slide to be cropped to just bigger than that slide’s text. I cannot think of any way of automating this cropping.
Does anyone have a better approach than converting each Arabic phrase to a PNG file?
I have been looking for PNG editors with some sort of command line interface but can find nothing that would be easier than using PowerPoint. Does anyone know of an alternative to PowerPoint?
Does anyone have any suggestions for automating the cropping of each image? When a string is placed in a PowerPoint slide it is possible to set its width to, say, 6.5cm (which looks good on my Kindle) and get the height determined by PowerPoint. This could be saved for later use if anyone knows how to use it.
Implementing solution
Pending any suggestions for improving the approach described above, the following outlines how I would implement it.
I would not attempt to process the Word document. I would save it as a Web Page, Filtered HTML file, which is a required step on the way to creating a Kindle eBook, and process that.
Within the HTML file created from my test document, the Arabic phrase comes out as:
<p class="MsoNormal"></p>
<p class="MsoNormal" align="center" style="text-align:center"><span dir="RTL"
style="font-size:24.0pt;font-family:Arial">
&#64336;&#64337;&#64338;&#64339;&#64340;&#64341;
&#64342;&#64343;&#65153;&#65154;&#65276;&#65275;
&#65274;&#65273;&#65246;&#65226;&#65227;&#65228;
</span><span style="font-size:24.0pt"></span></p>
<p class="MsoNormal"></p>
<p class="MsoNormal"></p>
I assume Abdullah's document will result in something similar. Note 1: the above is a random collection of Arabic letters. Note 2: they are held left-to-right in reading sequence even though, when displayed or printed, they are read right-to-left.
The whole of this block will have to be replaced with something like:
<br><imc src="xxxx.png"><br>
where the file xxxx.png holds an image of the Arabic text.
The file names, such as xxxx.png, could be systematic (A001.png, A002.png, ...) but I would have thought that transliterating the first ten or twenty characters of the phrase from the Arabic to English alphabets and using the result, with a numeric suffix, as the file name would be more convenient.
I would hold the records necessary to manage the process in an Excel worksheet. I would place the VBA code in the same workbook.
The steps in the conversion process that I envisage are:
VBA macro to extract Arabic strings from latest HTML file and add new strings to the Excel worksheet. (More about the Excel worksheet later.)
VBA macro to create PowerPoint file, with one slide per new string, and use SaveAs in PNG format to create one PNG file per slide before discarding the PowerPoint file.
Human to crop each PNG file. (There appears to be no way of automating the cropping so this task will be minimised by use of data in the Excel worksheet.)
VBA macro to rename each slide from SLIDEnnn.PNG to its permanent name and to record the permanent name in the Excel worksheet.
VBA macro to update the latest HTML file by replacing the block containing the Arabic phrase with the appropriate HTML IMG element.
The Excel worksheet needs two columns: Arabic phrase and PNG file name. If there is any risk of the worksheet being sorted between steps 2 and 4, we may need a sequence number as well.
Macro 1 will extract an Arabic phrase from the HTML file, look down the list in the worksheet for this phrase and add the phrase at the bottom if it is not already present.
Macro 2 will look for phrases in the worksheet that do not have a PNG file name. These new phrases are the ones to be written to the PowerPoint presentation. That is, a phrase only goes into this process once.
Task 3, cropping each PNG file, will be a pain. All I can say is that it will only be once per phrase.
Macro 4 will assume that the SLIDE001.PNG, SLIDE002.PNG, … are in the sequence of phrases without PNG files in the worksheet. If this might not be true (because the worksheet has been sorted) we will either need a sequence number or to retain the PowerPoint file. The macro will assign a unique name to each new phrase, record this name in the worksheet and rename the PNG file.
Macro 5 creates a new copy of the latest HTML file using the contents of the worksheet to determine which phrase to replace with which PNG file.
This process is not ideal but it will achieve the desired result and has no obvious complications. Any suggestions for improving it?
Before you begin these instructions, press record in the Microsoft Word macro editor, so you can see what the VBA code is.
I'm wondering if this will be easier if you convert the docx file to .rtf (rich text format) and replace that line with an image? Go to File > Save As.. > name it "old.rtf", then replace the line with an image and Save As.. again and name it "new.rtf" and then download Beyond Compare or your favorite diff program to see what happened. It should be easy to do this pro-grammatically if you choose to. I think working in text would be easier than Microsoft's binary format unless you can find a good library to modify their doc or docx formats.
Sub CopySelPasteAsPicture()
' Take a picture of a selection and paste it at the
' document end
With Selection
.CopyAsPicture
End With
ActiveDocument.Content.Select
With Selection
.Collapse Direction:=wdCollapseEnd
.TypeParagraph
.TypeParagraph
.PasteSpecial DataType:=wdPasteMetafilePicture
End With
End Sub

Specifying location of new inlineshape in Word VBA?

I'm working on a document "wizard" for the company that I work for. It's a .dot file with a header consisting of some text and some form fields, and a lot of VBA code. The body of the document is pulled in as an OLE object from a separate .doc file.
Currently, this is being done as a Shape, rather than an InlineShape. I did this because I can absolutely position the Shape, whereas the InlineShape always appears at the beginning of the document.
The problem with this is that a Shape doesn't move when the size of the header changes. If someone needs to add or remove a line from the header due to a special case, they also need to move the object that defines the body. This is a pain, and I'd like to avoid it if possible.
Long story short, how do I position an InlineShape using VBA in Word?
The version I'm using is Word 97.
InlineShape is treated as a letter. Hence, the same technique.
ThisDocument.Range(15).InlineShapes.AddPicture "1.gif"
My final code ended up using ThisDocument.Paragraphs to get the range I needed. But GSerg pointed me in the right direction of using a Range to get my object where it needed to be.