---
title: "Tools & defaults — Angular"
description: "Change what each tool draws, add your own tools, and choose the cursor, the preview and what happens after drawing."
framework: "Angular"
source: "https://www.embedpdf.com/docs/headless/angular/annotations/tools"
---

# Tools & defaults

A tool says what a click or a drag on the page creates: the kind of annotation, its default
colors and sizes, the cursor, and whether a preview follows the pointer. Change the built-in
tools, or add your own, with the `tools` setting of `withAnnotation()`:

```ts
withAnnotation({
  tools: [
    { id: 'ink', defaults: { color: '#1e90ff', strokeWidth: 3 } },
    {
      id: 'arrow',
      extends: 'line',
      defaults: { lineEndings: { start: 'none', end: 'closed-arrow' } },
    },
  ],
});
```

The first entry changes the built-in `ink` tool. The second adds an `arrow` tool: a line with an
arrowhead. Activate a tool with `interaction.activateTool('arrow')`, as on the
[overview](../annotations#let-people-draw).

## Change the default color, width and font

A tool's `defaults` are the fields every annotation it draws starts with. They're the same fields
the annotation has, so the [engine's page for each kind](https://www.embedpdf.com/docs/engine/annotations) lists what you
can set:

```ts
withAnnotation({
  tools: [
    { id: 'square', defaults: { color: '#dc143c', strokeWidth: 2, interiorColor: '#ffe4e1' } },
    { id: 'highlight', defaults: { color: '#90ee90', opacity: 0.6 } },
    { id: 'free-text', defaults: { fontFamily: 'helvetica', fontSize: 16, fontColor: '#1e1e1e' } },
    { id: 'note', defaults: { icon: 'help', color: '#ffd700' } },
  ],
});
```

Defaults can be any field except where the annotation goes (`box`, points, `rect`), since that
comes from the drawing. Flags are fields too: `{ id: 'note', defaults: { locked: true } }` makes
every new note locked.

A field the tool's kind doesn't have (a `fontSize` on a square) stops the app at startup with an
error that names the tool and the field.

## Make your own tool

`extends` starts from a tool and changes what you name. The new tool keeps everything else: its
kind, cursor, preview and behavior:

```ts
withAnnotation({
  tools: [
    { id: 'red-pen', extends: 'ink', defaults: { color: '#ff0000', strokeWidth: 2 } },
    { id: 'marker', extends: 'ink-highlight', defaults: { color: '#ffa500' } },
    { id: 'todo', extends: 'note', defaults: { icon: 'key', contents: 'TODO' } },
    {
      id: 'docs-link',
      extends: 'link',
      defaults: { target: { kind: 'uri', uri: 'https://example.com/docs' } },
    },
  ],
});
```

Give a tool a label and an icon for your toolbar in `meta`. The plugin keeps it and never reads
it:

```ts
withAnnotation({
  tools: [{ id: 'red-pen', extends: 'ink', meta: { label: 'Red pen', icon: 'pen' } }],
});
```

`annotation.tools.list()` returns every tool, so a toolbar can build itself from it.

**`custom-tools.ts`**

```typescript
import {
  ChangeDetectionStrategy,
  Component,
  effect,
  inject,
  untracked,
  ViewEncapsulation,
} from '@angular/core';
import { EpdfDocument, EpdfDocumentGate, provideEmbedPdf } from '@embedpdf/angular/runtime';
import type { OpenInput } from '@embedpdf/angular/runtime';
import { EpdfPageTemplate, EpdfStage, withStage } from '@embedpdf/angular/stage';
import { EpdfRenderLayer, withRender } from '@embedpdf/angular/render';
import { EpdfInteraction, withInteraction } from '@embedpdf/angular/interaction';
import { EpdfAnnotation, EpdfAnnotationLayer, withAnnotation } from '@embedpdf/angular/annotation';
import type { AnnotationTool } from '@embedpdf/angular/annotation';
import { localEngine } from '@embedpdf/engine';

const ebook = async (): Promise<OpenInput> => {
  const response = await fetch('https://snippet.embedpdf.com/ebook.pdf');
  return { kind: 'bytes', id: 'ebook', bytes: new Uint8Array(await response.arrayBuffer()) };
};

// A toolbar that builds itself from the tools: every tool with a label gets a button.
@Component({
  selector: 'demo-toolbar',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <div class="toolbar">
      <div class="segmented" role="group" aria-label="Tool">
        <button
          type="button"
          [attr.aria-pressed]="interaction.activeToolId() === 'pointer'"
          (click)="interaction.activateTool('pointer')"
        >
          Select
        </button>
        @for (tool of labelled; track tool.id) {
          <button
            type="button"
            [attr.aria-pressed]="interaction.activeToolId() === tool.id"
            (click)="interaction.activateTool(tool.id)"
          >
            {{ label(tool) }}
          </button>
        }
      </div>
    </div>
  `,
})
export class Toolbar {
  private readonly annotation = inject(EpdfAnnotation);
  protected readonly interaction = inject(EpdfInteraction);
  private readonly document = inject(EpdfDocument);
  protected readonly labelled = this.annotation.tools
    .list()
    .filter((tool) => typeof tool.meta?.label === 'string');
  private added = false;

  constructor() {
    // On load: an arrow drawn with the arrow tool's defaults, and the arrow tool active.
    effect(() => {
      const cover = this.document.pages()[0]?.ref;
      if (this.annotation.status() !== 'ready' || !cover || this.added) return;
      this.added = true;
      untracked(() => {
        void this.annotation.create(
          cover,
          { subtype: 'line', linePoints: { start: { x: 520, y: 120 }, end: { x: 470, y: 230 } } },
          undefined,
          { tool: 'arrow' },
        );
        this.interaction.activateTool('arrow');
      });
    });
  }

  protected label(tool: AnnotationTool) {
    return String(tool.meta?.label);
  }
}

@Component({
  selector: 'demo-root',
  imports: [
    EpdfDocumentGate,
    EpdfStage,
    EpdfPageTemplate,
    EpdfRenderLayer,
    EpdfAnnotationLayer,
    Toolbar,
  ],
  providers: [
    provideEmbedPdf(
      { engine: () => localEngine(), initialDocuments: [{ source: ebook }] },
      withStage(),
      withRender(),
      withInteraction(),
      // A blue pen, an arrow, and three tools of your own. `meta` is yours: the toolbar reads its label.
      withAnnotation({
        tools: [
          {
            id: 'ink',
            defaults: { color: '#1e90ff', strokeWidth: 3 },
            meta: { label: 'Blue pen' },
          },
          {
            id: 'arrow',
            extends: 'line',
            defaults: { lineEndings: { start: 'none', end: 'closed-arrow' } },
            meta: { label: 'Arrow' },
          },
          {
            id: 'red-pen',
            extends: 'ink',
            defaults: { color: '#ff0000', strokeWidth: 2 },
            meta: { label: 'Red pen' },
          },
          {
            id: 'marker',
            extends: 'ink-highlight',
            defaults: { color: '#ffa500' },
            meta: { label: 'Marker' },
          },
          {
            id: 'todo',
            extends: 'note',
            defaults: { icon: 'key', contents: 'TODO' },
            meta: { label: 'To do' },
          },
        ],
      }),
    ),
  ],
  changeDetection: ChangeDetectionStrategy.OnPush,
  styleUrl: './custom-tools.css',
  encapsulation: ViewEncapsulation.None,
  template: `
    <ng-container *epdfDocumentGate="let document; fallback: loading">
      <demo-toolbar />
      <epdf-stage class="stage">
        <ng-template epdfPage>
          <epdf-render-layer />
          <epdf-annotation-layer />
        </ng-template>
      </epdf-stage>
    </ng-container>

    <ng-template #loading><p class="loading">Loading…</p></ng-template>
  `,
})
export class App {}
```

**`custom-tools.css`**

```css
/* Colors */
:root {
  --ink: #1a2748;
  --text: #3b4b7a;
  --muted: #5a6b92;
  --line: #e6eaf2;
  --line-strong: #dce8fc;
  --surface: #ffffff;
  --tint: #f3f7fe;
  --canvas: #f7f9fc;
  --accent: #054fb3;
  --focus: #7db6ff;
  --shadow: rgb(7 32 76 / 0.04);
}

