Log in

View Full Version : Saving out multiple pdf after mail merge



AlexW
08-04-2011, 03:58 AM
I run a mail merge then export individual pdf pages as below. Instead of naming by the counter i want to use a line from each document, how can I retreive this to use?

For Counter = 1 To CInt(Selection.Information(wdNumberOfPagesInDocument))
ActiveDocument.ExportAsFixedFormat OutputFileName:="D:\Documents and Settings\user\Desktop\PDF\" & ActiveDocument.Name & "-" & Counter & ".pdf" _
, ExportFormat:=wdExportFormatPDF, OpenAfterExport:=False, OptimizeFor:=wdExportOptimizeForPrint, Range:=wdExportFromTo _
, From:=Counter, To:=Counter, Item:=wdExportDocumentContent, IncludeDocProps:=True, KeepIRM:=True _
, CreateBookmarks:=wdExportCreateNoBookmarks, DocStructureTags:=True, BitmapMissingFonts:=True, UseISO19005_1:=False
Next

Frosty
08-04-2011, 09:21 AM
Your question is so broad as to be unanswerable, except to say "yes, you can do it."

Where the text is that you want to use is going to dictate the *MANY* possible solutions to what you want to do. Is it:
1. At the very top of the document?
2. Contained in a document property?
3. In a footer?
4. Somewhere in the document, but always immediately following something else?
5. A unique style name?
6. The name of the folder the document it in?

Also, use the VBA codes, it makes your code more friendly to read.

AlexW
08-04-2011, 11:06 AM
I can include the text that I want the file names to be anywhere, possibly the first line of the document or inside a text box so it could more easily be referenced?

I just want to replace

& ActiveDocument.Name & "-" & Counter

- with this text for each pdf document that is written so I can specify the file names by importing it from the mail merge CSV source file.

I tried using ActiveDocument.Content.Find and found the text to set the file names accordingly but this searches the whole document and not page by page (like the loop seems to work by saving pages out individually) - so it ended up finding the text on the last page and working with that which is no good.

Any help appreciated :think:

Frosty
08-04-2011, 11:15 AM
Can't help until you give a specific problem, honestly.

You have this code:

For Counter = 1 To CInt(Selection.Information(wdNumberOfPagesInDocument))
ActiveDocument.ExportAsFixedFormat OutputFileName:="D:\Documents and Settings\user\Desktop\PDF\" & ActiveDocument.Name & "-" & Counter & ".pdf" _
, ExportFormat:=wdExportFormatPDF, OpenAfterExport:=False, OptimizeFor:=wdExportOptimizeForPrint, Range:=wdExportFromTo _
, From:=Counter, To:=Counter, Item:=wdExportDocumentContent, IncludeDocProps:=True, KeepIRM:=True _
, CreateBookmarks:=wdExportCreateNoBookmarks, DocStructureTags:=True, BitmapMissingFonts:=True, UseISO19005_1:=False
Next
I assume this is contained in a larger code structure, since you need at least "Sub XYZZY" and "End Sub" around that code chunk for it to be workable.

And if you are using Option Explicit at the top of your module (as you should be), then you would (at least) need to add "Dim Counter As Integer" included in that code block.

Now that you're that far... you don't want the name of the document to be the counter, you want it to be "something else" and I say... "what something else?"

The following would be "something else" (using you existing code chunk)...

Sub XYZZY
Dim Counter As Integer
Dim sAppendThis as String

sAppendThis = "SomethingElse"

For Counter = 1 To CInt(Selection.Information(wdNumberOfPagesInDocument))
ActiveDocument.ExportAsFixedFormat OutputFileName:="D:\Documents and Settings\user\Desktop\PDF\" & ActiveDocument.Name & "-" & Counter & sAppendThis & ".pdf" _
, ExportFormat:=wdExportFormatPDF, OpenAfterExport:=False, OptimizeFor:=wdExportOptimizeForPrint, Range:=wdExportFromTo _
, From:=Counter, To:=Counter, Item:=wdExportDocumentContent, IncludeDocProps:=True, KeepIRM:=True _
, CreateBookmarks:=wdExportCreateNoBookmarks, DocStructureTags:=True, BitmapMissingFonts:=True, UseISO19005_1:=False
Next
End Sub
But I assume you want something else than "something else" :)

Frosty
08-04-2011, 11:32 AM
Also, this topic comes up periodically. Word just simply doesn't really break itself into "pages" as easily as one might think.

yes, you can tell it to print page 1, and then print page 2 (which is what that loop is doing), however, trying to identify that actual range before printing is very tricky, and there is no "one-size-fits-all" solution (which is why I need you to be more specific).

Text box can work, but so could a style which is at the top of each page. But you have to take the document as you find it, I assume. There are so many questions, and your answer can't be "whatever you want."

So post the code you've tried and had fail, or explain better what the document is and how it is structured, or post up a mock up (no-sensitive data please!!!) which would allow some code to be written to give you a proof-of-concept that you can then extrapolate.

But you need to say what you want the output to be. I assume you don't want just some random string of names
BillyPage1.pdf
SallyPage2.pdf
BobbyPage3.pdf

So... what do you want?

AlexW
08-05-2011, 02:58 AM
Hi Frosty, appreciate your time on this here is all my code:

