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 }