/* The controls, above the pages */
.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 14px;
}

/* One choice of a few: the pressed button is the current one */
.segmented {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 2px;
  padding: 3px;
  background: var(--tint);
  border: 1px solid var(--line);
  border-radius: 11px;
}

.segmented button {
  font: inherit;
  font-size: 12.5px;
  font-weight: 550;
  color: var(--muted);
  background: transparent;
  border: 0;
  border-radius: 8px;
  padding: 6px 12px;
  cursor: pointer;
  white-space: nowrap;
}

.segmented button:hover {
  color: var(--ink);
}

.segmented button[aria-pressed='true'] {
  color: var(--accent);
  background: var(--surface);
  box-shadow: 0 1px 2px rgb(7 32 76 / 0.1);
}

.segmented button:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* The pages */
.stage {
  height: 420px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--canvas);
  box-shadow:
    0 1px 2px var(--shadow),
    0 18px 40px -28px rgb(7 32 76 / 0.28);
}

.loading {
  margin: 0;
  padding: 24px 0;
  text-align: center;
  font-size: 13px;
  color: var(--muted);
}
```

> **Try it:** every button but **Select** comes from a tool's `meta`. Draw with **Red pen**, then
> place a **To do** note.

Tools can also be added while the app runs. `register()` returns a function that removes the tool
again:

```ts
const remove = annotation.tools.register({
  id: 'green-pen',
  extends: 'ink',
  defaults: { color: '#00a000' },
});
```

## Let people change the defaults

A color picker or a width slider in your toolbar changes a tool's defaults for the drawings that
follow:

**`ink-color.ts`**

```typescript
import { Component, inject } from '@angular/core';
import { EpdfAnnotation } from '@embedpdf/angular/annotation';

@Component({
  selector: 'app-ink-color',
  template: `
    <input
      #picker
      type="color"
      [value]="defaults().color"
      (input)="annotation.tools.updateDefaults('ink', { color: picker.value })"
    />
  `,
})
export class InkColor {
  protected readonly annotation = inject(EpdfAnnotation);
  protected readonly defaults = this.annotation.tools.defaultsOf('ink');
}
```

`annotation.tools.defaultsOf()` updates when the defaults change, so the picker always
shows the current color. To build a whole style panel for any tool, ask for its fields; they come in
the same shape as a [style panel for the selection](selecting#a-style-panel):

```ts
const { properties, values } = annotation.tools.getProperties('ink'); // color, opacity, strokeWidth, …
```

Defaults belong to the person drawing: they're kept in this viewer and never shared with others on
the same document.

### Remember them

To keep a person's choices for next time, save them when they change:

**`remember.ts`**

```typescript
import { Component, inject } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { EpdfAnnotation } from '@embedpdf/angular/annotation';

