• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

adnsistemas / pdf-lib / #18

24 Mar 2026 08:15PM UTC coverage: 74.286% (+0.3%) from 74.001%
#18

push

David N. Abdala
Documentation change

2569 of 3981 branches covered (64.53%)

Branch coverage included in aggregate %.

7372 of 9401 relevant lines covered (78.42%)

297170.51 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.32
/src/core/annotation/PDFAnnotation.ts
1
import PDFDict from '../objects/PDFDict';
54✔
2
import PDFName from '../objects/PDFName';
54✔
3
import PDFStream from '../objects/PDFStream';
54✔
4
import PDFArray from '../objects/PDFArray';
54✔
5
import PDFRef from '../objects/PDFRef';
6
import PDFNumber from '../objects/PDFNumber';
54✔
7
import { isPDFInstance, PDFClasses } from '../../api/objects';
54✔
8

9
class PDFAnnotation {
10
  readonly dict: PDFDict;
11

12
  static fromDict = (dict: PDFDict): PDFAnnotation => new PDFAnnotation(dict);
54✔
13

14
  protected constructor(dict: PDFDict) {
15
    this.dict = dict;
318✔
16
  }
17

18
  // This is technically required by the PDF spec
19
  Rect(): PDFArray | undefined {
20
    return this.dict.lookup(PDFName.of('Rect'), PDFArray);
165✔
21
  }
22

23
  AP(): PDFDict | undefined {
24
    return this.dict.lookupMaybe(PDFName.of('AP'), PDFDict);
441✔
25
  }
26

27
  F(): PDFNumber | undefined {
28
    const numberOrRef = this.dict.lookup(PDFName.of('F'));
84✔
29
    return this.dict.context.lookupMaybe(numberOrRef, PDFNumber);
84✔
30
  }
31

32
  getRectangle(): { x: number; y: number; width: number; height: number } {
33
    const Rect = this.Rect();
165✔
34
    return Rect?.asRectangle() ?? { x: 0, y: 0, width: 0, height: 0 };
165!
35
  }
36

37
  setRectangle(rect: { x: number; y: number; width: number; height: number }) {
38
    const { x, y, width, height } = rect;
26✔
39
    const Rect = this.dict.context.obj([x, y, x + width, y + height]);
26✔
40
    this.dict.set(PDFName.of('Rect'), Rect);
26✔
41
  }
42

43
  getAppearanceState(): PDFName | undefined {
44
    const AS = this.dict.lookup(PDFName.of('AS'));
30✔
45
    if (isPDFInstance(AS, PDFClasses.PDFName)) return AS as PDFName;
30✔
46
    return undefined;
×
47
  }
48

49
  setAppearanceState(state: PDFName) {
50
    this.dict.set(PDFName.of('AS'), state);
46✔
51
  }
52

53
  setAppearances(appearances: PDFDict) {
54
    this.dict.set(PDFName.of('AP'), appearances);
×
55
  }
56

57
  ensureAP(): PDFDict {
58
    let AP = this.AP();
83✔
59
    if (!AP) {
83✔
60
      AP = this.dict.context.obj({});
51✔
61
      this.dict.set(PDFName.of('AP'), AP);
51✔
62
    }
63
    return AP;
83✔
64
  }
65

66
  getNormalAppearance(): PDFRef | PDFDict {
67
    const AP = this.ensureAP();
7✔
68
    const N = AP.get(PDFName.of('N'));
7✔
69
    if (
7✔
70
      isPDFInstance(N, PDFClasses.PDFRef) ||
13✔
71
      isPDFInstance(N, PDFClasses.PDFDict)
72
    )
73
      return N as PDFRef | PDFDict;
7✔
74

75
    throw new Error(`Unexpected N type: ${N?.constructor.name}`);
×
76
  }
77

78
  /** @param appearance A PDFDict or PDFStream (direct or ref) */
79
  setNormalAppearance(appearance: PDFRef | PDFDict) {
80
    const AP = this.ensureAP();
51✔
81
    AP.set(PDFName.of('N'), appearance);
51✔
82
  }
83

84
  /** @param appearance A PDFDict or PDFStream (direct or ref) */
85
  setRolloverAppearance(appearance: PDFRef | PDFDict) {
86
    const AP = this.ensureAP();
×
87
    AP.set(PDFName.of('R'), appearance);
×
88
  }
89

90
  /** @param appearance A PDFDict or PDFStream (direct or ref) */
91
  setDownAppearance(appearance: PDFRef | PDFDict) {
92
    const AP = this.ensureAP();
25✔
93
    AP.set(PDFName.of('D'), appearance);
25✔
94
  }
95

96
  removeRolloverAppearance() {
97
    const AP = this.AP();
51✔
98
    AP?.delete(PDFName.of('R'));
51!
99
  }
100

101
  removeDownAppearance() {
102
    const AP = this.AP();
26✔
103
    AP?.delete(PDFName.of('D'));
26!
104
  }
105

106
  getAppearances():
107
    | {
108
        normal: PDFStream | PDFDict;
109
        rollover?: PDFStream | PDFDict;
110
        down?: PDFStream | PDFDict;
111
      }
112
    | undefined {
113
    const AP = this.AP();
281✔
114

115
    if (!AP) return undefined;
281✔
116

117
    const N = AP.lookup(PDFName.of('N'), PDFDict, PDFStream);
262✔
118
    const R = AP.lookupMaybe(PDFName.of('R'), PDFDict, PDFStream);
262✔
119
    const D = AP.lookupMaybe(PDFName.of('D'), PDFDict, PDFStream);
262✔
120

121
    return { normal: N, rollover: R, down: D };
262✔
122
  }
123

124
  getFlags(): number {
125
    return this.F()?.asNumber() ?? 0;
84✔
126
  }
127

128
  setFlags(flags: number) {
129
    this.dict.set(PDFName.of('F'), PDFNumber.of(flags));
78✔
130
  }
131

132
  hasFlag(flag: number): boolean {
133
    const flags = this.getFlags();
6✔
134
    return (flags & flag) !== 0;
6✔
135
  }
136

137
  setFlag(flag: number) {
138
    const flags = this.getFlags();
27✔
139
    this.setFlags(flags | flag);
27✔
140
  }
141

142
  clearFlag(flag: number) {
143
    const flags = this.getFlags();
51✔
144
    this.setFlags(flags & ~flag);
51✔
145
  }
146

147
  setFlagTo(flag: number, enable: boolean) {
148
    if (enable) this.setFlag(flag);
78✔
149
    else this.clearFlag(flag);
51✔
150
  }
151
}
152

153
export default PDFAnnotation;
54✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc