vbams-word

Insert a Cover Page using vba


I am trying to develop vba that inserts a Cover Page that is identical to that inserted when one uses the Insert Cover Page command in the UI.

In current Windows versions of Word (2016+) this

enter image description here

The best I've been able to come up with is the following vba which uses a Section break. The page will be counted in the NumPages field as well as the Pages document property. I've explored this in my writing in my chapter on Sections as Cover Page Anomalies.

Selection.HomeKey Unit:=wdStory
Selection.InsertBreak Type:=wdSectionBreakNextPage
Selection.EndKey Unit:=wdStory
Selection.HomeKey Unit:=wdStory
With Selection.Sections(1).Headers(1).PageNumbers
    .NumberStyle = wdPageNumberStyleArabic
    .HeadingLevelForChapter = 0
    .IncludeChapterNumber = False
    .ChapterPageSeparator = wdSeparatorHyphen
    .RestartNumberingAtSection = True
    .StartingNumber = 0
End With
With ActiveDocument
    .Sections(2).Headers(wdHeaderFooterFirstPage).LinkToPrevious = False
    .Sections(2).Footers(wdHeaderFooterFirstPage).LinkToPrevious = False
    .Sections(1).PageSetup.DifferentFirstPageHeaderFooter = True
    .Sections(1).Headers(wdHeaderFooterFirstPage).Range.Text = ""
    .Sections(1).Footers(wdHeaderFooterFirstPage).Range.Text = ""
End With

I do not know of any Word command or vba command that corresponds to the user interface command. Is there a way to use vba to insert a Cover Page that gives the same result as using the command in the UI?


Solution

  • You could try the following as a starting point for the Cover Page insertion, assuming that you want to insert the built-in cover page called "Whisp". You may still have to deal with the First Page section issue etc.

    Sub InsertWhispCoverPage()
    Dim bb As Word.BuildingBlock
    Dim d As Word.Document
    Set d = ActiveDocument
    Set bb = getCPBBOrNothing("Whisp")
    If bb Is Nothing Then
      Debug.Print "Could not find the specified cover page"
    Else
      ' Perhaps need to deal with things at the beginning of the doc. that cannot be overwritten
      ' but if we don't insert the Page Break first, 
      ' the Cover Page layout will probably break, e.g. one or
      ' more of the floating content controls may be re-anchored
      ' on the next page. It looks like trying any other approach
      ' would make it almost impossible to get the Page back to how
      ' it should be.
      d.Range(0, 0).InsertBreak Word.WdBreakType.wdPageBreak
      bb.Insert where:=d.Range(0, 0), RichText:=True
    End If
    Set d = Nothing
    Set bb = Nothing
    End Sub
    
    Function getCPBBOrNothing(BBName As String) As BuildingBlock
    Dim i As Long
    Dim j As Long
    Dim k As Long
    With Application.Templates
      .LoadBuildingBlocks
      For i = 1 To .Count
        With .Item(i).BuildingBlockTypes(wdTypeCoverPage).Categories
          For j = 1 To .Count
            With .Item(j).BuildingBlocks
              For k = 1 To .Count
                If .Item(k).Name = "Whisp" Then
                  Debug.Print .Item(k).Category.Name, .Item(k).Type.Name
                  Set getCPBBOrNothing = .Item(k)
                  Exit Function
                End If
              Next
            End With
          Next
        End With
      Next
    End With
    End Function
    

    This raises several questions, such as "how are you actually deciding which cover page to use", "are you actually trying to do this without using a cover page?", "what does Word do with pagination and headers with various different document layouts, e.g. double-sided, facing pages layouts", and so on.. I feel sure that the building blocks could be handled better than I have done.

    The problem if you are trying to avoid using a Cover Page Building Block is that as @TimothyRylatt mentioned, it's the building block that triggers the page numbering behaviour. I had a look at the inserted OOXML and the Document Part starts something like this

    <w:body>
      <w:sdt>
        <w:sdtPr>
          <w:id w:val="1382976450"/>
          <w:docPartObj>
            <w:docPartGallery w:val="Cover Pages"/>
            <w:docPartUnique/>
          </w:docPartObj>
        </w:sdtPr>
        <w:sdtContent>
    

    followed by the content of the sdt, then closing with

        </w:sdtContent>
      </w:sdt>
    

    and eventually

    </w:body>
    

    You might be able to simulate having a suitable Building Block Building Block and inserting that. I haven't tried. Failing that, I have now tried inserting the necessary <w:sdt> code using Range.InsertXML. It seems to work. I think the most you need is

      <w:sdt>
        <w:sdtPr>
          <w:docPartObj>
            <w:docPartGallery w:val="Cover Pages"/>
            <w:docPartUnique/>
          </w:docPartObj>
        </w:sdtPr>
      </w:sdt>
    

    You have to have the w:val attribute in the DocPartGalleryElement and it has to be set to "Cover Pages"

    Some code for doing that, starting with an empty document:

    Sub insertCPsdt()
    Dim sa As String ' Essential Flat OPC format "head"
    Dim s As String ' The document content you want to insert
    Dim sz As String ' Essential Flat OPC format "tail"
    
    sa = ""
    sa = sa & "<pkg:package xmlns:pkg=""http://schemas.microsoft.com/office/2006/xmlPackage"">" & vbCrLf
    sa = sa & "  <pkg:part pkg:name=""/_rels/.rels"" pkg:contentType=""application/vnd.openxmlformats-package.relationships+xml"" pkg:padding=""512"">" & vbCrLf
    sa = sa & "    <pkg:xmlData>" & vbCrLf
    sa = sa & "      <Relationships xmlns=""http://schemas.openxmlformats.org/package/2006/relationships"">" & vbCrLf
    sa = sa & "        <Relationship Id=""rId1"" Type=""http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"" Target=""word/document.xml""/>" & vbCrLf
    sa = sa & "      </Relationships>" & vbCrLf
    sa = sa & "   </pkg:xmlData>" & vbCrLf
    sa = sa & "  </pkg:part>" & vbCrLf
    sa = sa & "  <pkg:part pkg:name=""/word/document.xml"" pkg:contentType=""application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"">" & vbCrLf
    sa = sa & "    <pkg:xmlData>" & vbCrLf
    sa = sa & "      <w:document xmlns:w=""http://schemas.openxmlformats.org/wordprocessingml/2006/main"">" & vbCrLf
    sa = sa & "        <w:body>" & vbCrLf
    
    s = "<w:sdt><w:sdtPr><w:docPartObj><w:docPartGallery w:val=""Cover Pages""/></w:docPartObj></w:sdtPr></w:sdt>" & vbCrLf
            
    sz = ""
    sz = sz & "        </w:body>" & vbCrLf
    sz = sz & "      </w:document>" & vbCrLf
    sz = sz & "    </pkg:xmlData>" & vbCrLf
    sz = sz & "  </pkg:part>" & vbCrLf
    sz = sz & "</pkg:package>" & vbCrLf
    
    ActiveDocument.Range(0, 0).InsertXML sa & s & sz
    End Sub
    

    This was all done in the current version of Word 365 on Windows. The Cover Page XML contains a fair amount of "compatibility mode" XML which may make some differences to the page behaviour in different versions, but my guess is that it is the version of Word itself that decides about the page numbering and not something in the compatibility code that tells it to do so.