@Component({
  selector: 'app-remember-defaults',
  template: '',
})
export class RememberDefaults {
  constructor() {
    inject(EpdfAnnotation)
      .tools.defaultsChanged$.pipe(takeUntilDestroyed())
      .subscribe(({ toolId, defaults }) => {
        localStorage.setItem(`tool:${toolId}`, JSON.stringify(defaults));
      });
  }
}
```

Then pass them back at startup:

```ts
withAnnotation({
  tools: ['ink', 'highlight'].map((id) => ({
    id,
    defaults: JSON.parse(localStorage.getItem(`tool:${id}`) ?? '{}'),
  })),
});
```

**`tool-defaults.ts`**

```typescript
import {
  ChangeDetectionStrategy,
  Component,
  effect,
  inject,
  untracked,
  ViewEncapsulation,
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { EpdfDocument, EpdfDocumentGate, provideEmbedPdf } from '@embedpdf/angular/runtime';
import type { OpenInput } from '@embedpdf/angular/runtime';
import { EpdfPageTemplate, EpdfStage, withStage } from '@embedpdf/angular/stage';
import { EpdfRenderLayer, withRender } from '@embedpdf/angular/render';
import { EpdfInteraction, withInteraction } from '@embedpdf/angular/interaction';
import { EpdfAnnotation, EpdfAnnotationLayer, withAnnotation } from '@embedpdf/angular/annotation';
import { localEngine } from '@embedpdf/engine';

const ebook = async (): Promise<OpenInput> => {
  const response = await fetch('https://snippet.embedpdf.com/ebook.pdf');
  return { kind: 'bytes', id: 'ebook', bytes: new Uint8Array(await response.arrayBuffer()) };
};

const COLORS = ['#e5484d', '#1e90ff', '#30a46c', '#1a2748'];

// The pen's color and width, for the strokes that follow.
@Component({
  selector: 'demo-pen-style',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <div class="toolbar">
      <div class="swatches" role="group" aria-label="Pen color">
        @for (color of colors; track color) {
          <button
            type="button"
            class="swatch"
            [attr.aria-label]="color"
            [attr.aria-pressed]="defaults().color === color"
            [style.background]="color"
            (click)="annotation.tools.updateDefaults('ink', { color })"
          ></button>
        }
      </div>
      <input
        #picker
        type="color"
        class="color"
        aria-label="Any color"
        [value]="defaults().color ?? '#000000'"
        (input)="annotation.tools.updateDefaults('ink', { color: picker.value })"
      />
      <label class="range">
        Width
        <input
          #width
          type="range"
          min="1"
          max="12"
          [value]="defaults().strokeWidth ?? 1"
          (input)="annotation.tools.updateDefaults('ink', { strokeWidth: width.valueAsNumber })"
        />
        <output class="readout">{{ defaults().strokeWidth }} pt</output>
      </label>
    </div>
  `,
})
export class PenStyle {
  protected readonly colors = COLORS;
  protected readonly annotation = inject(EpdfAnnotation);
  protected readonly defaults = this.annotation.tools.defaultsOf('ink');
}

// Every change is kept for next time.
@Component({
  selector: 'demo-remember-defaults',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: '',
})
export class RememberDefaults {
  constructor() {
    inject(EpdfAnnotation)
      .tools.defaultsChanged$.pipe(takeUntilDestroyed())
      .subscribe(({ toolId, defaults }) => {
        localStorage.setItem(`tool:${toolId}`, JSON.stringify(defaults));
      });
  }
}

// On load: the pen is active, with a stroke drawn in its current style.
@Component({
  selector: 'demo-start-with-the-pen',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: '',
})
export class StartWithThePen {
  private readonly annotation = inject(EpdfAnnotation);
  private readonly interaction = inject(EpdfInteraction);
  private readonly document = inject(EpdfDocument);
  private added = false;

  constructor() {
    effect(() => {
      const cover = this.document.pages()[0]?.ref;
      if (this.annotation.status() !== 'ready' || !cover || this.added) return;
      this.added = true;
      untracked(() => {
        const wave = Array.from({ length: 24 }, (_, i) => ({
          x: 300 + i * 10,
          y: 540 + Math.sin(i / 2) * 14,
        }));
        void this.annotation.create(cover, { subtype: 'ink', inkList: [wave] }, undefined, {
          tool: 'ink',
        });
        this.interaction.activateTool('ink');
      });
    });
  }
}

@Component({
  selector: 'demo-root',
  imports: [
    EpdfDocumentGate,
    EpdfStage,
    EpdfPageTemplate,
    EpdfRenderLayer,
    EpdfAnnotationLayer,
    StartWithThePen,
    RememberDefaults,
    PenStyle,
  ],
  providers: [
    provideEmbedPdf(
      { engine: () => localEngine(), initialDocuments: [{ source: ebook }] },
      withStage(),
      withRender(),
      withInteraction(),
      // The pen starts with what the reader picked last time: see RememberDefaults above.
      withAnnotation({
        tools: [{ id: 'ink', defaults: JSON.parse(localStorage.getItem('tool:ink') ?? '{}') }],
      }),
    ),
  ],
  changeDetection: ChangeDetectionStrategy.OnPush,
  styleUrl: './tool-defaults.css',
  encapsulation: ViewEncapsulation.None,
  template: `
    <ng-container *epdfDocumentGate="let document; fallback: loading">
      <demo-start-with-the-pen />
      <demo-remember-defaults />
      <demo-pen-style />
      <epdf-stage class="stage">
        <ng-template epdfPage>
          <epdf-render-layer />
          <epdf-annotation-layer />
        </ng-template>
      </epdf-stage>
    </ng-container>

    <ng-template #loading><p class="loading">Loading…</p></ng-template>
  `,
})
export class App {}
```

**`tool-defaults.css`**

```css
/* Colors */
:root {
  --ink: #1a2748;
  --text: #3b4b7a;
  --muted: #5a6b92;
  --line: #e6eaf2;
  --line-strong: #dce8fc;
  --surface: #ffffff;
  --tint: #f3f7fe;
  --canvas: #f7f9fc;
  --accent: #054fb3;
  --focus: #7db6ff;
  --shadow: rgb(7 32 76 / 0.04);
}

/* The controls, above the pages */
.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 14px;
}

/* Colors to pick from: the pressed one is the current one */
.swatches {
  display: inline-flex;
  gap: 6px;
}

.swatch {
  width: 26px;
  height: 26px;
  padding: 0;
  border: 2px solid var(--surface);
  border-radius: 50%;
  box-shadow: 0 0 0 1px var(--line);
  cursor: pointer;
}

.swatch[aria-pressed='true'] {
  box-shadow: 0 0 0 2px var(--ink);
}

.swatch:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* Any color */
.color {
  width: 34px;
  height: 30px;
  padding: 2px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 8px;
  cursor: pointer;
}

/* A slider, with its label and its value */
.range {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 550;
  color: var(--ink);
}

.range input {
  width: 110px;
  accent-color: var(--accent);
}

.readout {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

/* The pages */
.stage {
  height: 420px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--canvas);
  box-shadow:
    0 1px 2px var(--shadow),
    0 18px 40px -28px rgb(7 32 76 / 0.28);
}

.loading {
  margin: 0;
  padding: 24px 0;
  text-align: center;
  font-size: 13px;
  color: var(--muted);
}
```

> **Try it:** pick a color and a width, and draw. Reload the page: the pen still has them.

## What happens after drawing

By default the new annotation is selected, so its handles and your menu appear at once, and the
tool stays active, so people can draw another. Change it for every tool, or for one:

```ts
withAnnotation({
  afterCreate: { select: false, tool: 'stay' }, // every tool: keep drawing, don't select
  tools: [
    { id: 'stamp', afterCreate: { select: true, tool: 'default' } }, // one stamp, then back
  ],
});
```

| Setting    | What it does                                                                                  | Default  |
| :--------- | :-------------------------------------------------------------------------------------------- | :------- |
| `select`   | Select the new annotation.                                                                    | `true`   |
| `tool`     | `'stay'` keeps the tool active. `'default'` goes back to the default tool, usually `pointer`. | `'stay'` |
| `editText` | For text boxes: start typing in the new box right away.                                       | `true`   |

A tool's own `afterCreate` wins over the one for every tool.

The setting can change while the app runs. A "keep tool" button in your toolbar is one call:

```ts
annotation.updateSettings({ afterCreate: { tool: keepTool ? 'stay' : 'default' } });
```

**`after-create.ts`**

```typescript
import {
  ChangeDetectionStrategy,
  Component,
  computed,
  inject,
  ViewEncapsulation,
} from '@angular/core';
import { EpdfDocumentGate, provideEmbedPdf } from '@embedpdf/angular/runtime';
import type { OpenInput } from '@embedpdf/angular/runtime';
import { EpdfPageTemplate, EpdfStage, withStage } from '@embedpdf/angular/stage';
import { EpdfRenderLayer, withRender } from '@embedpdf/angular/render';
import { EpdfInteraction, withInteraction } from '@embedpdf/angular/interaction';
import { EpdfAnnotation, EpdfAnnotationLayer, withAnnotation } from '@embedpdf/angular/annotation';
import { localEngine } from '@embedpdf/engine';

const ebook = async (): Promise<OpenInput> => {
  const response = await fetch('https://snippet.embedpdf.com/ebook.pdf');
  return { kind: 'bytes', id: 'ebook', bytes: new Uint8Array(await response.arrayBuffer()) };
};

@Component({
  selector: 'demo-after-drawing',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <div class="toolbar">
      <button
        type="button"
        class="button"
        [attr.aria-pressed]="interaction.activeToolId() === 'square'"
        (click)="interaction.activateTool('square')"
      >
        Rectangle
      </button>
      <label class="check">
        <input
          #select
          type="checkbox"
          [checked]="afterCreate().select"
          (change)="annotation.updateSettings({ afterCreate: { select: select.checked } })"
        />
        Select it
      </label>
      <label class="check">
        <input
          #stay
          type="checkbox"
          [checked]="afterCreate().tool === 'stay'"
          (change)="
            annotation.updateSettings({ afterCreate: { tool: stay.checked ? 'stay' : 'default' } })
          "
        />
        Keep the tool
      </label>
      <button type="button" class="button" (click)="annotation.resetSettings()">Reset</button>
      <span class="spacer"></span>
      <output class="readout">
        {{ interaction.activeToolId() === 'square' ? 'Drawing' : 'Selecting' }}
      </output>
    </div>
  `,
})
export class AfterDrawing {
  protected readonly annotation = inject(EpdfAnnotation);
  protected readonly interaction = inject(EpdfInteraction);
  protected readonly afterCreate = computed(() => this.annotation.settings().afterCreate);

  constructor() {
    // The rectangle tool is active on load.
    this.interaction.activateTool('square');
  }
}

@Component({
  selector: 'demo-root',
  imports: [
    EpdfDocumentGate,
    EpdfStage,
    EpdfPageTemplate,
    EpdfRenderLayer,
    EpdfAnnotationLayer,
    AfterDrawing,
  ],
  providers: [
    provideEmbedPdf(
      { engine: () => localEngine(), initialDocuments: [{ source: ebook }] },
      withStage(),
      withRender(),
      withInteraction(),
      withAnnotation(),
    ),
  ],
  changeDetection: ChangeDetectionStrategy.OnPush,
  styleUrl: './after-create.css',
  encapsulation: ViewEncapsulation.None,
  template: `
    <ng-container *epdfDocumentGate="let document; fallback: loading">
      <demo-after-drawing />
      <epdf-stage class="stage">
        <ng-template epdfPage>
          <epdf-render-layer />
          <epdf-annotation-layer />
        </ng-template>
      </epdf-stage>
    </ng-container>

    <ng-template #loading><p class="loading">Loading…</p></ng-template>
  `,
})
export class App {}
```

**`after-create.css`**

```css
/* Colors */
:root {
  --ink: #1a2748;
  --text: #3b4b7a;
  --muted: #5a6b92;
  --line: #e6eaf2;
  --line-strong: #dce8fc;
  --surface: #ffffff;
  --tint: #f3f7fe;
  --canvas: #f7f9fc;
  --accent: #054fb3;
  --focus: #7db6ff;
  --shadow: rgb(7 32 76 / 0.04);
}

/* The controls, above the pages */
.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 14px;
}

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  font: inherit;
  font-size: 13px;
  font-weight: 550;
  line-height: 1.1;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 9px;
  padding: 8px 12px;
  cursor: pointer;
  white-space: nowrap;
  box-shadow: 0 1px 2px var(--shadow);
  transition:
    background-color 0.15s ease,
    border-color 0.15s ease;
}

