Recently Written · git

scan-subread

PDF reader that follows an audiobook with the SubRead suite

git clone https://github.com/equwal/scan-subread

Log | Files | Refs


src/ppm.ts (1411 bytes)

1 // Encode RGBA pixels as a binary PPM image. Pure: no DOM.
2 //
3 // tesseract.js reads image files. When it gets a canvas, it makes a PNG file
4 // of it with canvas.toBlob, and in the Android WebView of the test phone that
5 // took about 13 s for each page. A binary PPM file is a short text header and
6 // the RGB bytes, so it takes milliseconds to make, and it keeps each pixel.
7 // Leptonica, the image library of tesseract.js, reads it.
8 
9 /** RGBA pixels, 4 bytes for each pixel, row by row from the top: the layout of ImageData. */
10 export interface RgbaImage {
11   width: number;
12   height: number;
13   data: Uint8ClampedArray;
14 }
15 
16 /**
17  * A binary PPM file ("P6", maximum value 255) of `image`. The alpha channel
18  * is dropped: the canvas of a pdf.js page is opaque.
19  */
20 export function imageToPpm(image: RgbaImage): Uint8Array {
21   const { width, height, data } = image;
22   const pixels = width * height;
23   if (data.length !== pixels * 4) {
24     throw new RangeError(
25       `An RGBA image of ${width}x${height} has ${pixels * 4} bytes, not ${data.length}.`,
26     );
27   }
28   const header = new TextEncoder().encode(`P6\n${width} ${height}\n255\n`);
29   const ppm = new Uint8Array(header.length + pixels * 3);
30   ppm.set(header);
31   for (let from = 0, to = header.length; from < data.length; from += 4, to += 3) {
32     ppm[to] = data[from]!;
33     ppm[to + 1] = data[from + 1]!;
34     ppm[to + 2] = data[from + 2]!;
35   }
36   return ppm;
37 }