Merge pull request #9729 from Snuffleupagus/gulp-image_decoders

Add a `gulp image_decoders` command to package the image decoders (i.e. jpg.js, jpx.js, jbig2.js) separately, and publish them in pdfjs-dist
This commit is contained in:
Tim van der Meij 2018-06-26 23:27:32 +02:00 committed by GitHub
commit 14b69a4c1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 288 additions and 14 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1,40 @@
<!DOCTYPE html>
<!--
Copyright 2018 Mozilla Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html dir="ltr" mozdisallowselectionprint>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="google" content="notranslate">
<title>PDF.js standalone JpegImage parser</title>
<style>
body {
background-color: #808080;
margin: 0;
padding: 0;
}
</style>
<script src="../../node_modules/pdfjs-dist/image_decoders/pdf.image_decoders.js"></script>
</head>
<body tabindex="1">
<canvas id="jpegCanvas" width="0" height="0"></canvas>
<script src="jpeg_viewer.js"></script>
</body>
</html>

View File

@ -0,0 +1,75 @@
/* Copyright 2018 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
if (!pdfjsImageDecoders.JpegImage) {
alert('Please build the pdfjs-dist library using `gulp dist-install`');
}
var JPEG_IMAGE = 'fish.jpg';
var jpegCanvas = document.getElementById('jpegCanvas');
var jpegCtx = jpegCanvas.getContext('2d');
// Load the image data, and convert it to a Uint8Array.
//
var nonBinaryRequest = false;
var request = new XMLHttpRequest();
request.open('GET', JPEG_IMAGE, false);
try {
request.responseType = 'arraybuffer';
nonBinaryRequest = request.responseType !== 'arraybuffer';
} catch (e) {
nonBinaryRequest = true;
}
if (nonBinaryRequest && request.overrideMimeType) {
request.overrideMimeType('text/plain; charset=x-user-defined');
}
request.send(null);
var typedArrayImage;
if (nonBinaryRequest) {
var str = request.responseText, length = str.length;
var bytes = new Uint8Array(length);
for (var i = 0; i < length; ++i) {
bytes[i] = str.charCodeAt(i) & 0xFF;
}
typedArrayImage = bytes;
} else {
typedArrayImage = new Uint8Array(request.response);
}
// Parse the image data using `JpegImage`.
//
var jpegImage = new pdfjsImageDecoders.JpegImage();
jpegImage.parse(typedArrayImage);
var width = jpegImage.width, height = jpegImage.height;
var jpegData = jpegImage.getData(width, height, /* forceRGB = */ true);
// Render the JPEG image on a <canvas>.
//
var imageData = jpegCtx.createImageData(width, height);
var imageBytes = imageData.data;
for (var i = 0, j = 0, ii = width * height * 4; i < ii;) {
imageBytes[i++] = jpegData[j++];
imageBytes[i++] = jpegData[j++];
imageBytes[i++] = jpegData[j++];
imageBytes[i++] = 255;
}
jpegCanvas.width = width, jpegCanvas.height = height;
jpegCtx.putImageData(imageData, 0, 0);

View File