.button:hover {
  background: var(--tint);
  border-color: var(--line-strong);
}

.button:active {
  transform: translateY(1px);
}

.button:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.button[aria-pressed='true'] {
  color: var(--accent);
  background: var(--tint);
  border-color: var(--line-strong);
}

.button:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  transform: none;
}

/* A switch: a checkbox with its label */
.check {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  font-weight: 550;
  color: var(--ink);
  cursor: pointer;
}

.check input {
  width: 16px;
  height: 16px;
  margin: 0;
  accent-color: var(--accent);
}

/* Pushes what follows to the right */
.spacer {
  flex: 1 1 auto;
}

.readout {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

/* The pages */
.stage {
  height: 420px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--canvas);
  box-shadow:
    0 1px 2px var(--shadow),
    0 18px 40px -28px rgb(7 32 76 / 0.28);
}

.loading {
  margin: 0;
  padding: 24px 0;
  text-align: center;
  font-size: 13px;
  color: var(--muted);
}
```

> **Try it:** draw a rectangle, then turn off **Keep the tool** and draw another: the tool goes back
> to selecting.

Annotations your code creates are never selected unless you ask for it; see
[Create an annotation](with-code#create-an-annotation).

## Click or drag

Most drawing tools also work with a single click: it places an annotation of a default size where
you click. `clickCreate` sets that size, or turns it off:

```ts
withAnnotation({
  tools: [
    { id: 'square', clickCreate: { width: 120, height: 80 } },
    { id: 'line', clickCreate: { length: 100 } },
    { id: 'circle', clickCreate: false }, // drag only
  ],
});
```

| Tool                 | A click makes                                         |
| :------------------- | :---------------------------------------------------- |
| `square`, `circle`   | 80 × 60 points, centered on the click                 |
| `line`               | 80 points long, pointing right, centered on the click |
| `free-text`          | 180 × 40 points, its top-left at the click            |
| `note`, `attachment` | A 20 × 20 icon                                        |
| `stamp`              | The stamp, 150 points wide, centered on the click     |

`anchor` says which part of it lands on the click. A box takes `'center'` (the default) or
`'top-left'`; a line takes `'center'` (the default), `'start'` or `'end'`. A line's `rotation` turns it
clockwise from pointing right, in degrees. An arrow whose tip lands where you click, pointing down:

```ts
withAnnotation({
  tools: [{ id: 'arrow', clickCreate: { length: 80, rotation: 90, anchor: 'end' } }],
});
```

**`click-create.ts`**

```typescript
import {
  ChangeDetectionStrategy,
  Component,
  computed,
  inject,
  ViewEncapsulation,
} from '@angular/core';
import { EpdfDocumentGate, provideEmbedPdf } from '@embedpdf/angular/runtime';
import type { OpenInput } from '@embedpdf/angular/runtime';
import { EpdfPageTemplate, EpdfStage, withStage } from '@embedpdf/angular/stage';
import { EpdfRenderLayer, withRender } from '@embedpdf/angular/render';
import { EpdfInteraction, withInteraction } from '@embedpdf/angular/interaction';
import { EpdfAnnotationLayer, withAnnotation } from '@embedpdf/angular/annotation';
import { localEngine } from '@embedpdf/engine';

const ebook = async (): Promise<OpenInput> => {
  const response = await fetch('https://snippet.embedpdf.com/ebook.pdf');
  return { kind: 'bytes', id: 'ebook', bytes: new Uint8Array(await response.arrayBuffer()) };
};

const TOOLS = [
  { id: 'square', label: 'Rectangle', hint: 'A click makes 120 × 80 points' },
  { id: 'arrow', label: 'Arrow', hint: 'A click points an arrow down at it' },
  { id: 'circle', label: 'Circle', hint: 'Drag only: a click does nothing' },
];

