The axial/radial ramps and the function-based lattice converted one color per
call, i.e. one Wasm round-trip each for `IccColorSpace` (hence for
`/DeviceCMYK`). Add `ColorSpace.getRgbItems`, overridden by `IccColorSpace` and
`AlternateCS`, to convert a whole batch at once.
`IndexedCS` invoked the base color space once per palette entry, which is
cheap for e.g. `DeviceRgbCS` but not for `IccColorSpace` where every call
is a Wasm round-trip.
In the `pr4731.pdf` document the trailer is actually a Stream, rather than the expected Dictionary, hence update the "nested trailer dictionary" check to make that clearer.
*Note:* This is something that I happened to noticed while working on the previous patch.
Part of this is very old code, hence modernizing it a little bit more really shouldn't hurt.
This patch also shortens some `XRef` code by using nullish coalescing assignment respectively ternary operators more.
Finally, adds the recently introduced `countUpdatesAfter` method to the `XRefMock`/`XRefWrapper` classes to avoid having to check for its existence.
Given that Maps are used a lot more these days, this helper function is now used very sparingly and inlining the necessary code seems reasonable.
*Note:* Once [this proposal](https://github.com/tc39/proposal-object-keys-length) makes it into Firefox, we should be able to simplify all `Object.keys(...).length` call-sites.
Compared to regular Objects there's a number of advantages to using Maps:
- They support proper iteration.
- They have a simple way to check for the existence of data.
- They have a simple/efficient way to check the number of elements.
If this functionality was added today, I cannot imagine that we'd choose an Object for this data.
When getting/creating a new `Ref` instance, via the static `Ref.get` method, we already need to compute a cache-key.
Since this key is identical to the format used by the `toString` method, we can avoid having to re-create that string (a lot) by also providing it to the `Ref` constructor.
Compared to regular Objects there's a number of advantages to using Maps:
- They support proper iteration.
- They have a simple way to check for the existence of data.
- They have a simple/efficient way to check the number of elements.
If this functionality was added today, I cannot imagine that we'd choose an Object for this data.
The Type-4 PostScript -> Wasm compiler emitted i32.const immediates with the
unsigned LEB128 encoder (_emitULEB128). Wasm decodes i32.const as a *signed*
LEB128, so any immediate whose final 7-bit group has bit 0x40 set is
mis-decoded (e.g. 64 -> single byte 0x40 -> read back as -64).
The output-store address for the i-th result is emitted as i32.const (i*8).
For a function with >= 9 outputs the 9th offset is 64, which decodes as -64, so
f64.store targets 0xFFFFFFC0 and traps (memory access out of bounds). The module
still validates and instantiates, so the JS fallback in function.js is not
engaged; the trap only surfaces at render time (the affected shading / tint
transform silently fails to render). The same mis-encoding affects the constant
bitshift amount.
Add a signed-LEB128 emitter (_emitSLEB128) and use it for the three i32.const
immediate sites; local/import/type indices remain unsigned (correct). Add a unit
test covering functions with 9+ outputs, and a reference test rendering a shading
whose colour function has 9 outputs.
Some Annotations, e.g. `SignatureWidgetAnnotation`, set `fieldValue = null` which may causing saving to fail during `writeXFADataForAcroform`, hence we simply ignore any such Annotation-data during saving.
This avoids duplicating the charCode/glyphId lookup, and (slightly) shortens the code. In particular:
- Given that the path-data is returned as a TypedArray, it's easy enough to instead return `null` to indicate that the glyph was previously compiled.
- The charCode-cache can be changed into a Set, since we only need to track the "seen" charCodes and not their relation to the glyphIds.
- The `compileFontPathInfo` call is moved into `CompiledFont` class, since that simplifies the `src/core/evaluator.js` code a tiny bit.
Compared to regular Objects there's a number of advantages to using Maps:
- They support proper iteration.
- They have a simple way to check for the existence of data.
- They have a simple/efficient way to check the number of elements.
If this functionality was added today, I cannot imagine that we'd choose an Object for this data.
- Initialize the `bCache` lazily, since many/most PDF documents don't need it.
- Change the `bCache` to a `Map`, rather than an Object, which thanks to `getOrInsertComputed` allows the `buildB` function to be inlined.
Also, while unrelated here, move the `this.matrix = null;` definition to the `BaseShadingPattern` class to reduce (a tiny bit of) unnecessary duplication.
Merging pages with conflicting AcroForm /DR must inline the resources
each field's appearance relies on, since only one /DR survives in the
output. That fixup:
- resolved the appearance /Resources ref against the source document's
xref instead of the merged one, so the lookup failed and merging threw
(null.has(...));
- skipped checkbox/radio widgets, whose /AP /N is a sub-dictionary of
appearance states rather than a single stream;
- threw on a non-dictionary /Resources (e.g. a stray name) instead of
falling back to the default resources.