Documentation

Fill an existing PDF

An application form, a contract with fields: inject(JasyForms) reads and fills the form of a PDF you already have. The source is a URL, the File of an <input type="file">, or bytes. It works in the browser and on the server.

The PDF reader is loaded on first use, so an app that only creates PDFs never downloads it.

Live from an Angular form

filledPdfResource() fills the PDF again whenever its values change, and loads the source only once. Like pdfResource, it has latest() for the preview.

// application.ts
import { Component, inject } from "@angular/core";
import { FormControl, FormGroup, ReactiveFormsModule } from "@angular/forms";
import { toSignal } from "@angular/core/rxjs-interop";
import { JasyForms, JasyPdf, filledPdfResource } from "@jasy/angular";

const SOURCE = "/forms/application.pdf";

@Component({
  selector: "app-application",
  imports: [ReactiveFormsModule],
  template: `
    <form [formGroup]="form">
      <input formControlName="full_name" />
      <input type="checkbox" formControlName="agree" />
    </form>
    @if (preview.latest(); as pdf) {
      <iframe [src]="pdf.safeUrl" title="Application"></iframe>
    }
    @if (preview.error(); as error) {
      <p>{{ error.message }}</p>
    }
    <button (click)="send()">Download</button>
  `,
})
export class Application {
  private readonly forms = inject(JasyForms);
  private readonly pdf = inject(JasyPdf);

  readonly form = new FormGroup({
    full_name: new FormControl(""),
    agree: new FormControl(false),
  });

  // The PDF fills itself as the user types. The source is loaded once.
  readonly preview = filledPdfResource(
    SOURCE,
    toSignal(this.form.valueChanges, { initialValue: this.form.value }),
  );

  async send() {
    // flatten: the values become page content and can no longer be edited.
    const { bytes } = await this.forms.fill(SOURCE, this.form.value, { flatten: true });
    await this.pdf.download(bytes, "application.pdf");
  }
}

The field names are the PDF's own. inject(JasyForms).read(source) lists them, with their types and choices.

inject(JasyForms)

MethodDoes
read(source, options?)The fields: names, types, current values, choices.
fill(source, values, options?)Fills the fields by name. { flatten: true } freezes them in one step.
flatten(source, options?)Turns the fields into page content.

read() returns undefined for a PDF without a form. A value that is undefined leaves its field as it is, the way an Angular form leaves out a disabled control. A number is written as its text, as a number input gives it. The options are those of the engine, such as password for an encrypted PDF: see Filling a form somebody else made.

A URL goes through HttpClient, so your interceptors apply - an auth header reaches a protected form.

filledPdfResource()

filledPdfResource(source, values, { options?, injector? })
  • source is a URL, a Blob or bytes, or a function returning one. It is loaded once per source, not once per change.
  • values is read like resource({ params }). Return undefined from it, or from source, to wait.
  • options are the fill options, and a function is tracked like values.

It is the same resource as pdfResource, latest() included, and like it, it stays idle on the server.