@Component({
  selector: 'demo-toolbar',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <div class="toolbar">
      <div class="segmented" role="group" aria-label="Tool">
        @for (tool of tools; track tool.id) {
          <button
            type="button"
            [attr.aria-pressed]="interaction.activeToolId() === tool.id"
            (click)="interaction.activateTool(tool.id)"
          >
            {{ tool.label }}
          </button>
        }
      </div>
      <p class="hint">{{ active()?.hint ?? 'Pick a tool, then click the page' }}</p>
    </div>
  `,
})
export class Toolbar {
  protected readonly tools = TOOLS;
  protected readonly interaction = inject(EpdfInteraction);
  protected readonly active = computed(() =>
    TOOLS.find((tool) => tool.id === this.interaction.activeToolId()),
  );

  constructor() {
    // The rectangle tool is active on load: click the page.
    this.interaction.activateTool('square');
  }
}

@Component({
  selector: 'demo-root',
  imports: [
    EpdfDocumentGate,
    EpdfStage,
    EpdfPageTemplate,
    EpdfRenderLayer,
    EpdfAnnotationLayer,
    Toolbar,
  ],
  providers: [
    provideEmbedPdf(
      { engine: () => localEngine(), initialDocuments: [{ source: ebook }] },
      withStage(),
      withRender(),
      withInteraction(),
      // What one click makes: a 120 × 80 rectangle, an arrow pointing down at the click, and no circle.
      withAnnotation({
        tools: [
          { id: 'square', clickCreate: { width: 120, height: 80 } },
          {
            id: 'arrow',
            extends: 'line',
            defaults: { lineEndings: { start: 'none', end: 'closed-arrow' } },
            clickCreate: { length: 80, rotation: 90, anchor: 'end' },
          },
          { id: 'circle', clickCreate: false }, // drag only
        ],
      }),
    ),
  ],
  changeDetection: ChangeDetectionStrategy.OnPush,
  styleUrl: './click-create.css',
  encapsulation: ViewEncapsulation.None,
  template: `
    <ng-container *epdfDocumentGate="let document; fallback: loading">
      <demo-toolbar />
      <epdf-stage class="stage">
        <ng-template epdfPage>
          <epdf-render-layer />
          <epdf-annotation-layer />
        </ng-template>
      </epdf-stage>
    </ng-container>

    <ng-template #loading><p class="loading">Loading…</p></ng-template>
  `,
})
export class App {}
```

**`click-create.css`**

```css
/* Colors */
:root {
  --ink: #1a2748;
  --text: #3b4b7a;
  --muted: #5a6b92;
  --line: #e6eaf2;
  --line-strong: #dce8fc;
  --surface: #ffffff;
  --tint: #f3f7fe;
  --canvas: #f7f9fc;
  --accent: #054fb3;
  --focus: #7db6ff;
  --shadow: rgb(7 32 76 / 0.04);
}

/* The controls, above the pages */
.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 14px;
}

/* One choice of a few: the pressed button is the current one */
.segmented {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 2px;
  padding: 3px;
  background: var(--tint);
  border: 1px solid var(--line);
  border-radius: 11px;
}

.segmented button {
  font: inherit;
  font-size: 12.5px;
  font-weight: 550;
  color: var(--muted);
  background: transparent;
  border: 0;
  border-radius: 8px;
  padding: 6px 12px;
  cursor: pointer;
  white-space: nowrap;
}

.segmented button:hover {
  color: var(--ink);
}

.segmented button[aria-pressed='true'] {
  color: var(--accent);
  background: var(--surface);
  box-shadow: 0 1px 2px rgb(7 32 76 / 0.1);
}

.segmented button:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.hint {
  margin: 0;
  font-size: 13px;
  color: var(--muted);
}

/* The pages */
.stage {
  height: 420px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--canvas);
  box-shadow:
    0 1px 2px var(--shadow),
    0 18px 40px -28px rgb(7 32 76 / 0.28);
}

.loading {
  margin: 0;
  padding: 24px 0;
  text-align: center;
  font-size: 13px;
  color: var(--muted);
}
```

> **Try it:** click the page with each tool. The arrow's tip lands where you click.

What a click makes always stays on the page: near an edge, it's moved inside.

## The cursor

While a tool is active, the pointer shows the tool's cursor. Over an annotation the viewer switches
to a move or resize cursor by itself, and over text to a text cursor, so people always see what a
click will do. You choose the cursor for the rest of the page. There are three steps, from simple
to fully yours.

**A standard cursor.** Any CSS cursor name:

```ts
withAnnotation({ tools: [{ id: 'square', cursor: 'cell' }] });
```

**An image.** `svgCursor()`, from `@embedpdf/angular/interaction`, turns an SVG into a cursor. The
SVG needs a `width` and a `height`, and `hotspot` is the point in it that clicks:

```ts
withAnnotation({
  tools: [{ id: 'ink', cursor: svgCursor({ svg: penIcon, hotspot: { x: 2, y: 22 } }) }],
});
```

**An image that follows the defaults.** `interaction.overrideCursor()` sets a tool's cursor from a component, so
it can change with the tool's color:

**`ink-cursor.ts`**

```typescript
import { Component, inject } from '@angular/core';
import { EpdfAnnotation } from '@embedpdf/angular/annotation';
import { EpdfInteraction } from '@embedpdf/angular/interaction';

import { penIcon } from './icons';

@Component({
  selector: 'app-ink-cursor',
  template: '',
})
export class InkCursor {
  private readonly defaults = inject(EpdfAnnotation).tools.defaultsOf('ink');

  constructor() {
    // Follows the color, and gives the tool its own cursor back when this component goes
    inject(EpdfInteraction).overrideCursor(() => ({
      toolId: 'ink',
      cursors: { crosshair: { svg: penIcon(this.defaults().color), hotspot: { x: 2, y: 22 } } },
    }));
  }
}
```

`cursors` replaces cursors by name: `crosshair` is the ink tool's own cursor. Name `text` too to
replace the text cursor a markup tool shows over text. The cursor goes back to the tool's own when
the component is removed.

**`cursor.ts`**

```typescript
import { ChangeDetectionStrategy, Component, inject, ViewEncapsulation } from '@angular/core';
import { EpdfDocumentGate, provideEmbedPdf } from '@embedpdf/angular/runtime';
import type { OpenInput } from '@embedpdf/angular/runtime';
import { EpdfPageTemplate, EpdfStage, withStage } from '@embedpdf/angular/stage';
import { EpdfRenderLayer, withRender } from '@embedpdf/angular/render';
import { EpdfInteraction, withInteraction } from '@embedpdf/angular/interaction';
import { EpdfAnnotation, EpdfAnnotationLayer, withAnnotation } from '@embedpdf/angular/annotation';
import { localEngine } from '@embedpdf/engine';

const ebook = async (): Promise<OpenInput> => {
  const response = await fetch('https://snippet.embedpdf.com/ebook.pdf');
  return { kind: 'bytes', id: 'ebook', bytes: new Uint8Array(await response.arrayBuffer()) };
};

// A pen, 24 × 24, in a color: its tip is the bottom-left corner.
const penIcon = (color: string) =>
  `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">` +
  `<path d="M3 21l1.5-5.5L16 4l4 4L8.5 19.5z" fill="${color}" stroke="#ffffff" stroke-width="1.5" stroke-linejoin="round"/>` +
  `</svg>`;

// The pen cursor follows the pen's color: the spec is read again when the color changes.
@Component({
  selector: 'demo-ink-cursor',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: '',
})
export class InkCursor {
  constructor() {
    const defaults = inject(EpdfAnnotation).tools.defaultsOf('ink');
    inject(EpdfInteraction).overrideCursor(() => ({
      toolId: 'ink',
      cursors: {
        crosshair: { svg: penIcon(defaults().color ?? '#000000'), hotspot: { x: 2, y: 22 } },
      },
    }));
  }
}

const COLORS = ['#e5484d', '#1e90ff', '#30a46c', '#1a2748'];

@Component({
  selector: 'demo-toolbar',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <div class="toolbar">
      <button
        type="button"
        class="button"
        [attr.aria-pressed]="interaction.activeToolId() === 'ink'"
        (click)="interaction.activateTool('ink')"
      >
        Pen
      </button>
      <div class="swatches" role="group" aria-label="Pen color">
        @for (swatch of colors; track swatch) {
          <button
            type="button"
            class="swatch"
            [attr.aria-label]="swatch"
            [attr.aria-pressed]="defaults().color === swatch"
            [style.background]="swatch"
            (click)="pick(swatch)"
          ></button>
        }
      </div>
      <button
        type="button"
        class="button"
        [attr.aria-pressed]="interaction.activeToolId() === 'square'"
        (click)="interaction.activateTool('square')"
      >
        Rectangle
      </button>
    </div>
  `,
})
export class Toolbar {
  protected readonly colors = COLORS;
  private readonly annotation = inject(EpdfAnnotation);
  protected readonly interaction = inject(EpdfInteraction);
  protected readonly defaults = this.annotation.tools.defaultsOf('ink');

  constructor() {
    // The pen is active on load: move the pointer over the page.
    this.interaction.activateTool('ink');
  }

  protected pick(swatch: string) {
    this.annotation.tools.updateDefaults('ink', { color: swatch });
    this.interaction.activateTool('ink');
  }
}

@Component({
  selector: 'demo-root',
  imports: [
    EpdfDocumentGate,
    EpdfStage,
    EpdfPageTemplate,
    EpdfRenderLayer,
    EpdfAnnotationLayer,
    InkCursor,
    Toolbar,
  ],
  providers: [
    provideEmbedPdf(
      { engine: () => localEngine(), initialDocuments: [{ source: ebook }] },
      withStage(),
      withRender(),
      withInteraction(),
      // A standard cursor for the rectangle tool: any CSS cursor name.
      withAnnotation({ tools: [{ id: 'square', cursor: 'cell' }] }),
    ),
  ],
  changeDetection: ChangeDetectionStrategy.OnPush,
  styleUrl: './cursor.css',
  encapsulation: ViewEncapsulation.None,
  template: `
    <ng-container *epdfDocumentGate="let document; fallback: loading">
      <demo-ink-cursor />
      <demo-toolbar />
      <epdf-stage class="stage">
        <ng-template epdfPage>
          <epdf-render-layer />
          <epdf-annotation-layer />
        </ng-template>
      </epdf-stage>
    </ng-container>

    <ng-template #loading><p class="loading">Loading…</p></ng-template>
  `,
})
export class App {}
```

**`cursor.css`**

```css
/* Colors */
:root {
  --ink: #1a2748;
  --text: #3b4b7a;
  --muted: #5a6b92;
  --line: #e6eaf2;
  --line-strong: #dce8fc;
  --surface: #ffffff;
  --tint: #f3f7fe;
  --canvas: #f7f9fc;
  --accent: #054fb3;
  --focus: #7db6ff;
  --shadow: rgb(7 32 76 / 0.04);
}

/* The controls, above the pages */
.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 14px;
}

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  font: inherit;
  font-size: 13px;
  font-weight: 550;
  line-height: 1.1;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 9px;
  padding: 8px 12px;
  cursor: pointer;
  white-space: nowrap;
  box-shadow: 0 1px 2px var(--shadow);
  transition:
    background-color 0.15s ease,
    border-color 0.15s ease;
}

.button:hover {
  background: var(--tint);
  border-color: var(--line-strong);
}

.button:active {
  transform: translateY(1px);
}

.button:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.button[aria-pressed='true'] {
  color: var(--accent);
  background: var(--tint);
  border-color: var(--line-strong);
}

.button:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  transform: none;
}

/* Colors to pick from: the pressed one is the current one */
.swatches {
  display: inline-flex;
  gap: 6px;
}

.swatch {
  width: 26px;
  height: 26px;
  padding: 0;
  border: 2px solid var(--surface);
  border-radius: 50%;
  box-shadow: 0 0 0 1px var(--line);
  cursor: pointer;
}

.swatch[aria-pressed='true'] {
  box-shadow: 0 0 0 2px var(--ink);
}

.swatch:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* The pages */
.stage {
  height: 420px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--canvas);
  box-shadow:
    0 1px 2px var(--shadow),
    0 18px 40px -28px rgb(7 32 76 / 0.28);
}

.loading {
  margin: 0;
  padding: 24px 0;
  text-align: center;
  font-size: 13px;
  color: var(--muted);
}
```

> **Try it:** move the pointer over the page, then pick another color. **Rectangle** shows the
> standard `cell` cursor.

## Show a preview before placing

A **preview** (a ghost) is a see-through copy of what a click would place, following the pointer. It
shows exactly where the annotation will land and how big it will be:

```ts
withAnnotation({
  tools: [
    { id: 'square', ghost: true },
    { id: 'note', ghost: { opacity: 0.3 } },
    { id: 'todo', extends: 'note', ghost: false },
  ],
});
```

The preview is the annotation the click will make, drawn with the tool's current defaults: its
color, its width, an arrow's head, a form field's border and background. Change a default and the
preview changes with it. It's half see-through unless you set `opacity`; `--epdf-ghost-opacity`
sets it from CSS for every tool (see [Colors & theming](../ui/theming)).

**`ghost.ts`**

```typescript
import {
  ChangeDetectionStrategy,
  Component,
  computed,
  inject,
  model,
  signal,
  ViewEncapsulation,
} from '@angular/core';
import { EpdfDocumentGate, provideEmbedPdf } from '@embedpdf/angular/runtime';
import type { OpenInput } from '@embedpdf/angular/runtime';
import { EpdfPageTemplate, EpdfStage, withStage } from '@embedpdf/angular/stage';
import { EpdfRenderLayer, withRender } from '@embedpdf/angular/render';
import { EpdfInteraction, withInteraction } from '@embedpdf/angular/interaction';
import { EpdfAnnotationLayer, withAnnotation } from '@embedpdf/angular/annotation';
import { localEngine } from '@embedpdf/engine';

const ebook = async (): Promise<OpenInput> => {
  const response = await fetch('https://snippet.embedpdf.com/ebook.pdf');
  return { kind: 'bytes', id: 'ebook', bytes: new Uint8Array(await response.arrayBuffer()) };
};

const TOOLS = [
  { id: 'note', label: 'Note' },
  { id: 'square', label: 'Rectangle' },
];

@Component({
  selector: 'demo-toolbar',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <div class="toolbar">
      <div class="segmented" role="group" aria-label="Tool">
        @for (tool of tools; track tool.id) {
          <button
            type="button"
            [attr.aria-pressed]="interaction.activeToolId() === tool.id"
            (click)="interaction.activateTool(tool.id)"
          >
            {{ tool.label }}
          </button>
        }
      </div>
      <label class="range">
        From CSS
        <input
          #slider
          type="range"
          min="0.1"
          max="0.9"
          step="0.1"
          [value]="opacity() ?? 0.5"
          (input)="opacity.set(+slider.value)"
        />
        <output class="readout">{{ readout() }}</output>
      </label>
      <button
        type="button"
        class="button"
        [disabled]="opacity() === null"
        (click)="opacity.set(null)"
      >
        Reset
      </button>
    </div>
  `,
})
export class Toolbar {
  /** The ghost opacity from CSS, or `null` for each tool's own. */
  readonly opacity = model.required<number | null>();

  protected readonly interaction = inject(EpdfInteraction);
  protected readonly tools = TOOLS;
  protected readonly readout = computed(() => {
    const opacity = this.opacity();
    return opacity === null ? "each tool's own" : `${Math.round(opacity * 100)}%`;
  });

  constructor() {
    // The note tool is active on load: move the pointer over the page.
    this.interaction.activateTool('note');
  }
}

@Component({
  selector: 'demo-root',
  imports: [
    EpdfDocumentGate,
    EpdfStage,
    EpdfPageTemplate,
    EpdfRenderLayer,
    EpdfAnnotationLayer,
    Toolbar,
  ],
  providers: [
    provideEmbedPdf(
      { engine: () => localEngine(), initialDocuments: [{ source: ebook }] },
      withStage(),
      withRender(),
      withInteraction(),
      // A preview for the rectangle (off by default for tools you drag), and a fainter one for notes.
      withAnnotation({
        tools: [
          { id: 'square', ghost: true },
          { id: 'note', ghost: { opacity: 0.3 } },
        ],
      }),
    ),
  ],
  changeDetection: ChangeDetectionStrategy.OnPush,
  styleUrl: './ghost.css',
  encapsulation: ViewEncapsulation.None,
  template: `
    <ng-container *epdfDocumentGate="let document; fallback: loading">
      <demo-toolbar [(opacity)]="opacity" />
      <epdf-stage class="stage" [style.--epdf-ghost-opacity]="opacity()">
        <ng-template epdfPage>
          <epdf-render-layer />
          <epdf-annotation-layer />
        </ng-template>
      </epdf-stage>
    </ng-container>

    <ng-template #loading><p class="loading">Loading…</p></ng-template>
  `,
})
export class App {
  // `--epdf-ghost-opacity` from CSS wins over each tool's own opacity, for every tool.
  protected readonly opacity = signal<number | null>(null);
}
```

**`ghost.css`**

```css
/* Colors */
:root {
  --ink: #1a2748;
  --text: #3b4b7a;
  --muted: #5a6b92;
  --line: #e6eaf2;
  --line-strong: #dce8fc;
  --surface: #ffffff;
  --tint: #f3f7fe;
  --canvas: #f7f9fc;
  --accent: #054fb3;
  --focus: #7db6ff;
  --shadow: rgb(7 32 76 / 0.04);
}

/* The controls, above the pages */
.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 14px;
}

/* One choice of a few: the pressed button is the current one */
.segmented {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 2px;
  padding: 3px;
  background: var(--tint);
  border: 1px solid var(--line);
  border-radius: 11px;
}

.segmented button {
  font: inherit;
  font-size: 12.5px;
  font-weight: 550;
  color: var(--muted);
  background: transparent;
  border: 0;
  border-radius: 8px;
  padding: 6px 12px;
  cursor: pointer;
  white-space: nowrap;
}

.segmented button:hover {
  color: var(--ink);
}

.segmented button[aria-pressed='true'] {
  color: var(--accent);
  background: var(--surface);
  box-shadow: 0 1px 2px rgb(7 32 76 / 0.1);
}

.segmented button:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* A slider, with its label and its value */
.range {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 550;
  color: var(--ink);
}

.range input {
  width: 110px;
  accent-color: var(--accent);
}

.readout {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  font: inherit;
  font-size: 13px;
  font-weight: 550;
  line-height: 1.1;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 9px;
  padding: 8px 12px;
  cursor: pointer;
  white-space: nowrap;
  box-shadow: 0 1px 2px var(--shadow);
  transition:
    background-color 0.15s ease,
    border-color 0.15s ease;
}

.button:hover {
  background: var(--tint);
  border-color: var(--line-strong);
}

.button:active {
  transform: translateY(1px);
}

.button:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.button[aria-pressed='true'] {
  color: var(--accent);
  background: var(--tint);
  border-color: var(--line-strong);
}

.button:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  transform: none;
}