Sub SaveToPDF()
For Counter = 1 To CInt(Selection.Information(wdNumberOfPagesInDocument))
ActiveDocument.ExportAsFixedFormat OutputFileName:="D:\Documents and Settings\user\Desktop\PDF\" & Counter & ".pdf" _
, ExportFormat:=wdExportFormatPDF, OpenAfterExport:=False, OptimizeFor:=wdExportOptimizeForPrint, Range:=wdExportFromTo _
, From:=Counter, To:=Counter, Item:=wdExportDocumentContent, IncludeDocProps:=True, KeepIRM:=True _
, CreateBookmarks:=wdExportCreateNoBookmarks, DocStructureTags:=True, BitmapMissingFonts:=True, UseISO19005_1:=False
Next
End Sub

I can import the text I wish to be passed to each file name in the mail merge. It seems simple to me but perhaps its harder than I thought but I just want to replace the counter (in bold above) with the text imported to each page on the merge - this way I can supply it for each record in my CSV mail merge source file and have the resulting page named accordingly.

I tried using ActiveDocument to find this text but it appears to search the entire document instead of each one individually and so only returns the last text in the entire thing and not page by page, even when I searched inside the loop.

I also tried reading the name from a text box and replacing 'counter' with:

wdExportFromTo.ShapeRange.Item(1).TextFrame.TextRange

- but this had a compile error.

So my question is given that I can import text in the mail merge that I want to be the file name of each page, how can I pass this to the ultimate file names instead of just using the counter?

Frosty
08-05-2011, 06:22 AM
Well, I think it's probably simple, but you're not really helping me help you. If that's all the code, then I need to see how the document is set up to help you. Your merge template is 1 page, but the resultant merge document is multiple sections, yes? That might be a clue.

Why don't you post your resultant document (only 3 merged fields, dummy text and data)

Frosty
08-05-2011, 09:37 AM
Just a quick follow up... the "easiest" way to do this is to put the merge field code you want at the very top of your document, in its own paragraph.

Then when your loop you can do something like this (but please read the caveat):

Sub SaveToPDF()
Dim iCounter As Integer
Dim sNewOutputFileName As String
Dim sNewOutputPath As String

'this doesn't change, so we can leave it out of the loop
sNewOutputPath = "D:\Documents and Settings\user\Desktop\PDF\"

'here's your loop
For iCounter = 1 To CInt(Selection.Information(wdNumberOfPagesInDocument))
'here's where you get your name
'Option #1 -- old way
sNewOutputFileName = sNewOutputPath & iCounter

'Option #2 -- using the text of the first paragraph, except for the end carriage return
sNewOutputFileName = ActiveDocument.Sections(iCounter).Range.Paragraphs.First.Range.Text
sNewOutputFileName = Replace(sNewOutputFileName, vbCr, "")
sNewOutputFileName = sNewOutputPath & sNewOutputFileName

'here's where we add the file extension (although I don't think you need to do this:
sNewOutputFileName = sNewOutputFileName & ".pdf"

'and here's where you do it. However, you need to review all of these options
'as you are potentially including metadata you don't want to include, etc
ActiveDocument.ExportAsFixedFormat _
OutputFileName:=sNewOutputFileName, _
ExportFormat:=wdExportFormatPDF, _
OpenAfterExport:=False, _
OptimizeFor:=wdExportOptimizeForPrint, _
Range:=wdExportFromTo, _
From:=iCounter, To:=iCounter, _
Item:=wdExportDocumentContent, _
IncludeDocProps:=True, _
KeepIRM:=True, _
CreateBookmarks:=wdExportCreateNoBookmarks, _
DocStructureTags:=True, _
BitmapMissingFonts:=True, _
UseISO19005_1:=False
Next
End Sub

HOWEVER: this contains NO VALIDATION of whether your merge field is unique. Unlike the counter (which does have implicit validation-- every file you create will have a new number), without some kind of validation routine, you may very well end up with two (or more) "Bob.pdf" files. And one will either be copied over or it will stop the routine mid-processing.

So you could put the counter AND the merge field in the file name.

The bottom line: the only way to answer this question is to really know where you want to put it, and not in a theoretical sense (i.e., figuring out a way to upload a demo without any sensitive data in it).

Because if it's only being used for the filename, you probably also want to get rid of it. Or if it is being used for another purpose, then it probably isn't conveniently in the location I used for demonstration purposes.

And putting it into textboxes or footers or bookmarks or using the find object all have inherent problems because the way you'd refer to those objects in the merge template is going to be entirely different than the way you refer to them in the resultant merge document.

The best way to proceed is to know that merge documents generally result in a section break defining your new area. However, if your merge template ALSO has section breaks, then you have to know that so that you can change the counter to "skip" the sections which don't matter.

That's all the info I can give you without you uploading a document.

Good luck!

AlexW
08-08-2011, 05:46 AM
Thanks Frosty.

macropod
08-14-2011, 05:05 AM
Just a quick comment:

If it's a standard letter merge, you can work with Sections instead of pages, since Word will insert a 'next page' Section break between the letters. That means you don't need to worry about whether a given letter spans more than one page. So:
For iCounter = 1 To CInt(Selection.Information(wdNumberOfPagesInDocument))
becomes:
For iCounter = 1 To ActiveDocument.Sections.Count
and:
From:=iCounter, To:=iCounter
becomes:
From:="s" & iCounter, To:="s" & iCounter

Of course, if you've got multiple Sections per letter, the process is a little more complicated, but not much.