[SalesForce] Place image on last page in PDF document

I have a Visualforce page rendered as PDF with a static first page and dynamic pages following.

I have a header and footer set and they are working as expected. My issue is trying to place an image above the footer on the last page. Which could be page 3 or page 4 etc.

<apex:page standardController="Account" extensions="myExtension" 
           standardStylesheets="false" docType="html-5.0" sidebar="false" showHeader="false" 
           renderAs="pdf" applyBodyTag="false" applyHtmlTag="false">

    <style type="text/css" media="print">
         @page {
             @bottom-left {
                  content: element(footer, first));
             }
              @bottom-right{
                  content: "Page " counter(page) " of " counter(pages);
                  font-size: small;
                  color: gray;
               }
          }

          div.footer {
               position: running(footer);
               font-size: small;
               color: gray;
          }
          .last-page-image {
                position: fixed;
                right: 0;
                bottom: 0;
            }

            .bottom-image {
                width: 350px;
            }
    </style>
    <body>
         <div class="footer">
             <apex:outputText value="Report Date: {!NOW()}"/>
         </div>
         <div class="page-1">
             fixed content
             page-break
         </div>
         <div class="page-2">
             content to span multiple pages without specified page breaks
         </div>
    </body>
</apex:page>

Attempt 1: as depicted above where I set the image as fixed and to the bottom, which will place the image in the correct place, but on every page as opposed to the last page.

Attempt 2: @page :last as recommended in many many places. This is not a valid selector and does nothing.

Attempt 3: Set height of html and body to 100%, use a wrapper div that has a height of 100% that 'should' span the size of the given page space, however, regardless of when I use it in a full document div wrapper or page2 div wrapper, the height remains the size of the content, OR this size of the first page that 'page2' begins on. This was through setting various things as relative and absolute, though I could never get my box to extend to 2nd page content if it auto page broke to another page.

Attempt 4: Most intriguing with little to no examples, which is also still in the code below use an optional argument in the element(). According to HERE under the running elements section, there is a little except about using arguments. I have attempted this and did not get it to work as expected. 1: I have found in VF rendering as PDF you cannot use chained classes header once. Though, I think if anyone has experience with this, this could be a viable option where on the last header last is an acceptable argument and footer in my case, I could add an additional running footer with my image in it. I also don't know if this is a CSS3 or CSS2.1 option as I have a hard time finding versions.

This code illustrates how to change the running header on one page in the middle of a run of pages:

...
<style>
@page { @top-center {
  content: element(header, first) }}
.header { position: running(header) }
.once { font-weight: bold }
</style>
...
<div class="header">Not now</div>
<p>Da di ha di da di ...
  <span class="header once">NOW!</span>
  <span class="header">Not now</span>
  ... da di ha di hum.</p>
...
The header is "Not now" from the outset, due to the "div" element. The first "span" element changes it to "NOW!" on the page where the "span" element would have appeared. The second "span" element, which would have appeared on the same page as the first is not used because the ''first'' keyword has been specified. However, the second "span" element still sets the exit value for "header" and this value is used on subsequent pages.

Theoretical option 4 that I don't know how to implement would be to use a custom counter? Theoretical and not attempted at this point due to late night musings.

So, at the end of the day, I want my image to be just above the last footer, whether it happens via paged media queries or document height. I would just like a workable solution if any of you super smart developers have done this.

Best Answer

I finally figured it out! This was a continuation of Attempt 4 above using the optional argument of last. Removing the string values of Page of pages and pushing everything into my footer div. I just added another footer at the end of the document which should update the values in the running footer element.

The element() value of the content property places an element (which has been removed from the normal flow via running()) in a page margin box. Whenever the value of the element changes, the value of element() is updated.

Just as with string(), element() takes an optional keyword to describe which value should be used in the case of multiple assignments on a page. User agents must be able to recall many values, as element() can return past, current, or future values of the assignment.

Sources: CSS Generated Content for Paged Media Module and This Stack Overflow Question

<style type="text/css" media="print">
     @page {
         @bottom-left {
              content: element(footer);
         }

         @bottom-left{
              content: element(footer, last);
                }
         }

        .pageNumber:before {
             content: counter(page);
        }

        .pageCount:before {
            content: counter(pages);
        }

        div.footer {
            position: running(footer);
            font-size: small;
            color: gray;
        }

        div.subheader-right {
            float: right;
        }
</style>

<body>
    <div class="footer">
        <apex:outputText value="Report Date: {!NOW()}"/>

        <div class="subheader-right">
            Page <span class="pageNumber"/> of <span class="pageCount"></span>
        </div>
    </div>

    ........ content ..........

    <div class="footer">
        <div class="last-page-image">
            <apex:image styleClass="bottom-image" style="z-index: 2000;" url="myImageUrl" />
         </div>
         <apex:outputText value="Report Date: {!NOW()}"/>

         <div class="subheader-right">
             Page <span class="pageNumber"/> of <span class="pageCount"></span>
         </div>
    </div>
</body>
Related Topic