/* The pages */
.stage {
  height: 420px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--canvas);
  box-shadow:
    0 1px 2px var(--shadow),
    0 18px 40px -28px rgb(7 32 76 / 0.28);
}

.loading {
  margin: 0;
  padding: 24px 0;
  text-align: center;
  font-size: 13px;
  color: var(--muted);
}
```

> **Try it:** move the pointer over the page: the note follows it, faintly. Then move the slider.

It shows only where a click would place something. Over an annotation the click selects it, and
over text a text tool selects text, so the preview hides there. It hides too when the person may
not create annotations. A tool whose click makes nothing, like ink, a polygon, or a tool with
`clickCreate: false`, never shows one. While you press, the preview stays, since letting go is
still a click; once you drag, the drawing takes its place.

It's on by default for tools you use by clicking: stamps, notes, attachments, and the form
plugin's field tools, which show the field a click places (a radio button round, as it will be).
It's off for tools you mostly drag, like shapes and lines, since the size usually comes from the
drag. `ghost: true` turns it on for them.

The cursor and the preview do different jobs. The cursor says *which* tool is active; the preview
says *where* the annotation will land. You can use either, or both.

## Stamps and files

The `stamp` and `attachment` tools ask for a file after the click.

The file dialog comes from a provider feature: add `withFilePicker()` to `provideEmbedPdf()`, next
to your other features:

**`file-picker.ts`**

```typescript
import { Component } from '@angular/core';
import { withFilePicker } from '@embedpdf/angular/annotation';
import { provideEmbedPdf } from '@embedpdf/angular/runtime';

