---
title: Add Header and Footer to a PDF Document
product: PDF Generator
description: Boost your brand awareness with custom headers and footers in your PDF forms. This free demo for JavaScript will show you how to add your brand logo, slogan, or annotations to give your PDF forms a professional and personalized look.
framework: Angular
source: https://surveyjs.io/pdf-generator/examples/customize-header-and-footer-of-pdf-form/angular
index: https://surveyjs.io/pdf-generator/examples/overview.md
---

# Add Header and Footer to a PDF Document (Angular)

You can improve the appeal of your PDF forms or simply make their contents more clear to respondents by customizing their header and footer with annotations, page numbers, a brand logo, titles, and more. This example shows how you can add a brand logo or fully custom header and footer to a PDF form.

## Add a Brand Logo to Your PDF Form

SurveyJS Form Library allows you to add a logo to your survey on a web page. This logo can be exported to PDF without additional configuration.

To add a logo, assign a Base64 or URL string value to the [`logo`](https://surveyjs.io/form-library/documentation/api-reference/survey-data-model#logo) property in the survey JSON schema. You can also specify other logo parameters:

- [`logoHeight`](https://surveyjs.io/form-library/documentation/api-reference/survey-data-model#logoHeight) and [`logoWidth`](https://surveyjs.io/form-library/documentation/api-reference/survey-data-model#logoWidth)          
Set the height and width of a logo in CSS-accepted values.

- [`logoFit`](https://surveyjs.io/form-library/documentation/api-reference/survey-data-model#logoFit): `"contain"` (default) | `"cover"` | `"fill"` | `"none"`          
Specifies how a logo should be resized to fit its container.

- [`logoPosition`](https://surveyjs.io/form-library/documentation/api-reference/survey-data-model#logoPosition): `"left"` (default) | `"right"` | `"none"`           
Specifies the position of a logo relative to the survey title.

This example demonstrates how to embed a Base64 logo in a survey JSON schema and display the logo in the survey header. Click "Save as PDF" to export a PDF form with the logo.

## Customize Header and Footer in a PDF Form

With SurveyJS PDF Generator, you can create a fully custom header and footer for your PDF form. To do this, implement the [`onRenderHeader`](https://surveyjs.io/pdf-generator/documentation/api-reference/surveypdf#onRenderHeader) and [`onRenderFooter`](https://surveyjs.io/pdf-generator/documentation/api-reference/surveypdf#onRenderFooter) event handlers. They accept a [`DrawCanvas`](https://surveyjs.io/pdf-generator/documentation/api-reference/drawcanvas) object as the second parameter. Use this object to draw images or text within a PDF page header or footer.

In this demo, a `DrawCanvas` method, [`drawImage`()](https://surveyjs.io/pdf-generator/documentation/api-reference/drawcanvas#drawImage), renders a SurveyJS logo in the PDF page header. Another `DrawCanvas` method, [`drawText()`](https://surveyjs.io/pdf-generator/documentation/api-reference/drawcanvas#drawText), renders page numbers and additional information in the footer.

## Files

### `src/app/components/json.ts`

```ts
export const json = {
  "title": "NPS Survey Question",
  "description": "NPS (net promoter score) is a metric used to evaluate customer loyalty and business growth opportunities. To measure NPS, respondents should rate on a scale of 0 to 10 how likely they would recommend your product or service to a friend or colleague.",
  "logo": "https://api.surveyjs.io/private/Surveys/files?name=52f2ca1e-9604-4d41-a887-0ee358b37625",
  "logoHeight": "80px",
  "logoFit": "cover",
  "pages": [
    {
      "name": "page1",
      "elements": [
        {
          "type": "panel",
          "name": "panel1",
          "elements": [
            {
              "type": "rating",
              "name": "nps-score",
              "title": "On a scale from 0 to 10, how likely are you to recommend us to a friend or colleague?",
              "defaultValue": 9,
              "rateCount": 11,
              "rateMin": 0,
              "rateMax": 10
            },
            {
              "type": "comment",
              "name": "improvements-required",
              "title": "What can we do to make your experience more satisfying?",
              "maxLength": 300
            },
            {
              "type": "checkbox",
              "name": "promoter-features",
              "title": "Which of the following features do you value the most?",
              "description": "Please select no more than three features.",
              "isRequired": true,
              "choices": [
                {
                  "value": "performance",
                  "text": "Performance"
                },
                {
                  "value": "stability",
                  "text": "Stability"
                },
                {
                  "value": "ui",
                  "text": "User interface"
                },
                {
                  "value": "complete-functionality",
                  "text": "Complete functionality"
                },
                {
                  "value": "learning-materials",
                  "text": "Learning materials (documentation, demos, code examples)"
                },
                {
                  "value": "support",
                  "text": "Quality support"
                }
              ],
              "showOtherItem": true,
              "otherPlaceholder": "Please specify...",
              "otherText": "Other features",
              "colCount": 2,
              "maxSelectedChoices": 3
            }
          ]
        }
      ]
    },
    {
      "name": "page2",
      "elements": [
        {
          "type": "boolean",
          "name": "rebuy",
          "title": "Would you buy our product again?"
        }
      ]
    },
    {
      "name": "page3",
      "elements": [
        {
          "type": "radiogroup",
          "name": "testimonial",
          "title": "Would you mind providing us a brief testimonial for the website?",
          "choices": [
            {
              "value": "yes",
              "text": "Sure!"
            },
            {
              "value": "no",
              "text": "No"
            }
          ]
        },
        {
          "type": "text",
          "name": "email",
          "title": "What is your email address?",
          "validators": [
            {
              "type": "email"
            }
          ],
          "placeholder": "Enter your email here"
        }
      ]
    }
  ],
  "showPrevButton": false,
  "questionsOnPageMode": "singlePage",  
  "headerView": "advanced"
};
```

### `src/app/components/pdf.component.css`

```css
/* You can add your custom CSS here. */
```

### `src/app/components/pdf.component.html`

```html
<survey [model]="model"></survey>
```

### `src/app/components/pdf.component.ts`

```ts
import { Component, OnInit } from "@angular/core";
import { Model } from "survey-core";
import { SurveyPDF } from "survey-pdf";
import { json } from "./json";
import "./pdf.component.css";
import "survey-core/survey-core.min.css";

function createSurveyPdfModel (surveyModel) {
    const surveyPDF = new SurveyPDF(json);
    if (surveyModel) {
        surveyPDF.data = surveyModel.data;
        surveyPDF.locale = surveyModel.locale;
        
        
    }
    // Adds the Generated on date note aligned right
    surveyPDF.onRenderHeader.add((_, canvas) => {
        canvas.drawText({
            text: "Generated on: " + new Date().toLocaleDateString(),
            fontSize: 9,
            horizontalAlign: "right",
            verticalAlign: "top",
            margins: { top: 12, right: 12 }
        });
    });

    // Adds custom text and page information to the footer area
    surveyPDF.onRenderFooter.add((_, canvas) => {
        canvas.drawText({
            text: "Created by SurveyJS PDF Generator",
            fontSize: 10
        });
        canvas.drawText({
            text: "Page " + canvas.pageNumber + " of " + canvas.pageCount,
            fontSize: 10,
            horizontalAlign: "right",   
            margins: { right: 12 }      
        });
    });
    return surveyPDF;
}
function saveSurveyToPdf (filename, surveyModel) {
    const pdfDoc = createSurveyPdfModel(surveyModel);
    if (!pdfDoc) return;

    pdfDoc.save(filename);
}

const previewDiv = document.getElementById("pdf-preview");
const previewEmbed = document.getElementById("pdf-preview-frame");
let pdfBlobUrl;

function showPdfPreview(surveyModel) {
    if (!previewDiv || !previewEmbed) return;

    const pdfDoc = createSurveyPdfModel(surveyModel);
    if (!pdfDoc) return;

    pdfDoc.raw("blob").then((blob) => {
        if (pdfBlobUrl) {
            URL.revokeObjectURL(pdfBlobUrl);
        }
        pdfBlobUrl = URL.createObjectURL(blob);
        previewEmbed.setAttribute("src", pdfBlobUrl);
        previewDiv.style.display = "block";
    });
}

function previewPdf(surveyModel) {
    if (!previewDiv) return;

    if (previewDiv.style.display !== "none") {
        previewDiv.style.display = "none";
        return;
    }

    showPdfPreview(surveyModel);
}

@Component({
    // tslint:disable-next-line:component-selector
    selector: "component-survey",
    templateUrl: "./pdf.component.html",
    styleUrls: ["./pdf.component.css"]
})
export class SurveyPdfComponent implements OnInit {
    model: Model;
    ngOnInit() {
        const survey = new Model(json);
        survey.showCompleteButton = false;
        survey.navigationButtonsLocation = "topBottom";
        survey.addNavigationItem({
            id: "survey_save_as_file",
            title: "Download PDF",
            action: () => {
                saveSurveyToPdf("surveyResult.pdf", survey);
            }
        });
        survey.addNavigationItem({
            id: "survey_pdf_preview",
            title: "Preview PDF",
            visible: window.navigator.pdfViewerEnabled,
            action: () => {
                previewPdf(survey);
            }
        });
        survey.data = {
            "nps-score": 9
        };
        this.model = survey;
    }
}
```

### `src/app/components/theme.ts`

```ts
export const themeJson = {};
```

### `src/app/app.component.css`

```css

```

### `src/app/app.component.html`

```html
<component-survey></component-survey>
```

### `src/app/app.component.ts`

```ts
import { Component } from "@angular/core";

@Component({
    selector: "app-root",
    templateUrl: "./app.component.html",
    styleUrls: ["./app.component.css"]
})
export class AppComponent {
    title = "CodeSandbox";
}
```

### `src/app/app.module.ts`

```ts
import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";

import { AppComponent } from "./app.component";
import { SurveyModule } from "survey-angular-ui";
import { SurveyPdfComponent } from "./components/pdf.component";

@NgModule({
    declarations: [AppComponent, SurveyPdfComponent],
    imports: [BrowserModule, SurveyModule],
    providers: [],
    bootstrap: [AppComponent]
})
export class AppModule { }
```

### `src/environments/environment.prod.ts`

```ts
export const environment = {
    production: true
};
```

### `src/environments/environment.ts`

```ts
// The file contents for the current environment will overwrite these during build.
// The build system defaults to the dev environment which uses `environment.ts`, but if you do
// `ng build --env=prod` then `environment.prod.ts` will be used instead.
// The list of which env maps to which file can be found in `.angular-cli.json`.

export const environment = {
    production: false
};
```

### `src/index.html`

```html
<div style="display: flex;">
    <div id="surveyElement" style="flex: 1 1 0%; height: 100%; min-width: 0;">
        <app-root></app-root>
    </div>
    <div id="pdf-preview" style="flex: 0.7 0.7 0%; display: none">
        <embed id="pdf-preview-frame" type="application/pdf" style="width:100%; height:100%;" />
    </div>
</div>
```

### `src/main.ts`

```ts
import { enableProdMode } from "@angular/core";
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";

import { AppModule } from "./app/app.module";
import { environment } from "./environments/environment";

if (environment.production) {
    enableProdMode();
}

platformBrowserDynamic()
    .bootstrapModule(AppModule)
    .catch(err => console.log(err));
```

### `src/polyfills.ts`

```ts
/**
 * This file includes polyfills needed by Angular and is loaded before the app.
 * You can add your own extra polyfills to this file.
 *
 * This file is divided into 2 sections:
 *   1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
 *   2. Application imports. Files imported after ZoneJS that should be loaded before your main
 *      file.
 *
 * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
 * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
 * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
 *
 * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
 */

/***************************************************************************************************
 * BROWSER POLYFILLS
 */

/** IE9, IE10 and IE11 requires all of the following polyfills. **/
// import 'core-js/es6/symbol';
// import 'core-js/es6/object';
// import 'core-js/es6/function';
// import 'core-js/es6/parse-int';
// import 'core-js/es6/parse-float';
// import 'core-js/es6/number';
// import 'core-js/es6/math';
// import 'core-js/es6/string';
// import 'core-js/es6/date';
// import 'core-js/es6/array';
// import 'core-js/es6/regexp';
// import 'core-js/es6/map';
// import 'core-js/es6/weak-map';
// import 'core-js/es6/set';

/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js';  // Run `npm install classlist.js`.

/** IE10 and IE11 requires the following for the Reflect API. */
// import 'core-js/es6/reflect';

/** Evergreen browsers require these. **/
// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
import "core-js/proposals/reflect-metadata";

/**
 * Required to support Web Animations `@angular/platform-browser/animations`.
 * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
 **/
// import 'web-animations-js';  // Run `npm install web-animations-js`.

/***************************************************************************************************
 * Zone JS is required by default for Angular itself.
 */
import "zone.js/dist/zone"; // Included with Angular CLI.

/***************************************************************************************************
 * APPLICATION IMPORTS
 */
```

### `src/styles.css`

```css

```

### `src/typings.d.ts`

```ts
/* SystemJS module definition */
declare var module: NodeModule;
interface NodeModule {
    id: string;
}
```

### `.angular-cli.json`

```json
{
  "apps": [
    {
      "root": "src",
      "outDir": "dist",
      "assets": [ "assets", "favicon.ico" ],
      "index": "index.html",
      "main": "main.ts",
      "polyfills": "polyfills.ts",
      "prefix": "app",
      "styles": [ "styles.css" ],
      "scripts": [ ],
      "environmentSource": "environments/environment.ts",
      "environments": {
        "dev": "environments/environment.ts",
        "prod": "environments/environment.prod.ts"
      }
    }
  ]
}
```

### `_tsconfig.json`

```json
{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "allowSyntheticDefaultImports": true,
    "downlevelIteration": true,
    "experimentalDecorators": true,
    "moduleResolution": "node",
    "importHelpers": true,
    "resolveJsonModule": true,
    "target": "es2015",
    "module": "es2020",
    "lib": [
      "es2018",
      "dom"
    ]
  }
}
```

### `package.json`

```json
{
  "name": "surveyjs-angular",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build --prod",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "14.1.1",
    "@angular/cdk": "14.1.1",
    "@angular/common": "14.1.1",
    "@angular/compiler": "14.1.1",
    "@angular/core": "14.1.1",
    "@angular/forms": "14.1.1",
    "@angular/platform-browser": "14.1.1",
    "@angular/platform-browser-dynamic": "14.1.1",
    "@angular/router": "14.1.1",
    "core-js": "3.6.4",
    "rxjs": "6.5.4",
    "survey-core": "latest",
    "survey-angular-ui": "latest",
    "survey-pdf": "latest",
    "tslib": "1.13.0",
    "zone.js": "0.11.7"
  },
  "devDependencies": {
    "@angular/cli": "1.6.6",
    "@angular/compiler-cli": "^5.2.0",
    "@angular/language-service": "^5.2.0",
    "@types/core-js": "0.9.46",
    "@types/jasmine": "~2.8.3",
    "@types/jasminewd2": "~2.0.2",
    "@types/node": "~6.0.60",
    "codelyzer": "^4.0.1",
    "jasmine-core": "~2.8.0",
    "jasmine-spec-reporter": "~4.2.1",
    "karma": "~2.0.0",
    "karma-chrome-launcher": "~2.2.0",
    "karma-coverage-istanbul-reporter": "^1.2.1",
    "karma-jasmine": "~1.1.0",
    "karma-jasmine-html-reporter": "^0.2.2",
    "protractor": "~5.1.2",
    "ts-node": "~4.1.0",
    "tslint": "~5.9.1",
    "typescript": "3.9.7"
  },
  "keywords": [ "angular", "surveyjs" ],
  "description": "SurveyJS-Angular example project"
}
```

## Other Frameworks

- [React](https://surveyjs.io/pdf-generator/examples/customize-header-and-footer-of-pdf-form/reactjs.md)
- [Vue 3](https://surveyjs.io/pdf-generator/examples/customize-header-and-footer-of-pdf-form/vue3js.md)
- [jQuery](https://surveyjs.io/pdf-generator/examples/customize-header-and-footer-of-pdf-form/jquery.md)
- [Vanilla JS](https://surveyjs.io/pdf-generator/examples/customize-header-and-footer-of-pdf-form/vanillajs.md)