@ -50,6 +50,7 @@ var BASELINE_DIR = BUILD_DIR + 'baseline/';
var MOZCENTRAL_BASELINE_DIR = BUILD_DIR + 'mozcentral.baseline/';
var GENERIC_DIR = BUILD_DIR + 'generic/';
var COMPONENTS_DIR = BUILD_DIR + 'components/';
var IMAGE_DECODERS_DIR = BUILD_DIR + 'image_decoders';
var MINIFIED_DIR = BUILD_DIR + 'minified/';
var JSDOC_BUILD_DIR = BUILD_DIR + 'jsdoc/';
var GH_PAGES_DIR = BUILD_DIR + 'gh-pages/';
@ -70,7 +71,7 @@ var builder = require('./external/builder/builder.js');
var CONFIG_FILE = 'pdfjs.config';
var config = JSON.parse(fs.readFileSync(CONFIG_FILE).toString());
// Default Autoprefixer config used for generic, components, minifed-pre
// Default Autoprefixer config used for generic, components, minified-pre
var AUTOPREFIXER_CONFIG = {
browsers: [
'last 2 versions',
@ -96,6 +97,7 @@ var DEFINES = {
COMPONENTS: false,
LIB: false,
SKIP_BABEL: false,
IMAGE_DECODERS: false,
};
function safeSpawnSync(command, parameters, options) {
@ -306,6 +308,22 @@ function createComponentsBundle(defines) {
.pipe(replaceJSRootName(componentsAMDName, 'pdfjsViewer'));
}
function createImageDecodersBundle(defines) {
var imageDecodersAMDName = 'pdfjs-dist/image_decoders/pdf.image_decoders';
var imageDecodersOutputName = 'pdf.image_decoders.js';
var componentsFileConfig = createWebpackConfig(defines, {
filename: imageDecodersOutputName,
library: imageDecodersAMDName,
libraryTarget: 'umd',
umdNamedDefine: true,
});
return gulp.src('./src/pdf.image_decoders.js')
.pipe(webpack2Stream(componentsFileConfig))
.pipe(replaceWebpackRequire())
.pipe(replaceJSRootName(imageDecodersAMDName, 'pdfjsImageDecoders'));
}
function checkFile(path) {
try {
var stat = fs.lstatSync(path);
@ -631,6 +649,15 @@ gulp.task('components', ['buildnumber'], function () {
]);
});
gulp.task('image_decoders', ['buildnumber'], function() {
console.log();
console.log('### Creating image decoders');
var defines = builder.merge(DEFINES, { GENERIC: true,
IMAGE_DECODERS: true, });
return createImageDecodersBundle(defines).pipe(gulp.dest(IMAGE_DECODERS_DIR));
});
gulp.task('minified-pre', ['buildnumber', 'locale'], function () {
console.log();
console.log('### Creating minified viewer');
@ -641,6 +668,8 @@ gulp.task('minified-pre', ['buildnumber', 'locale'], function () {
return merge([
createBundle(defines).pipe(gulp.dest(MINIFIED_DIR + 'build')),
createWebBundle(defines).pipe(gulp.dest(MINIFIED_DIR + 'web')),
createImageDecodersBundle(builder.merge(defines, { IMAGE_DECODERS: true, }))
.pipe(gulp.dest(MINIFIED_DIR + 'image_decoders')),
gulp.src(COMMON_WEB_FILES, { base: 'web/', })
.pipe(gulp.dest(MINIFIED_DIR + 'web')),
gulp.src([
@ -666,6 +695,8 @@ gulp.task('minified-post', ['minified-pre'], function () {
var pdfFile = fs.readFileSync(MINIFIED_DIR + '/build/pdf.js').toString();
var pdfWorkerFile =
fs.readFileSync(MINIFIED_DIR + '/build/pdf.worker.js').toString();
var pdfImageDecodersFile = fs.readFileSync(MINIFIED_DIR +
'/image_decoders/pdf.image_decoders.js').toString();
var viewerFiles = {
'pdf.js': pdfFile,
'viewer.js': fs.readFileSync(MINIFIED_DIR + '/web/viewer.js').toString(),
@ -684,6 +715,8 @@ gulp.task('minified-post', ['minified-pre'], function () {
UglifyES.minify(pdfFile).code);
fs.writeFileSync(MINIFIED_DIR + '/build/pdf.worker.min.js',
UglifyES.minify(pdfWorkerFile, optsForHugeFile).code);
fs.writeFileSync(MINIFIED_DIR + 'image_decoders/pdf.image_decoders.min.js',
UglifyES.minify(pdfImageDecodersFile).code);
console.log();
console.log('### Cleaning js files');
@ -696,6 +729,8 @@ gulp.task('minified-post', ['minified-pre'], function () {
MINIFIED_DIR + '/build/pdf.js');
fs.renameSync(MINIFIED_DIR + '/build/pdf.worker.min.js',
MINIFIED_DIR + '/build/pdf.worker.js');
fs.renameSync(MINIFIED_DIR + '/image_decoders/pdf.image_decoders.min.js',
MINIFIED_DIR + '/image_decoders/pdf.image_decoders.js');
});
gulp.task('minified', ['minified-post']);
@ -1138,7 +1173,8 @@ gulp.task('gh-pages-git', ['gh-pages-prepare', 'wintersmith'], function () {
gulp.task('web', ['gh-pages-prepare', 'wintersmith', 'gh-pages-git']);
gulp.task('dist-pre', ['generic', 'components', 'lib', 'minified'], function() {
gulp.task('dist-pre',
['generic', 'components', 'image_decoders', 'lib', 'minified'], function() {
var VERSION = getVersionJSON().version;
console.log();
@ -1228,8 +1264,13 @@ gulp.task('dist-pre', ['generic', 'components', 'lib', 'minified'], function() {
gulp.src(MINIFIED_DIR + 'build/pdf.worker.js')
.pipe(rename('pdf.worker.min.js'))
.pipe(gulp.dest(DIST_DIR + 'build/')),
gulp.src(MINIFIED_DIR + 'image_decoders/pdf.image_decoders.js')
.pipe(rename('pdf.image_decoders.min.js'))
.pipe(gulp.dest(DIST_DIR + 'image_decoders/')),
gulp.src(COMPONENTS_DIR + '**/*', { base: COMPONENTS_DIR, })
.pipe(gulp.dest(DIST_DIR + 'web/')),
gulp.src(IMAGE_DECODERS_DIR + '**/*', { base: IMAGE_DECODERS_DIR, })
.pipe(gulp.dest(DIST_DIR + 'image_decoders')),
gulp.src(LIB_DIR + '**/*', { base: LIB_DIR, })
.pipe(gulp.dest(DIST_DIR + 'lib/')),
]);

View File

@ -1192,6 +1192,47 @@ var Jbig2Image = (function Jbig2ImageClosure() {
return visitor.buffer;
}
function parseJbig2(data) {
let position = 0, end = data.length;
if (data[position] !== 0x97 || data[position + 1] !== 0x4A ||
data[position + 2] !== 0x42 || data[position + 3] !== 0x32 ||
data[position + 4] !== 0x0D || data[position + 5] !== 0x0A ||
data[position + 6] !== 0x1A || data[position + 7] !== 0x0A) {
throw new Jbig2Error('parseJbig2 - invalid header.');
}
let header = Object.create(null);
position += 8;
const flags = data[position++];
header.randomAccess = !(flags & 1);
if (!(flags & 2)) {
header.numberOfPages = readUint32(data, position);
position += 4;
}
let segments = readSegments(header, data, position, end);
let visitor = new SimpleSegmentVisitor();
processSegments(segments, visitor);
const { width, height, } = visitor.currentPageInfo;
const bitPacked = visitor.buffer;
let imgData = new Uint8ClampedArray(width * height);
let q = 0, k = 0;
for (let i = 0; i < height; i++) {
let mask = 0, buffer;
for (let j = 0; j < width; j++) {
if (!mask) {
mask = 128; buffer = bitPacked[k++];
}
imgData[q++] = (buffer & mask) ? 0 : 255;
mask >>= 1;
}
}
return { imgData, width, height, };
}
function SimpleSegmentVisitor() {}
SimpleSegmentVisitor.prototype = {
@ -2095,9 +2136,16 @@ var Jbig2Image = (function Jbig2ImageClosure() {
function Jbig2Image() {}
Jbig2Image.prototype = {
parseChunks: function Jbig2Image_parseChunks(chunks) {
parseChunks(chunks) {
return parseJbig2Chunks(chunks);
},
parse(data) {
const { imgData, width, height, } = parseJbig2(data);
this.width = width;
this.height = height;
return imgData;
},
};
return Jbig2Image;

View File

@ -63,7 +63,10 @@ let JpegStream = (function JpegStreamClosure() {
if (this.eof) {
return;
}
let jpegImage = new JpegImage();
let jpegOptions = {
decodeTransform: undefined,
colorTransform: undefined,
};
// Checking if values need to be transformed before conversion.
let decodeArr = this.dict.getArray('Decode', 'D');
@ -81,16 +84,17 @@ let JpegStream = (function JpegStreamClosure() {
}
}
if (transformNeeded) {
jpegImage.decodeTransform = transform;
jpegOptions.decodeTransform = transform;
}
}
// Fetching the 'ColorTransform' entry, if it exists.
if (isDict(this.params)) {
let colorTransform = this.params.get('ColorTransform');
if (Number.isInteger(colorTransform)) {
jpegImage.colorTransform = colorTransform;
jpegOptions.colorTransform = colorTransform;
}
}
const jpegImage = new JpegImage(jpegOptions);
jpegImage.parse(this.bytes);
let data = jpegImage.getData(this.drawWidth, this.drawHeight,

View File

@ -94,9 +94,9 @@ var JpegImage = (function JpegImageClosure() {
var dctSqrt2 = 5793; // sqrt(2)
var dctSqrt1d2 = 2896; // sqrt(2) / 2
function JpegImage() {
this.decodeTransform = null;
this.colorTransform = -1;
function JpegImage({ decodeTransform = null, colorTransform = -1, } = {}) {
this._decodeTransform = decodeTransform;
this._colorTransform = colorTransform;
}
function buildHuffmanTable(codeLengths, values) {
@ -1013,7 +1013,7 @@ var JpegImage = (function JpegImageClosure() {
}
// decodeTransform contains pairs of multiplier (-256..256) and additive
var transform = this.decodeTransform;
const transform = this._decodeTransform;
if (transform) {
for (i = 0; i < dataLength;) {
for (j = 0, k = 0; j < numComponents; j++, i++, k += 2) {
@ -1030,7 +1030,7 @@ var JpegImage = (function JpegImageClosure() {
return !!this.adobe.transformCode;
}
if (this.numComponents === 3) {
if (this.colorTransform === 0) {
if (this._colorTransform === 0) {
// If the Adobe transform marker is not present and the image
// dictionary has a 'ColorTransform' entry, explicitly set to `0`,
// then the colours should *not* be transformed.
@ -1039,7 +1039,7 @@ var JpegImage = (function JpegImageClosure() {
return true;
}
// `this.numComponents !== 3`
if (this.colorTransform === 1) {
if (this._colorTransform === 1) {
// If the Adobe transform marker is not present and the image
// dictionary has a 'ColorTransform' entry, explicitly set to `1`,
// then the colours should be transformed.

45
src/pdf.image_decoders.js Normal file
View File

@ -0,0 +1,45 @@
/* Copyright 2018 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable no-unused-vars */
import { getVerbosityLevel, setVerbosityLevel } from './shared/util';
import { Jbig2mage } from './core/jbig2';
import { JpegImage } from './core/jpg';
import { JpxImage } from './core/jpx';
// To ensure that the standalone PDF.js image decoders have the same
// browser/environment compatibility as the regular PDF.js library,
// the standard set of polyfills are thus included in this build as well.
//
// Given that the (current) image decoders don't use all of the features
// of the complete PDF.js library, e.g. they are completely synchronous,
// some of the larger polyfills are thus unnecessary.
//
// In an attempt to reduce the size of the standalone PDF.js image decoders,
// the following polyfills are currently being excluded:
// - ReadableStream
// - Promise
// - URL
const pdfjsVersion = PDFJSDev.eval('BUNDLE_VERSION');
const pdfjsBuild = PDFJSDev.eval('BUNDLE_BUILD');
export {
Jbig2mage,
JpegImage,
JpxImage,
getVerbosityLevel,
setVerbosityLevel,
};

View File

@ -147,6 +147,11 @@ const hasDOM = typeof window === 'object' && typeof document === 'object';
// Support: IE, Safari<8, Chrome<32
(function checkPromise() {
if (typeof PDFJSDev !== 'undefined' && PDFJSDev.test('IMAGE_DECODERS')) {
// The current image decoders are synchronous, hence `Promise` shouldn't
// need to be polyfilled for the IMAGE_DECODERS build target.
return;
}
if (globalScope.Promise) {
return;
}
@ -166,6 +171,11 @@ const hasDOM = typeof window === 'object' && typeof document === 'object';
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
(function checkURLConstructor() {
if (typeof PDFJSDev !== 'undefined' && PDFJSDev.test('IMAGE_DECODERS')) {
// The current image decoders doesn't utilize the `URL` constructor, hence
// it shouldn't need to be polyfilled for the IMAGE_DECODERS build target.
return;
}
// feature detect for URL constructor
var hasWorkingUrl = false;
try {

View File

@ -31,6 +31,17 @@ if (typeof ReadableStream !== 'undefined') {
if (isReadableStreamSupported) {
exports.ReadableStream = ReadableStream;
} else {
exports.ReadableStream =
require('../../external/streams/streams-lib').ReadableStream;
if (typeof PDFJSDev !== 'undefined' && PDFJSDev.test('IMAGE_DECODERS')) {
class DummyReadableStream {
constructor() {
throw new Error('The current image decoders are synchronous, ' +
'hence `ReadableStream` shouldn\'t need to be ' +
'polyfilled for the IMAGE_DECODERS build target.');
}
}
exports.ReadableStream = DummyReadableStream;
} else {
exports.ReadableStream =
require('../../external/streams/streams-lib').ReadableStream;
}
}