import { Pages } from './pages';
import { engine, features } from './setup';

@Component({
  selector: 'app-review',
  imports: [Pages],
  providers: [provideEmbedPdf({ engine }, ...features, withFilePicker())],
  template: `<app-pages />`,
})
export class Review {}
```

**`file-picker.ts`**

```typescript
import {
  ChangeDetectionStrategy,
  Component,
  effect,
  inject,
  untracked,
  ViewEncapsulation,
} from '@angular/core';
import { EpdfDocument, EpdfDocumentGate, provideEmbedPdf } from '@embedpdf/angular/runtime';
import type { OpenInput } from '@embedpdf/angular/runtime';
import { EpdfPageTemplate, EpdfStage, withStage } from '@embedpdf/angular/stage';
import { EpdfRenderLayer, withRender } from '@embedpdf/angular/render';
import { EpdfInteraction, withInteraction } from '@embedpdf/angular/interaction';
import {
  EpdfAnnotation,
  EpdfAnnotationLayer,
  withAnnotation,
  withFilePicker,
} from '@embedpdf/angular/annotation';
import { localEngine } from '@embedpdf/engine';

const ebook = async (): Promise<OpenInput> => {
  const response = await fetch('https://snippet.embedpdf.com/ebook.pdf');
  return { kind: 'bytes', id: 'ebook', bytes: new Uint8Array(await response.arrayBuffer()) };
};

const TOOLS = [
  { id: 'pointer', label: 'Select' },
  { id: 'stamp', label: 'Stamp' },
  { id: 'attachment', label: 'Attach a file' },
];

