From 303537bcb162159dec3a87d78240bdc219b52afe Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Wed, 16 May 2018 13:49:26 +0200 Subject: [PATCH] Add a `gulp image_decoders` command to allow packaging/distributing the image decoders (i.e. jpg.js, jpx.js, jbig2.js) separately from the main PDF.js library Please note that the standalone `pdf.image_decoders.js` file will be including the complete `src/shared/util.js` file, despite only using parts of it.[1] This was done *purposely*, to not negatively impact the readability/maintainability of the core PDF.js code. Furthermore, to ensure that the compatibility is the same in the regular PDF.js library *and* in the the standalone image decoders, `src/shared/compatibility.js` was included as well. To (hopefully) prevent future complaints about the size of the built `pdf.image_decoders.js` file, a few existing async-related polyfills are being skipped (since all of the image decoders are completely synchronous). Obviously this required adding a couple of pre-processor statements, but given that these are all limited to "compatibility" code, I think this might be OK!? --- [1] However, please note that previous commits moved `PageViewport` and `MessageHandler` out of `src/shared/util.js` which reduced its size. --- gulpfile.js | 45 ++++++++++++++++++++++++++++++++-- src/pdf.image_decoders.js | 45 ++++++++++++++++++++++++++++++++++ src/shared/compatibility.js | 10 ++++++++ src/shared/streams_polyfill.js | 15 ++++++++++-- 4 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 src/pdf.image_decoders.js diff --git a/gulpfile.js b/gulpfile.js index acd104d4e..a70c04ef1 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -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/')), ]); diff --git a/src/pdf.image_decoders.js b/src/pdf.image_decoders.js new file mode 100644 index 000000000..d7581e830 --- /dev/null +++ b/src/pdf.image_decoders.js @@ -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, +}; diff --git a/src/shared/compatibility.js b/src/shared/compatibility.js index 5a4911357..8a731a7a1 100644 --- a/src/shared/compatibility.js +++ b/src/shared/compatibility.js @@ -138,6 +138,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; } @@ -157,6 +162,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 { diff --git a/src/shared/streams_polyfill.js b/src/shared/streams_polyfill.js index 7c8e85d87..962a26f3b 100644 --- a/src/shared/streams_polyfill.js +++ b/src/shared/streams_polyfill.js @@ -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; + } }