2

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.

  • salesforce.stackexchange.com/questions/153315/… Has another idea in there as well that may be of interest. What's going on in the VF page that might push it to different page lengths? – Kris Goncalves Oct 3 '19 at 15:27
  • Wrapper class of 8 lists of repeats. Each list can have no size, or multiple records with fields to disperse through the document. Thus causing it to add more pages as neccessary and have no defined page break. – JRiffe Oct 3 '19 at 15:47
  • Also @KrisGoncalves I looked at the linked example, and there is no :last selector that you can use. You can kind of do it with named pages, which I forgot I also tried, but it ADDED a last page to put that specifically on it. Which isn't what I desired, as I want it on the last page with content. – JRiffe Oct 3 '19 at 15:50
  • You'll need to use a class that paginates for your controller that has a counter to find your last page. There's only a "first page" pseudoclass with CSS2. – crmprogdev Oct 3 '19 at 19:05
0

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>
| improve this answer | |
  • If I'd had time when I posted my comment, I would have pointed you to an old blog post or other Salesforce Documentation that existed which I've seen which showed the complete solution for this. unfortunately, I only had time for the comment. If you search, you should find the pagination code for the controller. – crmprogdev Oct 6 '19 at 0:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.