@Component({
  selector: 'demo-toolbar',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <div class="toolbar" role="toolbar">
      <div class="segmented" role="group" aria-label="Tool">
        @for (tool of tools; track tool.id) {
          <button
            type="button"
            [attr.aria-pressed]="interaction.activeToolId() === tool.id"
            (click)="interaction.activateTool(tool.id)"
          >
            {{ tool.label }}
          </button>
        }
      </div>
      <p class="hint">
        @if (interaction.activeToolId() === 'stamp') {
          Click the page, then pick a PNG or a JPEG
        } @else if (interaction.activeToolId() === 'attachment') {
          Click the page, then pick any file
        }
      </p>
    </div>
  `,
})
export class Toolbar {
  protected readonly interaction = inject(EpdfInteraction);
  protected readonly tools = TOOLS;
}

@Component({
  selector: 'demo-root',
  imports: [
    EpdfDocumentGate,
    EpdfStage,
    EpdfPageTemplate,
    EpdfRenderLayer,
    EpdfAnnotationLayer,
    Toolbar,
  ],
  providers: [
    provideEmbedPdf(
      { engine: () => localEngine(), initialDocuments: [{ source: ebook }] },
      withStage(),
      withRender(),
      withInteraction(),
      withAnnotation(),
      // The browser's file dialog, for the stamp and attachment tools of every document.
      withFilePicker(),
    ),
  ],
  changeDetection: ChangeDetectionStrategy.OnPush,
  styleUrl: './file-picker.css',
  encapsulation: ViewEncapsulation.None,
  template: `
    <ng-container *epdfDocumentGate="let document; fallback: loading">
      <demo-toolbar />
      <epdf-stage class="stage">
        <ng-template epdfPage>
          <epdf-render-layer />
          <epdf-annotation-layer />
        </ng-template>
      </epdf-stage>
    </ng-container>

    <ng-template #loading><p class="loading">Loading…</p></ng-template>
  `,
})
export class App {
  private readonly annotation = inject(EpdfAnnotation);
  private readonly interaction = inject(EpdfInteraction);
  private readonly document = inject(EpdfDocument);
  private added = false;

  constructor() {
    // On load: a text file pinned to the cover, and the stamp tool active.
    effect(() => {
      const cover = this.document.pages()[0]?.ref;
      if (this.annotation.status() !== 'ready' || !cover || this.added) return;
      this.added = true;
      untracked(() => {
        const notes = new File(['Questions for the next review.'], 'notes.txt', {
          type: 'text/plain',
        });
        void this.annotation.create(
          cover,
          { subtype: 'file-attachment', rect: { x: 470, y: 232, width: 20, height: 20 } },
          { file: notes },
        );
        this.interaction.activateTool('stamp');
      });
    });
  }
}
```

**`file-picker.css`**

```css
/* Colors */
:root {
  --ink: #1a2748;
  --text: #3b4b7a;
  --muted: #5a6b92;
  --line: #e6eaf2;
  --line-strong: #dce8fc;
  --surface: #ffffff;
  --tint: #f3f7fe;
  --canvas: #f7f9fc;
  --accent: #054fb3;
  --focus: #7db6ff;
  --shadow: rgb(7 32 76 / 0.04);
}

/* The controls, above the pages */
.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 14px;
}

/* One choice of a few: the pressed button is the current one */
.segmented {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 2px;
  padding: 3px;
  background: var(--tint);
  border: 1px solid var(--line);
  border-radius: 11px;
}

.segmented button {
  font: inherit;
  font-size: 12.5px;
  font-weight: 550;
  color: var(--muted);
  background: transparent;
  border: 0;
  border-radius: 8px;
  padding: 6px 12px;
  cursor: pointer;
  white-space: nowrap;
}

.segmented button:hover {
  color: var(--ink);
}

.segmented button[aria-pressed='true'] {
  color: var(--accent);
  background: var(--surface);
  box-shadow: 0 1px 2px rgb(7 32 76 / 0.1);
}

.segmented button:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.hint {
  margin: 0;
  font-size: 13px;
  color: var(--muted);
}

/* The pages */
.stage {
  height: 420px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--canvas);
  box-shadow:
    0 1px 2px var(--shadow),
    0 18px 40px -28px rgb(7 32 76 / 0.28);
}

.loading {
  margin: 0;
  padding: 24px 0;
  text-align: center;
  font-size: 13px;
  color: var(--muted);
}
```

> **Try it:** click the page with **Stamp**, and pick a PNG or a JPEG. The paper clip on the cover
> is a file attached from code.

To pick from your own library or a cloud drive instead, pass a function. It gets the tool and the
spot, and returns the file, or `null` to cancel:

**`file-picker-custom.ts`**

```typescript
import { Component } from '@angular/core';
import { withFilePicker } from '@embedpdf/angular/annotation';
import { provideEmbedPdf } from '@embedpdf/angular/runtime';

import { Pages } from './pages';
import { engine, features } from './setup';
import { openStampLibrary } from './stamp-library';

@Component({
  selector: 'app-review',
  imports: [Pages],
  providers: [
    provideEmbedPdf(
      { engine },
      ...features,
      withFilePicker(async ({ toolId, page, point }) => {
        if (toolId === 'stamp') return { data: await openStampLibrary() };
        return null;
      }),
    ),
  ],
  template: `<app-pages />`,
})
export class Review {}
```

A stamp tool can also carry fixed bytes, so a click places it at once:

```ts
withAnnotation({
  tools: [{ id: 'approved', extends: 'stamp', source: { kind: 'bytes', source: approvedPng } }],
});
```

For libraries of stamps, see [Stamps](stamps).

## Methods

What `inject(EpdfAnnotation)` gives you for tools and stamps: its `tools` and `stamps` parts. The rest is on
[Annotations in code](with-code#methods).

| Method                                  | What it does                                                                                                                                                                                      |
| :-------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `tools.list()`                          | Every tool, the built-in ones and yours, so a toolbar can build itself from it. [Make your own tool](#make-your-own-tool)                                                                         |
| `tools.get(id)`                         | One tool, or `null`                                                                                                                                                                               |
| `tools.register(tool)`                  | Adds a tool while the app runs, or replaces the one with the same id, and returns a function that removes it                                                                                      |
| `tools.getDefaults(id)`                 | The fields a tool's next annotation starts with. In a component, use `annotation.tools.defaultsOf(id)`. [Let people change the defaults](#let-people-change-the-defaults)                         |
| `tools.updateDefaults(id, changes)`     | Changes a tool's defaults for the drawings that follow. They stay in this viewer and are never shared.                                                                                            |
| `tools.getProperties(id)`               | What a style panel for the tool shows: `{ properties, values, mixed }`, the same shape as for the [selection](selecting#a-style-panel)                                                            |
| `stamps.arm(stamp)`, `stamps.disarm()`  | Arms a stamp's bytes so the next click places them, as the `stamp` tool does after picking a file, or disarms them. For stamps from a library, use [Stamps](stamps).                              |
| `stamps.isArmed()`                      | Whether a stamp is armed                                                                                                                                                                          |
| `stamps.place(stamp, { page, center })` | Places a stamp's bytes with its middle on `center`, without a click. Resolves `{ annotation }`.                                                                                                   |
| `setFilePickerProvider(provider)`       | Sets the function that gives the `stamp` and `attachment` tools their file, and returns a function that removes it. In a component, use `withFilePicker()`. [Stamps and files](#stamps-and-files) |

Every method that returns a promise also takes a `signal` in its options, to cancel it.

## Events

**`defaults-changed.ts`**

```typescript
import { Component, inject } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { EpdfAnnotation } from '@embedpdf/angular/annotation';

import { saveStyle } from './styles';

@Component({
  selector: 'app-save-styles',
  template: '',
})
export class SaveStyles {
  constructor() {
    inject(EpdfAnnotation)
      .tools.defaultsChanged$.pipe(takeUntilDestroyed())
      .subscribe(({ toolId, defaults }) => saveStyle(toolId, defaults));
  }
}
```

| Event                    | Fires when                                           | Carries              |
| :----------------------- | :--------------------------------------------------- | :------------------- |
| `tools.defaultsChanged$` | A tool's defaults changed, from your UI or your code | `toolId`, `defaults` |

## Good to know

### Rotated pages

On a rotated page, what a tool places looks the way its author saw it. Stamps and text boxes are
turned so they read upright. A clicked square or circle keeps its `clickCreate` size as the page
shows (it isn't turned, its sides swap), and a clicked line points the way its `clickCreate` says.
`upright: false` turns that off; `upright: true` turns it on for your own tools.

### Touch

On a touch screen, drawing tools take a single finger: it draws, and two fingers still scroll and
zoom. Click tools (notes, stamps) place with a tap, and a drag still scrolls.

### Every option

| Option        | What it is                                                                              |
| :------------ | :-------------------------------------------------------------------------------------- |
| `id`          | The tool's name, used with `interaction.activateTool(id)`.                              |
| `extends`     | A tool to start from.                                                                   |
| `subtype`     | The kind of annotation it draws, for a tool that doesn't extend another.                |
| `defaults`    | The fields every new annotation starts with.                                            |
| `cursor`      | The cursor while the tool is active.                                                    |
| `ghost`       | A preview that follows the pointer: `true`, `false` or `{ opacity }`.                   |
| `clickCreate` | What a single click makes, and which part lands on the click, or `false` for drag only. |
| `afterCreate` | Select the new annotation? Keep the tool? Start typing?                                 |
| `upright`     | Turn new annotations upright on rotated pages.                                          |
| `source`      | For stamp tools: fixed bytes, or ask for a file.                                        |
| `meta`        | Your own data, such as a label and an icon for a toolbar.                               |
