gulpfile.js 69 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496
  1. /* Copyright 2016 Mozilla Foundation
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. /* eslint-env node */
  16. "use strict";
  17. const autoprefixer = require("autoprefixer");
  18. const postcssDirPseudoClass = require("postcss-dir-pseudo-class");
  19. const postcssLogical = require("postcss-logical");
  20. const fs = require("fs");
  21. const gulp = require("gulp");
  22. const postcss = require("gulp-postcss");
  23. const rename = require("gulp-rename");
  24. const replace = require("gulp-replace");
  25. const mkdirp = require("mkdirp");
  26. const path = require("path");
  27. const rimraf = require("rimraf");
  28. const stream = require("stream");
  29. const exec = require("child_process").exec;
  30. const spawn = require("child_process").spawn;
  31. const spawnSync = require("child_process").spawnSync;
  32. const streamqueue = require("streamqueue");
  33. const merge = require("merge-stream");
  34. const zip = require("gulp-zip");
  35. const webpack2 = require("webpack");
  36. const webpackStream = require("webpack-stream");
  37. const Vinyl = require("vinyl");
  38. const vfs = require("vinyl-fs");
  39. const through = require("through2");
  40. const BUILD_DIR = "build/";
  41. const L10N_DIR = "l10n/";
  42. const TEST_DIR = "test/";
  43. const EXTENSION_SRC_DIR = "extensions/";
  44. const BASELINE_DIR = BUILD_DIR + "baseline/";
  45. const MOZCENTRAL_BASELINE_DIR = BUILD_DIR + "mozcentral.baseline/";
  46. const GENERIC_DIR = BUILD_DIR + "generic/";
  47. const GENERIC_LEGACY_DIR = BUILD_DIR + "generic-legacy/";
  48. const COMPONENTS_DIR = BUILD_DIR + "components/";
  49. const COMPONENTS_LEGACY_DIR = BUILD_DIR + "components-legacy/";
  50. const IMAGE_DECODERS_DIR = BUILD_DIR + "image_decoders/";
  51. const IMAGE_DECODERS_LEGACY_DIR = BUILD_DIR + "image_decoders-legacy/";
  52. const DEFAULT_PREFERENCES_DIR = BUILD_DIR + "default_preferences/";
  53. const MINIFIED_DIR = BUILD_DIR + "minified/";
  54. const MINIFIED_LEGACY_DIR = BUILD_DIR + "minified-legacy/";
  55. const JSDOC_BUILD_DIR = BUILD_DIR + "jsdoc/";
  56. const GH_PAGES_DIR = BUILD_DIR + "gh-pages/";
  57. const SRC_DIR = "src/";
  58. const LIB_DIR = BUILD_DIR + "lib/";
  59. const DIST_DIR = BUILD_DIR + "dist/";
  60. const TYPES_DIR = BUILD_DIR + "types/";
  61. const TMP_DIR = BUILD_DIR + "tmp/";
  62. const TYPESTEST_DIR = BUILD_DIR + "typestest/";
  63. const COMMON_WEB_FILES = [
  64. "web/images/*.{png,svg,gif}",
  65. "web/debugger.{css,js}",
  66. ];
  67. const MOZCENTRAL_DIFF_FILE = "mozcentral.diff";
  68. const REPO = "git@github.com:mozilla/pdf.js.git";
  69. const DIST_REPO_URL = "https://github.com/mozilla/pdfjs-dist";
  70. const builder = require("./external/builder/builder.js");
  71. const CONFIG_FILE = "pdfjs.config";
  72. const config = JSON.parse(fs.readFileSync(CONFIG_FILE).toString());
  73. const ENV_TARGETS = [
  74. "last 2 versions",
  75. "Chrome >= 85",
  76. "Firefox ESR",
  77. "Safari >= 14",
  78. "Node >= 14",
  79. "> 1%",
  80. "not IE > 0",
  81. "not dead",
  82. ];
  83. // Default Autoprefixer config used for generic, components, minified-pre
  84. const AUTOPREFIXER_CONFIG = {
  85. overrideBrowserslist: ENV_TARGETS,
  86. };
  87. // Default Babel targets used for generic, components, minified-pre
  88. const BABEL_TARGETS = ENV_TARGETS.join(", ");
  89. const DEFINES = Object.freeze({
  90. PRODUCTION: true,
  91. SKIP_BABEL: true,
  92. TESTING: undefined,
  93. // The main build targets:
  94. GENERIC: false,
  95. MOZCENTRAL: false,
  96. GECKOVIEW: false,
  97. CHROME: false,
  98. MINIFIED: false,
  99. COMPONENTS: false,
  100. LIB: false,
  101. IMAGE_DECODERS: false,
  102. });
  103. function transform(charEncoding, transformFunction) {
  104. return through.obj(function (vinylFile, enc, done) {
  105. const transformedFile = vinylFile.clone();
  106. transformedFile.contents = Buffer.from(
  107. transformFunction(transformedFile.contents),
  108. charEncoding
  109. );
  110. done(null, transformedFile);
  111. });
  112. }
  113. function safeSpawnSync(command, parameters, options) {
  114. // Execute all commands in a shell.
  115. options = options || {};
  116. options.shell = true;
  117. // `options.shell = true` requires parameters to be quoted.
  118. parameters = parameters.map(param => {
  119. if (!/[\s`~!#$*(){[|\\;'"<>?]/.test(param)) {
  120. return param;
  121. }
  122. return '"' + param.replace(/([$\\"`])/g, "\\$1") + '"';
  123. });
  124. const result = spawnSync(command, parameters, options);
  125. if (result.status !== 0) {
  126. console.log(
  127. 'Error: command "' +
  128. command +
  129. '" with parameters "' +
  130. parameters +
  131. '" exited with code ' +
  132. result.status
  133. );
  134. process.exit(result.status);
  135. }
  136. return result;
  137. }
  138. function startNode(args, options) {
  139. // Node.js decreased the maximum header size from 80 KB to 8 KB in newer
  140. // releases, which is not sufficient for some of our reference test files
  141. // (such as `issue6360.pdf`), so we need to restore this value. Note that
  142. // this argument needs to be before all other arguments as it needs to be
  143. // passed to the Node.js process itself and not to the script that it runs.
  144. args.unshift("--max-http-header-size=80000");
  145. return spawn("node", args, options);
  146. }
  147. function createStringSource(filename, content) {
  148. const source = stream.Readable({ objectMode: true });
  149. source._read = function () {
  150. this.push(
  151. new Vinyl({
  152. path: filename,
  153. contents: Buffer.from(content),
  154. })
  155. );
  156. this.push(null);
  157. };
  158. return source;
  159. }
  160. function createWebpackConfig(
  161. defines,
  162. output,
  163. {
  164. disableVersionInfo = false,
  165. disableSourceMaps = false,
  166. disableLicenseHeader = false,
  167. defaultPreferencesDir = null,
  168. } = {}
  169. ) {
  170. const versionInfo = !disableVersionInfo
  171. ? getVersionJSON()
  172. : { version: 0, commit: 0 };
  173. const bundleDefines = builder.merge(defines, {
  174. BUNDLE_VERSION: versionInfo.version,
  175. BUNDLE_BUILD: versionInfo.commit,
  176. TESTING:
  177. defines.TESTING !== undefined
  178. ? defines.TESTING
  179. : process.env.TESTING === "true",
  180. DEFAULT_PREFERENCES: defaultPreferencesDir
  181. ? getDefaultPreferences(defaultPreferencesDir)
  182. : {},
  183. DIALOG_POLYFILL_CSS:
  184. defines.GENERIC && !defines.SKIP_BABEL ? getDialogPolyfillCSS() : "",
  185. });
  186. const licenseHeaderLibre = fs
  187. .readFileSync("./src/license_header_libre.js")
  188. .toString();
  189. const enableSourceMaps =
  190. !bundleDefines.MOZCENTRAL &&
  191. !bundleDefines.CHROME &&
  192. !bundleDefines.LIB &&
  193. !bundleDefines.TESTING &&
  194. !disableSourceMaps;
  195. const skipBabel = bundleDefines.SKIP_BABEL;
  196. // `core-js` (see https://github.com/zloirock/core-js/issues/514), and
  197. // `src/core/{glyphlist,unicode}.js` (Babel is too slow for those when
  198. // source-maps are enabled) should be excluded from processing.
  199. const babelExcludes = ["node_modules[\\\\\\/]core-js"];
  200. if (enableSourceMaps) {
  201. babelExcludes.push("src[\\\\\\/]core[\\\\\\/](glyphlist|unicode)");
  202. }
  203. const babelExcludeRegExp = new RegExp(`(${babelExcludes.join("|")})`);
  204. const babelPlugins = ["@babel/plugin-transform-modules-commonjs"];
  205. const plugins = [];
  206. if (!disableLicenseHeader) {
  207. plugins.push(
  208. new webpack2.BannerPlugin({ banner: licenseHeaderLibre, raw: true })
  209. );
  210. }
  211. const experiments =
  212. output.library?.type === "module" ? { outputModule: true } : undefined;
  213. // Required to expose e.g., the `window` object.
  214. output.globalObject = "globalThis";
  215. return {
  216. mode: "none",
  217. experiments,
  218. output,
  219. performance: {
  220. hints: false, // Disable messages about larger file sizes.
  221. },
  222. plugins,
  223. resolve: {
  224. alias: {
  225. pdfjs: path.join(__dirname, "src"),
  226. "pdfjs-web": path.join(__dirname, "web"),
  227. "pdfjs-lib": path.join(__dirname, "web/pdfjs"),
  228. "pdfjs-fitCurve": path.join(__dirname, "src/display/editor/fit_curve"),
  229. },
  230. },
  231. devtool: enableSourceMaps ? "source-map" : undefined,
  232. module: {
  233. rules: [
  234. {
  235. loader: "babel-loader",
  236. exclude: babelExcludeRegExp,
  237. options: {
  238. presets: skipBabel ? undefined : ["@babel/preset-env"],
  239. plugins: babelPlugins,
  240. targets: BABEL_TARGETS,
  241. },
  242. },
  243. {
  244. loader: path.join(__dirname, "external/webpack/pdfjsdev-loader.js"),
  245. options: {
  246. rootPath: __dirname,
  247. saveComments: false,
  248. defines: bundleDefines,
  249. },
  250. },
  251. ],
  252. },
  253. // Avoid shadowing actual Node.js variables with polyfills, by disabling
  254. // polyfills/mocks - https://webpack.js.org/configuration/node/
  255. node: false,
  256. };
  257. }
  258. function webpack2Stream(webpackConfig) {
  259. // Replacing webpack1 to webpack2 in the webpack-stream.
  260. return webpackStream(webpackConfig, webpack2);
  261. }
  262. function getVersionJSON() {
  263. return JSON.parse(fs.readFileSync(BUILD_DIR + "version.json").toString());
  264. }
  265. function checkChromePreferencesFile(chromePrefsPath, webPrefs) {
  266. const chromePrefs = JSON.parse(fs.readFileSync(chromePrefsPath).toString());
  267. const chromePrefsKeys = Object.keys(chromePrefs.properties).filter(key => {
  268. const description = chromePrefs.properties[key].description;
  269. // Deprecated keys are allowed in the managed preferences file.
  270. // The code maintained is responsible for adding migration logic to
  271. // extensions/chromium/options/migration.js and web/chromecom.js .
  272. return !description || !description.startsWith("DEPRECATED.");
  273. });
  274. chromePrefsKeys.sort();
  275. const webPrefsKeys = Object.keys(webPrefs);
  276. webPrefsKeys.sort();
  277. if (webPrefsKeys.length !== chromePrefsKeys.length) {
  278. console.log("Warning: Pref objects doesn't have the same length.");
  279. return false;
  280. }
  281. let ret = true;
  282. for (let i = 0, ii = webPrefsKeys.length; i < ii; i++) {
  283. const value = webPrefsKeys[i];
  284. if (chromePrefsKeys[i] !== value) {
  285. ret = false;
  286. console.log(
  287. `Warning: not the same keys: ${chromePrefsKeys[i]} !== ${value}`
  288. );
  289. } else if (chromePrefs.properties[value].default !== webPrefs[value]) {
  290. ret = false;
  291. console.log(
  292. `Warning: not the same values (for "${value}"): ` +
  293. `${chromePrefs.properties[value].default} !== ${webPrefs[value]}`
  294. );
  295. }
  296. }
  297. return ret;
  298. }
  299. function replaceWebpackRequire() {
  300. // Produced bundles can be rebundled again, avoid collisions (e.g. in api.js)
  301. // by renaming __webpack_require__ to something else.
  302. return replace("__webpack_require__", "__w_pdfjs_require__");
  303. }
  304. function replaceNonWebpackImport() {
  305. return replace("__non_webpack_import__", "import");
  306. }
  307. function replaceJSRootName(amdName, jsName) {
  308. // Saving old-style JS module name.
  309. return replace(
  310. 'root["' + amdName + '"] = factory()',
  311. 'root["' + amdName + '"] = root.' + jsName + " = factory()"
  312. );
  313. }
  314. function createMainBundle(defines) {
  315. const mainAMDName = "pdfjs-dist/build/pdf";
  316. const mainOutputName = "pdf.js";
  317. const mainFileConfig = createWebpackConfig(defines, {
  318. filename: mainOutputName,
  319. library: mainAMDName,
  320. libraryTarget: "umd",
  321. umdNamedDefine: true,
  322. });
  323. return gulp
  324. .src("./src/pdf.js")
  325. .pipe(webpack2Stream(mainFileConfig))
  326. .pipe(replaceWebpackRequire())
  327. .pipe(replaceNonWebpackImport())
  328. .pipe(replaceJSRootName(mainAMDName, "pdfjsLib"));
  329. }
  330. function createScriptingBundle(defines, extraOptions = undefined) {
  331. const scriptingAMDName = "pdfjs-dist/build/pdf.scripting";
  332. const scriptingOutputName = "pdf.scripting.js";
  333. const scriptingFileConfig = createWebpackConfig(
  334. defines,
  335. {
  336. filename: scriptingOutputName,
  337. library: scriptingAMDName,
  338. libraryTarget: "umd",
  339. umdNamedDefine: true,
  340. },
  341. extraOptions
  342. );
  343. return gulp
  344. .src("./src/pdf.scripting.js")
  345. .pipe(webpack2Stream(scriptingFileConfig))
  346. .pipe(replaceWebpackRequire())
  347. .pipe(replaceNonWebpackImport())
  348. .pipe(
  349. replace(
  350. 'root["' + scriptingAMDName + '"] = factory()',
  351. "root.pdfjsScripting = factory()"
  352. )
  353. );
  354. }
  355. function createSandboxExternal(defines) {
  356. const preprocessor2 = require("./external/builder/preprocessor2.js");
  357. const licenseHeader = fs.readFileSync("./src/license_header.js").toString();
  358. const ctx = {
  359. saveComments: false,
  360. defines,
  361. };
  362. return gulp.src("./src/pdf.sandbox.external.js").pipe(
  363. transform("utf8", content => {
  364. content = preprocessor2.preprocessPDFJSCode(ctx, content);
  365. return `${licenseHeader}\n${content}`;
  366. })
  367. );
  368. }
  369. function createTemporaryScriptingBundle(defines, extraOptions = undefined) {
  370. return createScriptingBundle(defines, {
  371. disableVersionInfo: !!(extraOptions && extraOptions.disableVersionInfo),
  372. disableSourceMaps: true,
  373. disableLicenseHeader: true,
  374. }).pipe(gulp.dest(TMP_DIR));
  375. }
  376. function createSandboxBundle(defines, extraOptions = undefined) {
  377. const sandboxAMDName = "pdfjs-dist/build/pdf.sandbox";
  378. const sandboxOutputName = "pdf.sandbox.js";
  379. const scriptingPath = TMP_DIR + "pdf.scripting.js";
  380. // Insert the source as a string to be `eval`-ed in the sandbox.
  381. const sandboxDefines = builder.merge(defines, {
  382. PDF_SCRIPTING_JS_SOURCE: fs.readFileSync(scriptingPath).toString(),
  383. });
  384. fs.unlinkSync(scriptingPath);
  385. const sandboxFileConfig = createWebpackConfig(
  386. sandboxDefines,
  387. {
  388. filename: sandboxOutputName,
  389. library: sandboxAMDName,
  390. libraryTarget: "umd",
  391. umdNamedDefine: true,
  392. },
  393. extraOptions
  394. );
  395. return gulp
  396. .src("./src/pdf.sandbox.js")
  397. .pipe(webpack2Stream(sandboxFileConfig))
  398. .pipe(replaceWebpackRequire())
  399. .pipe(replaceNonWebpackImport())
  400. .pipe(replaceJSRootName(sandboxAMDName, "pdfjsSandbox"));
  401. }
  402. function createWorkerBundle(defines) {
  403. const workerAMDName = "pdfjs-dist/build/pdf.worker";
  404. const workerOutputName = "pdf.worker.js";
  405. const workerFileConfig = createWebpackConfig(defines, {
  406. filename: workerOutputName,
  407. library: workerAMDName,
  408. libraryTarget: "umd",
  409. umdNamedDefine: true,
  410. });
  411. return gulp
  412. .src("./src/pdf.worker.js")
  413. .pipe(webpack2Stream(workerFileConfig))
  414. .pipe(replaceWebpackRequire())
  415. .pipe(replaceNonWebpackImport())
  416. .pipe(replaceJSRootName(workerAMDName, "pdfjsWorker"));
  417. }
  418. function createWebBundle(defines, options) {
  419. const viewerOutputName = "viewer.js";
  420. const viewerFileConfig = createWebpackConfig(
  421. defines,
  422. {
  423. filename: viewerOutputName,
  424. },
  425. {
  426. defaultPreferencesDir: options.defaultPreferencesDir,
  427. }
  428. );
  429. return gulp
  430. .src("./web/viewer.js")
  431. .pipe(webpack2Stream(viewerFileConfig))
  432. .pipe(replaceNonWebpackImport());
  433. }
  434. function createGVWebBundle(defines, options) {
  435. const viewerOutputName = "viewer-geckoview.js";
  436. defines = builder.merge(defines, { GECKOVIEW: true });
  437. const viewerFileConfig = createWebpackConfig(
  438. defines,
  439. {
  440. filename: viewerOutputName,
  441. },
  442. {
  443. defaultPreferencesDir: options.defaultPreferencesDir,
  444. }
  445. );
  446. return gulp
  447. .src("./web/viewer-geckoview.js")
  448. .pipe(webpack2Stream(viewerFileConfig))
  449. .pipe(replaceNonWebpackImport());
  450. }
  451. function createComponentsBundle(defines) {
  452. const componentsAMDName = "pdfjs-dist/web/pdf_viewer";
  453. const componentsOutputName = "pdf_viewer.js";
  454. const componentsFileConfig = createWebpackConfig(defines, {
  455. filename: componentsOutputName,
  456. library: componentsAMDName,
  457. libraryTarget: "umd",
  458. umdNamedDefine: true,
  459. });
  460. return gulp
  461. .src("./web/pdf_viewer.component.js")
  462. .pipe(webpack2Stream(componentsFileConfig))
  463. .pipe(replaceWebpackRequire())
  464. .pipe(replaceNonWebpackImport())
  465. .pipe(replaceJSRootName(componentsAMDName, "pdfjsViewer"));
  466. }
  467. function createImageDecodersBundle(defines) {
  468. const imageDecodersAMDName = "pdfjs-dist/image_decoders/pdf.image_decoders";
  469. const imageDecodersOutputName = "pdf.image_decoders.js";
  470. const componentsFileConfig = createWebpackConfig(defines, {
  471. filename: imageDecodersOutputName,
  472. library: imageDecodersAMDName,
  473. libraryTarget: "umd",
  474. umdNamedDefine: true,
  475. });
  476. return gulp
  477. .src("./src/pdf.image_decoders.js")
  478. .pipe(webpack2Stream(componentsFileConfig))
  479. .pipe(replaceWebpackRequire())
  480. .pipe(replaceNonWebpackImport())
  481. .pipe(replaceJSRootName(imageDecodersAMDName, "pdfjsImageDecoders"));
  482. }
  483. function createFitCurveBundle(defines) {
  484. const fitCurveOutputName = "fit_curve.js";
  485. const fitCurveFileConfig = createWebpackConfig(
  486. defines,
  487. {
  488. filename: fitCurveOutputName,
  489. library: {
  490. type: "module",
  491. },
  492. },
  493. {
  494. disableVersionInfo: true,
  495. }
  496. );
  497. return gulp
  498. .src("src/display/editor/fit_curve.js")
  499. .pipe(webpack2Stream(fitCurveFileConfig));
  500. }
  501. function createCMapBundle() {
  502. return gulp.src(["external/bcmaps/*.bcmap", "external/bcmaps/LICENSE"], {
  503. base: "external/bcmaps",
  504. });
  505. }
  506. function createStandardFontBundle() {
  507. return gulp.src(
  508. [
  509. "external/standard_fonts/*.pfb",
  510. "external/standard_fonts/*.ttf",
  511. "external/standard_fonts/LICENSE_FOXIT",
  512. "external/standard_fonts/LICENSE_LIBERATION",
  513. ],
  514. {
  515. base: "external/standard_fonts",
  516. }
  517. );
  518. }
  519. function checkFile(filePath) {
  520. try {
  521. const stat = fs.lstatSync(filePath);
  522. return stat.isFile();
  523. } catch (e) {
  524. return false;
  525. }
  526. }
  527. function checkDir(dirPath) {
  528. try {
  529. const stat = fs.lstatSync(dirPath);
  530. return stat.isDirectory();
  531. } catch (e) {
  532. return false;
  533. }
  534. }
  535. function replaceInFile(filePath, find, replacement) {
  536. let content = fs.readFileSync(filePath).toString();
  537. content = content.replace(find, replacement);
  538. fs.writeFileSync(filePath, content);
  539. }
  540. function getTempFile(prefix, suffix) {
  541. mkdirp.sync(BUILD_DIR + "tmp/");
  542. const bytes = require("crypto").randomBytes(6).toString("hex");
  543. const filePath = BUILD_DIR + "tmp/" + prefix + bytes + suffix;
  544. fs.writeFileSync(filePath, "");
  545. return filePath;
  546. }
  547. function createTestSource(testsName, { bot = false, xfaOnly = false } = {}) {
  548. const source = stream.Readable({ objectMode: true });
  549. source._read = function () {
  550. console.log();
  551. console.log("### Running " + testsName + " tests");
  552. const PDF_TEST = process.env.PDF_TEST || "test_manifest.json";
  553. let forceNoChrome = false;
  554. const args = ["test.js"];
  555. switch (testsName) {
  556. case "browser":
  557. if (!bot) {
  558. args.push("--reftest");
  559. } else {
  560. const os = process.env.OS;
  561. if (/windows/i.test(os)) {
  562. // The browser-tests are too slow in Google Chrome on the Windows
  563. // bot, causing a timeout, hence disabling them for now.
  564. forceNoChrome = true;
  565. }
  566. }
  567. if (xfaOnly) {
  568. args.push("--xfaOnly");
  569. }
  570. args.push("--manifestFile=" + PDF_TEST);
  571. break;
  572. case "unit":
  573. args.push("--unitTest");
  574. break;
  575. case "font":
  576. args.push("--fontTest");
  577. break;
  578. case "integration":
  579. args.push("--integration");
  580. break;
  581. default:
  582. this.emit("error", new Error("Unknown name: " + testsName));
  583. return null;
  584. }
  585. if (bot) {
  586. args.push("--strictVerify");
  587. }
  588. if (process.argv.includes("--noChrome") || forceNoChrome) {
  589. args.push("--noChrome");
  590. }
  591. const testProcess = startNode(args, { cwd: TEST_DIR, stdio: "inherit" });
  592. testProcess.on("close", function (code) {
  593. source.push(null);
  594. });
  595. return undefined;
  596. };
  597. return source;
  598. }
  599. function makeRef(done, bot) {
  600. console.log();
  601. console.log("### Creating reference images");
  602. let forceNoChrome = false;
  603. const args = ["test.js", "--masterMode"];
  604. if (bot) {
  605. const os = process.env.OS;
  606. if (/windows/i.test(os)) {
  607. // The browser-tests are too slow in Google Chrome on the Windows
  608. // bot, causing a timeout, hence disabling them for now.
  609. forceNoChrome = true;
  610. }
  611. args.push("--noPrompts", "--strictVerify");
  612. }
  613. if (process.argv.includes("--noChrome") || forceNoChrome) {
  614. args.push("--noChrome");
  615. }
  616. const testProcess = startNode(args, { cwd: TEST_DIR, stdio: "inherit" });
  617. testProcess.on("close", function (code) {
  618. done();
  619. });
  620. }
  621. gulp.task("default", function (done) {
  622. console.log("Available tasks:");
  623. const tasks = Object.keys(gulp.registry().tasks());
  624. for (const taskName of tasks.sort()) {
  625. if (taskName.endsWith("-pre")) {
  626. continue;
  627. }
  628. console.log(" " + taskName);
  629. }
  630. done();
  631. });
  632. function createBuildNumber(done) {
  633. console.log();
  634. console.log("### Getting extension build number");
  635. exec(
  636. "git log --format=oneline " + config.baseVersion + "..",
  637. function (err, stdout, stderr) {
  638. let buildNumber = 0;
  639. if (!err) {
  640. // Build number is the number of commits since base version
  641. buildNumber = stdout ? stdout.match(/\n/g).length : 0;
  642. } else {
  643. console.log(
  644. "This is not a Git repository; using default build number."
  645. );
  646. }
  647. console.log("Extension build number: " + buildNumber);
  648. const version = config.versionPrefix + buildNumber;
  649. exec('git log --format="%h" -n 1', function (err2, stdout2, stderr2) {
  650. let buildCommit = "";
  651. if (!err2) {
  652. buildCommit = stdout2.replace("\n", "");
  653. }
  654. createStringSource(
  655. "version.json",
  656. JSON.stringify(
  657. {
  658. version,
  659. build: buildNumber,
  660. commit: buildCommit,
  661. },
  662. null,
  663. 2
  664. )
  665. )
  666. .pipe(gulp.dest(BUILD_DIR))
  667. .on("end", done);
  668. });
  669. }
  670. );
  671. }
  672. function buildDefaultPreferences(defines, dir) {
  673. console.log();
  674. console.log("### Building default preferences");
  675. const bundleDefines = builder.merge(defines, {
  676. LIB: true,
  677. SKIP_BABEL: false,
  678. BUNDLE_VERSION: 0, // Dummy version
  679. BUNDLE_BUILD: 0, // Dummy build
  680. TESTING:
  681. defines.TESTING !== undefined
  682. ? defines.TESTING
  683. : process.env.TESTING === "true",
  684. });
  685. const inputStream = merge([
  686. gulp.src(["web/app_options.js"], {
  687. base: ".",
  688. }),
  689. ]);
  690. return buildLibHelper(
  691. bundleDefines,
  692. inputStream,
  693. DEFAULT_PREFERENCES_DIR + dir
  694. );
  695. }
  696. function getDefaultPreferences(dir) {
  697. const { AppOptions, OptionKind } = require("./" +
  698. DEFAULT_PREFERENCES_DIR +
  699. dir +
  700. "web/app_options.js");
  701. return AppOptions.getAll(OptionKind.PREFERENCE);
  702. }
  703. function getDialogPolyfillCSS() {
  704. return fs
  705. .readFileSync("node_modules/dialog-polyfill/dist/dialog-polyfill.css")
  706. .toString();
  707. }
  708. gulp.task("locale", function () {
  709. const VIEWER_LOCALE_OUTPUT = "web/locale/";
  710. console.log();
  711. console.log("### Building localization files");
  712. rimraf.sync(VIEWER_LOCALE_OUTPUT);
  713. mkdirp.sync(VIEWER_LOCALE_OUTPUT);
  714. const subfolders = fs.readdirSync(L10N_DIR);
  715. subfolders.sort();
  716. let viewerOutput = "";
  717. const locales = [];
  718. for (const locale of subfolders) {
  719. const dirPath = L10N_DIR + locale;
  720. if (!checkDir(dirPath)) {
  721. continue;
  722. }
  723. if (!/^[a-z][a-z]([a-z])?(-[A-Z][A-Z])?$/.test(locale)) {
  724. console.log("Skipping invalid locale: " + locale);
  725. continue;
  726. }
  727. mkdirp.sync(VIEWER_LOCALE_OUTPUT + "/" + locale);
  728. locales.push(locale);
  729. if (checkFile(dirPath + "/viewer.properties")) {
  730. viewerOutput +=
  731. "[" +
  732. locale +
  733. "]\n" +
  734. "@import url(" +
  735. locale +
  736. "/viewer.properties)\n\n";
  737. }
  738. }
  739. return merge([
  740. createStringSource("locale.properties", viewerOutput).pipe(
  741. gulp.dest(VIEWER_LOCALE_OUTPUT)
  742. ),
  743. gulp
  744. .src(L10N_DIR + "/{" + locales.join(",") + "}/viewer.properties", {
  745. base: L10N_DIR,
  746. })
  747. .pipe(gulp.dest(VIEWER_LOCALE_OUTPUT)),
  748. ]);
  749. });
  750. gulp.task("cmaps", function (done) {
  751. const CMAP_INPUT = "external/cmaps";
  752. const VIEWER_CMAP_OUTPUT = "external/bcmaps";
  753. console.log();
  754. console.log("### Building cmaps");
  755. // Testing a file that usually present.
  756. if (!checkFile(CMAP_INPUT + "/UniJIS-UCS2-H")) {
  757. console.log("./external/cmaps has no cmap files, download them from:");
  758. console.log(" https://github.com/adobe-type-tools/cmap-resources");
  759. throw new Error("cmap files were not found");
  760. }
  761. // Remove old bcmap files.
  762. fs.readdirSync(VIEWER_CMAP_OUTPUT).forEach(function (file) {
  763. if (/\.bcmap$/i.test(file)) {
  764. fs.unlinkSync(VIEWER_CMAP_OUTPUT + "/" + file);
  765. }
  766. });
  767. const compressCmaps =
  768. require("./external/cmapscompress/compress.js").compressCmaps;
  769. compressCmaps(CMAP_INPUT, VIEWER_CMAP_OUTPUT, true);
  770. done();
  771. });
  772. function preprocessCSS(source, defines) {
  773. const outName = getTempFile("~preprocess", ".css");
  774. builder.preprocess(source, outName, defines);
  775. let out = fs.readFileSync(outName).toString();
  776. fs.unlinkSync(outName);
  777. // Strip out all license headers in the middle.
  778. const reg = /\n\/\* Copyright(.|\n)*?Mozilla Foundation(.|\n)*?\*\//g;
  779. out = out.replace(reg, "");
  780. const i = source.lastIndexOf("/");
  781. return createStringSource(source.substr(i + 1), out);
  782. }
  783. function preprocessHTML(source, defines) {
  784. const outName = getTempFile("~preprocess", ".html");
  785. builder.preprocess(source, outName, defines);
  786. const out = fs.readFileSync(outName).toString();
  787. fs.unlinkSync(outName);
  788. const i = source.lastIndexOf("/");
  789. return createStringSource(source.substr(i + 1), `${out.trimEnd()}\n`);
  790. }
  791. function buildGeneric(defines, dir) {
  792. rimraf.sync(dir);
  793. return merge([
  794. createMainBundle(defines).pipe(gulp.dest(dir + "build")),
  795. createWorkerBundle(defines).pipe(gulp.dest(dir + "build")),
  796. createSandboxBundle(defines).pipe(gulp.dest(dir + "build")),
  797. createWebBundle(defines, {
  798. defaultPreferencesDir: defines.SKIP_BABEL
  799. ? "generic/"
  800. : "generic-legacy/",
  801. }).pipe(gulp.dest(dir + "web")),
  802. gulp.src(COMMON_WEB_FILES, { base: "web/" }).pipe(gulp.dest(dir + "web")),
  803. gulp.src("LICENSE").pipe(gulp.dest(dir)),
  804. gulp
  805. .src(["web/locale/*/viewer.properties", "web/locale/locale.properties"], {
  806. base: "web/",
  807. })
  808. .pipe(gulp.dest(dir + "web")),
  809. createCMapBundle().pipe(gulp.dest(dir + "web/cmaps")),
  810. createStandardFontBundle().pipe(gulp.dest(dir + "web/standard_fonts")),
  811. preprocessHTML("web/viewer.html", defines).pipe(gulp.dest(dir + "web")),
  812. preprocessCSS("web/viewer.css", defines)
  813. .pipe(
  814. postcss([
  815. postcssLogical({ preserve: true }),
  816. postcssDirPseudoClass(),
  817. autoprefixer(AUTOPREFIXER_CONFIG),
  818. ])
  819. )
  820. .pipe(gulp.dest(dir + "web")),
  821. gulp
  822. .src("web/compressed.tracemonkey-pldi-09.pdf")
  823. .pipe(gulp.dest(dir + "web")),
  824. ]);
  825. }
  826. // Builds the generic production viewer that is only compatible with up-to-date
  827. // HTML5 browsers, which implement modern ECMAScript features.
  828. gulp.task(
  829. "generic",
  830. gulp.series(
  831. createBuildNumber,
  832. "locale",
  833. function scriptingGeneric() {
  834. const defines = builder.merge(DEFINES, { GENERIC: true });
  835. return merge([
  836. buildDefaultPreferences(defines, "generic/"),
  837. createTemporaryScriptingBundle(defines),
  838. ]);
  839. },
  840. function createGeneric() {
  841. console.log();
  842. console.log("### Creating generic viewer");
  843. const defines = builder.merge(DEFINES, { GENERIC: true });
  844. return buildGeneric(defines, GENERIC_DIR);
  845. }
  846. )
  847. );
  848. // Builds the generic production viewer that should be compatible with most
  849. // older HTML5 browsers.
  850. gulp.task(
  851. "generic-legacy",
  852. gulp.series(
  853. createBuildNumber,
  854. "locale",
  855. function scriptingGenericLegacy() {
  856. const defines = builder.merge(DEFINES, {
  857. GENERIC: true,
  858. SKIP_BABEL: false,
  859. });
  860. return merge([
  861. buildDefaultPreferences(defines, "generic-legacy/"),
  862. createTemporaryScriptingBundle(defines),
  863. ]);
  864. },
  865. function createGenericLegacy() {
  866. console.log();
  867. console.log("### Creating generic (legacy) viewer");
  868. const defines = builder.merge(DEFINES, {
  869. GENERIC: true,
  870. SKIP_BABEL: false,
  871. });
  872. return buildGeneric(defines, GENERIC_LEGACY_DIR);
  873. }
  874. )
  875. );
  876. function buildComponents(defines, dir) {
  877. rimraf.sync(dir);
  878. const COMPONENTS_IMAGES = [
  879. "web/images/annotation-*.svg",
  880. "web/images/loading-icon.gif",
  881. "web/images/shadow.png",
  882. ];
  883. return merge([
  884. createComponentsBundle(defines).pipe(gulp.dest(dir)),
  885. gulp.src(COMPONENTS_IMAGES).pipe(gulp.dest(dir + "images")),
  886. preprocessCSS("web/pdf_viewer.css", defines)
  887. .pipe(
  888. postcss([
  889. postcssLogical({ preserve: true }),
  890. postcssDirPseudoClass(),
  891. autoprefixer(AUTOPREFIXER_CONFIG),
  892. ])
  893. )
  894. .pipe(gulp.dest(dir)),
  895. ]);
  896. }
  897. gulp.task(
  898. "components",
  899. gulp.series(createBuildNumber, function createComponents() {
  900. console.log();
  901. console.log("### Creating generic components");
  902. const defines = builder.merge(DEFINES, { COMPONENTS: true, GENERIC: true });
  903. return buildComponents(defines, COMPONENTS_DIR);
  904. })
  905. );
  906. gulp.task(
  907. "components-legacy",
  908. gulp.series(createBuildNumber, function createComponentsLegacy() {
  909. console.log();
  910. console.log("### Creating generic (legacy) components");
  911. const defines = builder.merge(DEFINES, {
  912. COMPONENTS: true,
  913. GENERIC: true,
  914. SKIP_BABEL: false,
  915. });
  916. return buildComponents(defines, COMPONENTS_LEGACY_DIR);
  917. })
  918. );
  919. gulp.task(
  920. "image_decoders",
  921. gulp.series(createBuildNumber, function createImageDecoders() {
  922. console.log();
  923. console.log("### Creating image decoders");
  924. const defines = builder.merge(DEFINES, {
  925. GENERIC: true,
  926. IMAGE_DECODERS: true,
  927. });
  928. return createImageDecodersBundle(defines).pipe(
  929. gulp.dest(IMAGE_DECODERS_DIR)
  930. );
  931. })
  932. );
  933. gulp.task(
  934. "image_decoders-legacy",
  935. gulp.series(createBuildNumber, function createImageDecodersLegacy() {
  936. console.log();
  937. console.log("### Creating (legacy) image decoders");
  938. const defines = builder.merge(DEFINES, {
  939. GENERIC: true,
  940. IMAGE_DECODERS: true,
  941. SKIP_BABEL: false,
  942. });
  943. return createImageDecodersBundle(defines).pipe(
  944. gulp.dest(IMAGE_DECODERS_LEGACY_DIR)
  945. );
  946. })
  947. );
  948. function buildMinified(defines, dir) {
  949. rimraf.sync(dir);
  950. return merge([
  951. createMainBundle(defines).pipe(gulp.dest(dir + "build")),
  952. createWorkerBundle(defines).pipe(gulp.dest(dir + "build")),
  953. createSandboxBundle(defines).pipe(gulp.dest(dir + "build")),
  954. createWebBundle(defines, {
  955. defaultPreferencesDir: defines.SKIP_BABEL
  956. ? "minified/"
  957. : "minified-legacy/",
  958. }).pipe(gulp.dest(dir + "web")),
  959. createImageDecodersBundle(
  960. builder.merge(defines, { IMAGE_DECODERS: true })
  961. ).pipe(gulp.dest(dir + "image_decoders")),
  962. gulp.src(COMMON_WEB_FILES, { base: "web/" }).pipe(gulp.dest(dir + "web")),
  963. gulp
  964. .src(["web/locale/*/viewer.properties", "web/locale/locale.properties"], {
  965. base: "web/",
  966. })
  967. .pipe(gulp.dest(dir + "web")),
  968. createCMapBundle().pipe(gulp.dest(dir + "web/cmaps")),
  969. createStandardFontBundle().pipe(gulp.dest(dir + "web/standard_fonts")),
  970. preprocessHTML("web/viewer.html", defines).pipe(gulp.dest(dir + "web")),
  971. preprocessCSS("web/viewer.css", defines)
  972. .pipe(
  973. postcss([
  974. postcssLogical({ preserve: true }),
  975. postcssDirPseudoClass(),
  976. autoprefixer(AUTOPREFIXER_CONFIG),
  977. ])
  978. )
  979. .pipe(gulp.dest(dir + "web")),
  980. gulp
  981. .src("web/compressed.tracemonkey-pldi-09.pdf")
  982. .pipe(gulp.dest(dir + "web")),
  983. ]);
  984. }
  985. async function parseMinified(dir) {
  986. const pdfFile = fs.readFileSync(dir + "/build/pdf.js").toString();
  987. const pdfWorkerFile = fs
  988. .readFileSync(dir + "/build/pdf.worker.js")
  989. .toString();
  990. const pdfSandboxFile = fs
  991. .readFileSync(dir + "/build/pdf.sandbox.js")
  992. .toString();
  993. const pdfImageDecodersFile = fs
  994. .readFileSync(dir + "/image_decoders/pdf.image_decoders.js")
  995. .toString();
  996. const viewerFiles = {
  997. "pdf.js": pdfFile,
  998. "viewer.js": fs.readFileSync(dir + "/web/viewer.js").toString(),
  999. };
  1000. console.log();
  1001. console.log("### Minifying js files");
  1002. const Terser = require("terser");
  1003. const options = {
  1004. compress: {
  1005. // V8 chokes on very long sequences, work around that.
  1006. sequences: false,
  1007. },
  1008. keep_classnames: true,
  1009. keep_fnames: true,
  1010. };
  1011. fs.writeFileSync(
  1012. dir + "/web/pdf.viewer.js",
  1013. (await Terser.minify(viewerFiles, options)).code
  1014. );
  1015. fs.writeFileSync(
  1016. dir + "/build/pdf.min.js",
  1017. (await Terser.minify(pdfFile, options)).code
  1018. );
  1019. fs.writeFileSync(
  1020. dir + "/build/pdf.worker.min.js",
  1021. (await Terser.minify(pdfWorkerFile, options)).code
  1022. );
  1023. fs.writeFileSync(
  1024. dir + "/build/pdf.sandbox.min.js",
  1025. (await Terser.minify(pdfSandboxFile, options)).code
  1026. );
  1027. fs.writeFileSync(
  1028. dir + "image_decoders/pdf.image_decoders.min.js",
  1029. (await Terser.minify(pdfImageDecodersFile, options)).code
  1030. );
  1031. console.log();
  1032. console.log("### Cleaning js files");
  1033. fs.unlinkSync(dir + "/web/viewer.js");
  1034. fs.unlinkSync(dir + "/web/debugger.js");
  1035. fs.unlinkSync(dir + "/build/pdf.js");
  1036. fs.unlinkSync(dir + "/build/pdf.worker.js");
  1037. fs.unlinkSync(dir + "/build/pdf.sandbox.js");
  1038. fs.renameSync(dir + "/build/pdf.min.js", dir + "/build/pdf.js");
  1039. fs.renameSync(dir + "/build/pdf.worker.min.js", dir + "/build/pdf.worker.js");
  1040. fs.renameSync(
  1041. dir + "/build/pdf.sandbox.min.js",
  1042. dir + "/build/pdf.sandbox.js"
  1043. );
  1044. fs.renameSync(
  1045. dir + "/image_decoders/pdf.image_decoders.min.js",
  1046. dir + "/image_decoders/pdf.image_decoders.js"
  1047. );
  1048. }
  1049. gulp.task(
  1050. "minified",
  1051. gulp.series(
  1052. createBuildNumber,
  1053. "locale",
  1054. function scriptingMinified() {
  1055. const defines = builder.merge(DEFINES, { MINIFIED: true, GENERIC: true });
  1056. return merge([
  1057. buildDefaultPreferences(defines, "minified/"),
  1058. createTemporaryScriptingBundle(defines),
  1059. ]);
  1060. },
  1061. function createMinified() {
  1062. console.log();
  1063. console.log("### Creating minified viewer");
  1064. const defines = builder.merge(DEFINES, { MINIFIED: true, GENERIC: true });
  1065. return buildMinified(defines, MINIFIED_DIR);
  1066. },
  1067. async function compressMinified(done) {
  1068. await parseMinified(MINIFIED_DIR);
  1069. done();
  1070. }
  1071. )
  1072. );
  1073. gulp.task(
  1074. "minified-legacy",
  1075. gulp.series(
  1076. createBuildNumber,
  1077. "locale",
  1078. function scriptingMinifiedLegacy() {
  1079. const defines = builder.merge(DEFINES, {
  1080. MINIFIED: true,
  1081. GENERIC: true,
  1082. SKIP_BABEL: false,
  1083. });
  1084. return merge([
  1085. buildDefaultPreferences(defines, "minified-legacy/"),
  1086. createTemporaryScriptingBundle(defines),
  1087. ]);
  1088. },
  1089. function createMinifiedLegacy() {
  1090. console.log();
  1091. console.log("### Creating minified (legacy) viewer");
  1092. const defines = builder.merge(DEFINES, {
  1093. MINIFIED: true,
  1094. GENERIC: true,
  1095. SKIP_BABEL: false,
  1096. });
  1097. return buildMinified(defines, MINIFIED_LEGACY_DIR);
  1098. },
  1099. async function compressMinifiedLegacy(done) {
  1100. await parseMinified(MINIFIED_LEGACY_DIR);
  1101. done();
  1102. }
  1103. )
  1104. );
  1105. function preprocessDefaultPreferences(content) {
  1106. const preprocessor2 = require("./external/builder/preprocessor2.js");
  1107. const licenseHeader = fs.readFileSync("./src/license_header.js").toString();
  1108. const GLOBALS = "/* eslint-disable */\n";
  1109. const MODIFICATION_WARNING =
  1110. "//\n// THIS FILE IS GENERATED AUTOMATICALLY, DO NOT EDIT MANUALLY!\n//\n";
  1111. const bundleDefines = builder.merge(DEFINES, {
  1112. DEFAULT_PREFERENCES: getDefaultPreferences("mozcentral/"),
  1113. });
  1114. content = preprocessor2.preprocessPDFJSCode(
  1115. {
  1116. rootPath: __dirname,
  1117. defines: bundleDefines,
  1118. },
  1119. content
  1120. );
  1121. return (
  1122. licenseHeader +
  1123. "\n" +
  1124. GLOBALS +
  1125. "\n" +
  1126. MODIFICATION_WARNING +
  1127. "\n" +
  1128. content +
  1129. "\n"
  1130. );
  1131. }
  1132. gulp.task(
  1133. "mozcentral",
  1134. gulp.series(
  1135. createBuildNumber,
  1136. function scriptingMozcentral() {
  1137. const defines = builder.merge(DEFINES, { MOZCENTRAL: true });
  1138. return buildDefaultPreferences(defines, "mozcentral/");
  1139. },
  1140. function createMozcentral() {
  1141. console.log();
  1142. console.log("### Building mozilla-central extension");
  1143. const defines = builder.merge(DEFINES, { MOZCENTRAL: true });
  1144. const MOZCENTRAL_DIR = BUILD_DIR + "mozcentral/",
  1145. MOZCENTRAL_EXTENSION_DIR = MOZCENTRAL_DIR + "browser/extensions/pdfjs/",
  1146. MOZCENTRAL_CONTENT_DIR = MOZCENTRAL_EXTENSION_DIR + "content/",
  1147. MOZCENTRAL_L10N_DIR =
  1148. MOZCENTRAL_DIR + "browser/locales/en-US/pdfviewer/",
  1149. FIREFOX_CONTENT_DIR = EXTENSION_SRC_DIR + "/firefox/content/";
  1150. const MOZCENTRAL_WEB_FILES = [
  1151. ...COMMON_WEB_FILES,
  1152. "!web/images/toolbarButton-openFile.svg",
  1153. ];
  1154. // Clear out everything in the firefox extension build directory
  1155. rimraf.sync(MOZCENTRAL_DIR);
  1156. return merge([
  1157. createMainBundle(defines).pipe(
  1158. gulp.dest(MOZCENTRAL_CONTENT_DIR + "build")
  1159. ),
  1160. createScriptingBundle(defines).pipe(
  1161. gulp.dest(MOZCENTRAL_CONTENT_DIR + "build")
  1162. ),
  1163. createSandboxExternal(defines).pipe(
  1164. gulp.dest(MOZCENTRAL_CONTENT_DIR + "build")
  1165. ),
  1166. createWorkerBundle(defines).pipe(
  1167. gulp.dest(MOZCENTRAL_CONTENT_DIR + "build")
  1168. ),
  1169. createWebBundle(defines, { defaultPreferencesDir: "mozcentral/" }).pipe(
  1170. gulp.dest(MOZCENTRAL_CONTENT_DIR + "web")
  1171. ),
  1172. createGVWebBundle(defines, {
  1173. defaultPreferencesDir: "mozcentral/",
  1174. }).pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR + "web")),
  1175. gulp
  1176. .src(MOZCENTRAL_WEB_FILES, { base: "web/" })
  1177. .pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR + "web")),
  1178. createCMapBundle().pipe(
  1179. gulp.dest(MOZCENTRAL_CONTENT_DIR + "web/cmaps")
  1180. ),
  1181. createStandardFontBundle().pipe(
  1182. gulp.dest(MOZCENTRAL_CONTENT_DIR + "web/standard_fonts")
  1183. ),
  1184. preprocessHTML("web/viewer.html", defines).pipe(
  1185. gulp.dest(MOZCENTRAL_CONTENT_DIR + "web")
  1186. ),
  1187. preprocessHTML("web/viewer-geckoview.html", defines).pipe(
  1188. gulp.dest(MOZCENTRAL_CONTENT_DIR + "web")
  1189. ),
  1190. preprocessCSS("web/viewer.css", defines)
  1191. .pipe(
  1192. postcss([
  1193. autoprefixer({
  1194. overrideBrowserslist: ["last 1 firefox versions"],
  1195. }),
  1196. ])
  1197. )
  1198. .pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR + "web")),
  1199. preprocessCSS("web/viewer-geckoview.css", defines)
  1200. .pipe(
  1201. postcss([
  1202. autoprefixer({
  1203. overrideBrowserslist: ["last 1 firefox versions"],
  1204. }),
  1205. ])
  1206. )
  1207. .pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR + "web")),
  1208. gulp
  1209. .src("l10n/en-US/*.properties")
  1210. .pipe(gulp.dest(MOZCENTRAL_L10N_DIR)),
  1211. gulp.src("LICENSE").pipe(gulp.dest(MOZCENTRAL_EXTENSION_DIR)),
  1212. gulp
  1213. .src(FIREFOX_CONTENT_DIR + "PdfJsDefaultPreferences.jsm")
  1214. .pipe(transform("utf8", preprocessDefaultPreferences))
  1215. .pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR)),
  1216. ]);
  1217. }
  1218. )
  1219. );
  1220. gulp.task(
  1221. "chromium",
  1222. gulp.series(
  1223. createBuildNumber,
  1224. "locale",
  1225. function scriptingChromium() {
  1226. const defines = builder.merge(DEFINES, {
  1227. CHROME: true,
  1228. SKIP_BABEL: false,
  1229. });
  1230. return merge([
  1231. buildDefaultPreferences(defines, "chromium/"),
  1232. createTemporaryScriptingBundle(defines),
  1233. ]);
  1234. },
  1235. function createChromium() {
  1236. console.log();
  1237. console.log("### Building Chromium extension");
  1238. const defines = builder.merge(DEFINES, {
  1239. CHROME: true,
  1240. SKIP_BABEL: false,
  1241. });
  1242. const CHROME_BUILD_DIR = BUILD_DIR + "/chromium/",
  1243. CHROME_BUILD_CONTENT_DIR = CHROME_BUILD_DIR + "/content/";
  1244. const CHROME_WEB_FILES = [
  1245. ...COMMON_WEB_FILES,
  1246. "!web/images/toolbarButton-openFile.svg",
  1247. ];
  1248. // Clear out everything in the chrome extension build directory
  1249. rimraf.sync(CHROME_BUILD_DIR);
  1250. const version = getVersionJSON().version;
  1251. return merge([
  1252. createMainBundle(defines).pipe(
  1253. gulp.dest(CHROME_BUILD_CONTENT_DIR + "build")
  1254. ),
  1255. createWorkerBundle(defines).pipe(
  1256. gulp.dest(CHROME_BUILD_CONTENT_DIR + "build")
  1257. ),
  1258. createSandboxBundle(defines).pipe(
  1259. gulp.dest(CHROME_BUILD_CONTENT_DIR + "build")
  1260. ),
  1261. createWebBundle(defines, { defaultPreferencesDir: "chromium/" }).pipe(
  1262. gulp.dest(CHROME_BUILD_CONTENT_DIR + "web")
  1263. ),
  1264. gulp
  1265. .src(CHROME_WEB_FILES, { base: "web/" })
  1266. .pipe(gulp.dest(CHROME_BUILD_CONTENT_DIR + "web")),
  1267. gulp
  1268. .src(
  1269. ["web/locale/*/viewer.properties", "web/locale/locale.properties"],
  1270. { base: "web/" }
  1271. )
  1272. .pipe(gulp.dest(CHROME_BUILD_CONTENT_DIR + "web")),
  1273. createCMapBundle().pipe(
  1274. gulp.dest(CHROME_BUILD_CONTENT_DIR + "web/cmaps")
  1275. ),
  1276. createStandardFontBundle().pipe(
  1277. gulp.dest(CHROME_BUILD_CONTENT_DIR + "web/standard_fonts")
  1278. ),
  1279. preprocessHTML("web/viewer.html", defines).pipe(
  1280. gulp.dest(CHROME_BUILD_CONTENT_DIR + "web")
  1281. ),
  1282. preprocessCSS("web/viewer.css", defines)
  1283. .pipe(
  1284. postcss([
  1285. postcssLogical({ preserve: true }),
  1286. postcssDirPseudoClass(),
  1287. autoprefixer({ overrideBrowserslist: ["Chrome >= 85"] }),
  1288. ])
  1289. )
  1290. .pipe(gulp.dest(CHROME_BUILD_CONTENT_DIR + "web")),
  1291. gulp.src("LICENSE").pipe(gulp.dest(CHROME_BUILD_DIR)),
  1292. gulp
  1293. .src("extensions/chromium/manifest.json")
  1294. .pipe(replace(/\bPDFJSSCRIPT_VERSION\b/g, version))
  1295. .pipe(gulp.dest(CHROME_BUILD_DIR)),
  1296. gulp
  1297. .src(
  1298. [
  1299. "extensions/chromium/**/*.{html,js,css,png}",
  1300. "extensions/chromium/preferences_schema.json",
  1301. ],
  1302. { base: "extensions/chromium/" }
  1303. )
  1304. .pipe(gulp.dest(CHROME_BUILD_DIR)),
  1305. ]);
  1306. }
  1307. )
  1308. );
  1309. gulp.task("jsdoc", function (done) {
  1310. console.log();
  1311. console.log("### Generating documentation (JSDoc)");
  1312. const JSDOC_FILES = ["src/display/api.js"];
  1313. rimraf(JSDOC_BUILD_DIR, function () {
  1314. mkdirp(JSDOC_BUILD_DIR).then(function () {
  1315. const command =
  1316. '"node_modules/.bin/jsdoc" -d ' +
  1317. JSDOC_BUILD_DIR +
  1318. " " +
  1319. JSDOC_FILES.join(" ");
  1320. exec(command, done);
  1321. });
  1322. });
  1323. });
  1324. gulp.task("types", function (done) {
  1325. console.log("### Generating TypeScript definitions using `tsc`");
  1326. const args = [
  1327. "target ESNext",
  1328. "allowJS",
  1329. "declaration",
  1330. `outDir ${TYPES_DIR}`,
  1331. "strict",
  1332. "esModuleInterop",
  1333. "forceConsistentCasingInFileNames",
  1334. "emitDeclarationOnly",
  1335. "moduleResolution node",
  1336. ].join(" --");
  1337. exec(
  1338. `"node_modules/.bin/tsc" --${args} src/pdf.js web/pdf_viewer.component.js`,
  1339. done
  1340. );
  1341. });
  1342. function buildLibHelper(bundleDefines, inputStream, outputDir) {
  1343. // When we create a bundle, webpack is run on the source and it will replace
  1344. // require with __webpack_require__. When we want to use the real require,
  1345. // __non_webpack_require__ has to be used.
  1346. // In this target, we don't create a bundle, so we have to replace the
  1347. // occurrences of __non_webpack_require__ ourselves.
  1348. function babelPluginReplaceNonWebpackImports(babel) {
  1349. return {
  1350. visitor: {
  1351. Identifier(curPath, state) {
  1352. if (curPath.node.name === "__non_webpack_require__") {
  1353. curPath.replaceWith(babel.types.identifier("require"));
  1354. } else if (curPath.node.name === "__non_webpack_import__") {
  1355. curPath.replaceWith(babel.types.identifier("import"));
  1356. }
  1357. },
  1358. },
  1359. };
  1360. }
  1361. function preprocess(content) {
  1362. const skipBabel =
  1363. bundleDefines.SKIP_BABEL || /\/\*\s*no-babel-preset\s*\*\//.test(content);
  1364. content = preprocessor2.preprocessPDFJSCode(ctx, content);
  1365. content = babel.transform(content, {
  1366. sourceType: "module",
  1367. presets: skipBabel ? undefined : ["@babel/preset-env"],
  1368. plugins: [
  1369. "@babel/plugin-transform-modules-commonjs",
  1370. babelPluginReplaceNonWebpackImports,
  1371. ],
  1372. targets: BABEL_TARGETS,
  1373. }).code;
  1374. const removeCjsSrc =
  1375. /^(var\s+\w+\s*=\s*(_interopRequireDefault\()?require\(".*?)(?:\/src)(\/[^"]*"\)\)?;)$/gm;
  1376. content = content.replace(removeCjsSrc, (all, prefix, interop, suffix) => {
  1377. return prefix + suffix;
  1378. });
  1379. return licenseHeaderLibre + content;
  1380. }
  1381. const babel = require("@babel/core");
  1382. const ctx = {
  1383. rootPath: __dirname,
  1384. saveComments: false,
  1385. defines: bundleDefines,
  1386. map: {
  1387. "pdfjs-lib": "../pdf",
  1388. "pdfjs-fitCurve": "./fit_curve",
  1389. },
  1390. };
  1391. const licenseHeaderLibre = fs
  1392. .readFileSync("./src/license_header_libre.js")
  1393. .toString();
  1394. const preprocessor2 = require("./external/builder/preprocessor2.js");
  1395. return inputStream
  1396. .pipe(transform("utf8", preprocess))
  1397. .pipe(gulp.dest(outputDir));
  1398. }
  1399. function buildLib(defines, dir) {
  1400. const versionInfo = getVersionJSON();
  1401. const bundleDefines = builder.merge(defines, {
  1402. BUNDLE_VERSION: versionInfo.version,
  1403. BUNDLE_BUILD: versionInfo.commit,
  1404. TESTING:
  1405. defines.TESTING !== undefined
  1406. ? defines.TESTING
  1407. : process.env.TESTING === "true",
  1408. DEFAULT_PREFERENCES: getDefaultPreferences(
  1409. defines.SKIP_BABEL ? "lib/" : "lib-legacy/"
  1410. ),
  1411. DIALOG_POLYFILL_CSS:
  1412. defines.GENERIC && !defines.SKIP_BABEL ? getDialogPolyfillCSS() : "",
  1413. });
  1414. const inputStream = merge([
  1415. gulp.src(
  1416. [
  1417. "src/{core,display,shared}/**/*.js",
  1418. "!src/shared/{cffStandardStrings,fonts_utils}.js",
  1419. "src/{pdf,pdf.worker}.js",
  1420. ],
  1421. { base: "src/" }
  1422. ),
  1423. gulp.src(
  1424. ["examples/node/domstubs.js", "web/*.js", "!web/{pdfjs,viewer}.js"],
  1425. { base: "." }
  1426. ),
  1427. gulp.src("test/unit/*.js", { base: "." }),
  1428. ]);
  1429. return buildLibHelper(bundleDefines, inputStream, dir);
  1430. }
  1431. gulp.task(
  1432. "lib",
  1433. gulp.series(
  1434. createBuildNumber,
  1435. function scriptingLib() {
  1436. const defines = builder.merge(DEFINES, { GENERIC: true, LIB: true });
  1437. return merge([
  1438. buildDefaultPreferences(defines, "lib/"),
  1439. createTemporaryScriptingBundle(defines),
  1440. ]);
  1441. },
  1442. function createLib() {
  1443. const defines = builder.merge(DEFINES, { GENERIC: true, LIB: true });
  1444. return merge([
  1445. buildLib(defines, "build/lib/"),
  1446. createSandboxBundle(defines).pipe(gulp.dest("build/lib/")),
  1447. ]);
  1448. }
  1449. )
  1450. );
  1451. gulp.task(
  1452. "lib-legacy",
  1453. gulp.series(
  1454. createBuildNumber,
  1455. function scriptingLibLegacy() {
  1456. const defines = builder.merge(DEFINES, {
  1457. GENERIC: true,
  1458. LIB: true,
  1459. SKIP_BABEL: false,
  1460. });
  1461. return merge([
  1462. buildDefaultPreferences(defines, "lib-legacy/"),
  1463. createTemporaryScriptingBundle(defines),
  1464. ]);
  1465. },
  1466. function createLibLegacy() {
  1467. const defines = builder.merge(DEFINES, {
  1468. GENERIC: true,
  1469. LIB: true,
  1470. SKIP_BABEL: false,
  1471. });
  1472. return merge([
  1473. buildLib(defines, "build/lib-legacy/"),
  1474. createSandboxBundle(defines).pipe(gulp.dest("build/lib-legacy/")),
  1475. ]);
  1476. }
  1477. )
  1478. );
  1479. function compressPublish(targetName, dir) {
  1480. return gulp
  1481. .src(dir + "**")
  1482. .pipe(zip(targetName))
  1483. .pipe(gulp.dest(BUILD_DIR))
  1484. .on("end", function () {
  1485. console.log("Built distribution file: " + targetName);
  1486. });
  1487. }
  1488. gulp.task(
  1489. "publish",
  1490. gulp.series("generic", "generic-legacy", function createPublish(done) {
  1491. const version = JSON.parse(
  1492. fs.readFileSync(BUILD_DIR + "version.json").toString()
  1493. ).version;
  1494. config.stableVersion = version;
  1495. return merge([
  1496. createStringSource(CONFIG_FILE, JSON.stringify(config, null, 2)).pipe(
  1497. gulp.dest(".")
  1498. ),
  1499. compressPublish("pdfjs-" + version + "-dist.zip", GENERIC_DIR),
  1500. compressPublish(
  1501. "pdfjs-" + version + "-legacy-dist.zip",
  1502. GENERIC_LEGACY_DIR
  1503. ),
  1504. ]);
  1505. })
  1506. );
  1507. function setTestEnv(done) {
  1508. process.env.TESTING = "true";
  1509. // TODO: Re-write the relevant unit-tests, which are using `new Date(...)`,
  1510. // to not required the following time-zone hack since it doesn't work
  1511. // when the unit-tests are run directly in the browser.
  1512. process.env.TZ = "UTC";
  1513. done();
  1514. }
  1515. gulp.task("dev-fitCurve", function createDevFitCurve() {
  1516. console.log();
  1517. console.log("### Building development fitCurve");
  1518. const defines = builder.merge(DEFINES, { GENERIC: true, TESTING: true });
  1519. const fitCurveDir = BUILD_DIR + "dev-fitCurve/";
  1520. rimraf.sync(fitCurveDir);
  1521. return createFitCurveBundle(defines).pipe(gulp.dest(fitCurveDir));
  1522. });
  1523. gulp.task(
  1524. "test",
  1525. gulp.series(
  1526. setTestEnv,
  1527. "generic",
  1528. "components",
  1529. "dev-fitCurve",
  1530. function runTest() {
  1531. return streamqueue(
  1532. { objectMode: true },
  1533. createTestSource("unit"),
  1534. createTestSource("browser"),
  1535. createTestSource("integration")
  1536. );
  1537. }
  1538. )
  1539. );
  1540. gulp.task(
  1541. "bottest",
  1542. gulp.series(
  1543. setTestEnv,
  1544. "generic",
  1545. "components",
  1546. "dev-fitCurve",
  1547. function runBotTest() {
  1548. return streamqueue(
  1549. { objectMode: true },
  1550. createTestSource("unit", { bot: true }),
  1551. createTestSource("font", { bot: true }),
  1552. createTestSource("browser", { bot: true }),
  1553. createTestSource("integration")
  1554. );
  1555. }
  1556. )
  1557. );
  1558. gulp.task(
  1559. "xfatest",
  1560. gulp.series(
  1561. setTestEnv,
  1562. "generic",
  1563. "components",
  1564. "dev-fitCurve",
  1565. function runXfaTest() {
  1566. return streamqueue(
  1567. { objectMode: true },
  1568. createTestSource("unit"),
  1569. createTestSource("browser", { xfaOnly: true }),
  1570. createTestSource("integration")
  1571. );
  1572. }
  1573. )
  1574. );
  1575. gulp.task(
  1576. "botxfatest",
  1577. gulp.series(
  1578. setTestEnv,
  1579. "generic",
  1580. "components",
  1581. "dev-fitCurve",
  1582. function runBotXfaTest() {
  1583. return streamqueue(
  1584. { objectMode: true },
  1585. createTestSource("unit", { bot: true }),
  1586. createTestSource("font", { bot: true }),
  1587. createTestSource("browser", { bot: true, xfaOnly: true }),
  1588. createTestSource("integration")
  1589. );
  1590. }
  1591. )
  1592. );
  1593. gulp.task(
  1594. "browsertest",
  1595. gulp.series(setTestEnv, "generic", "components", function runBrowserTest() {
  1596. return createTestSource("browser");
  1597. })
  1598. );
  1599. gulp.task(
  1600. "botbrowsertest",
  1601. gulp.series(
  1602. setTestEnv,
  1603. "generic",
  1604. "components",
  1605. function runBotBrowserTest() {
  1606. return streamqueue(
  1607. { objectMode: true },
  1608. createTestSource("browser", { bot: true })
  1609. );
  1610. }
  1611. )
  1612. );
  1613. gulp.task(
  1614. "unittest",
  1615. gulp.series(setTestEnv, "generic", "dev-fitCurve", function runUnitTest() {
  1616. return createTestSource("unit");
  1617. })
  1618. );
  1619. gulp.task(
  1620. "integrationtest",
  1621. gulp.series(setTestEnv, "generic", function runIntegrationTest() {
  1622. return createTestSource("integration");
  1623. })
  1624. );
  1625. gulp.task(
  1626. "fonttest",
  1627. gulp.series(setTestEnv, function runFontTest() {
  1628. return createTestSource("font");
  1629. })
  1630. );
  1631. gulp.task(
  1632. "makeref",
  1633. gulp.series(setTestEnv, "generic", "components", function runMakeref(done) {
  1634. makeRef(done);
  1635. })
  1636. );
  1637. gulp.task(
  1638. "botmakeref",
  1639. gulp.series(
  1640. setTestEnv,
  1641. "generic",
  1642. "components",
  1643. function runBotMakeref(done) {
  1644. makeRef(done, true);
  1645. }
  1646. )
  1647. );
  1648. gulp.task(
  1649. "typestest",
  1650. gulp.series(
  1651. setTestEnv,
  1652. "generic",
  1653. "types",
  1654. function createTypesTest() {
  1655. return merge([
  1656. packageJson().pipe(gulp.dest(TYPESTEST_DIR)),
  1657. gulp
  1658. .src([
  1659. GENERIC_DIR + "build/pdf.js",
  1660. GENERIC_DIR + "build/pdf.worker.js",
  1661. SRC_DIR + "pdf.worker.entry.js",
  1662. ])
  1663. .pipe(gulp.dest(TYPESTEST_DIR + "build/")),
  1664. gulp
  1665. .src(TYPES_DIR + "**/*", { base: TYPES_DIR })
  1666. .pipe(gulp.dest(TYPESTEST_DIR + "types/")),
  1667. ]);
  1668. },
  1669. function runTypesTest(done) {
  1670. exec('"node_modules/.bin/tsc" -p test/types', function (err, stdout) {
  1671. if (err) {
  1672. console.log(`Couldn't compile TypeScript test: ${stdout}`);
  1673. }
  1674. done(err);
  1675. });
  1676. }
  1677. )
  1678. );
  1679. function createBaseline(done) {
  1680. console.log();
  1681. console.log("### Creating baseline environment");
  1682. const baselineCommit = process.env.BASELINE;
  1683. if (!baselineCommit) {
  1684. done(new Error("Missing baseline commit. Specify the BASELINE variable."));
  1685. return;
  1686. }
  1687. let initializeCommand = "git fetch origin";
  1688. if (!checkDir(BASELINE_DIR)) {
  1689. mkdirp.sync(BASELINE_DIR);
  1690. initializeCommand = "git clone ../../ .";
  1691. }
  1692. const workingDirectory = path.resolve(process.cwd(), BASELINE_DIR);
  1693. exec(initializeCommand, { cwd: workingDirectory }, function (error) {
  1694. if (error) {
  1695. done(new Error("Baseline clone/fetch failed."));
  1696. return;
  1697. }
  1698. exec(
  1699. "git checkout " + baselineCommit,
  1700. { cwd: workingDirectory },
  1701. function (error2) {
  1702. if (error2) {
  1703. done(new Error("Baseline commit checkout failed."));
  1704. return;
  1705. }
  1706. console.log('Baseline commit "' + baselineCommit + '" checked out.');
  1707. done();
  1708. }
  1709. );
  1710. });
  1711. }
  1712. gulp.task(
  1713. "unittestcli",
  1714. gulp.series(setTestEnv, "lib-legacy", function runUnitTestCli(done) {
  1715. const options = [
  1716. "node_modules/jasmine/bin/jasmine",
  1717. "JASMINE_CONFIG_PATH=test/unit/clitests.json",
  1718. ];
  1719. const jasmineProcess = startNode(options, { stdio: "inherit" });
  1720. jasmineProcess.on("close", function (code) {
  1721. if (code !== 0) {
  1722. done(new Error("Unit tests failed."));
  1723. return;
  1724. }
  1725. done();
  1726. });
  1727. })
  1728. );
  1729. gulp.task("lint", function (done) {
  1730. console.log();
  1731. console.log("### Linting JS/CSS files");
  1732. // Ensure that we lint the Firefox specific *.jsm files too.
  1733. const esLintOptions = [
  1734. "node_modules/eslint/bin/eslint",
  1735. "--ext",
  1736. ".js,.jsm,.json",
  1737. ".",
  1738. "--report-unused-disable-directives",
  1739. ];
  1740. if (process.argv.includes("--fix")) {
  1741. esLintOptions.push("--fix");
  1742. }
  1743. const styleLintOptions = [
  1744. "node_modules/stylelint/bin/stylelint",
  1745. "**/*.css",
  1746. "--report-needless-disables",
  1747. ];
  1748. if (process.argv.includes("--fix")) {
  1749. styleLintOptions.push("--fix");
  1750. }
  1751. const esLintProcess = startNode(esLintOptions, { stdio: "inherit" });
  1752. esLintProcess.on("close", function (esLintCode) {
  1753. if (esLintCode !== 0) {
  1754. done(new Error("ESLint failed."));
  1755. return;
  1756. }
  1757. const styleLintProcess = startNode(styleLintOptions, { stdio: "inherit" });
  1758. styleLintProcess.on("close", function (styleLintCode) {
  1759. if (styleLintCode !== 0) {
  1760. done(new Error("Stylelint failed."));
  1761. return;
  1762. }
  1763. console.log("files checked, no errors found");
  1764. done();
  1765. });
  1766. });
  1767. });
  1768. gulp.task(
  1769. "lint-chromium",
  1770. gulp.series(
  1771. function scriptingLintChromium() {
  1772. const defines = builder.merge(DEFINES, {
  1773. CHROME: true,
  1774. SKIP_BABEL: false,
  1775. TESTING: false,
  1776. });
  1777. return buildDefaultPreferences(defines, "lint-chromium/");
  1778. },
  1779. function runLintChromium(done) {
  1780. console.log();
  1781. console.log("### Checking supplemental Chromium files");
  1782. if (
  1783. !checkChromePreferencesFile(
  1784. "extensions/chromium/preferences_schema.json",
  1785. getDefaultPreferences("lint-chromium/")
  1786. )
  1787. ) {
  1788. done(new Error("chromium/preferences_schema is not in sync."));
  1789. return;
  1790. }
  1791. done();
  1792. }
  1793. )
  1794. );
  1795. gulp.task("dev-css", function createDevCSS() {
  1796. console.log();
  1797. console.log("### Building development CSS");
  1798. const defines = builder.merge(DEFINES, { GENERIC: true, TESTING: true });
  1799. const cssDir = BUILD_DIR + "dev-css/";
  1800. return merge([
  1801. gulp.src("web/images/*", { base: "web/" }).pipe(gulp.dest(cssDir)),
  1802. preprocessCSS("web/viewer.css", defines)
  1803. .pipe(
  1804. postcss([
  1805. postcssLogical({ preserve: true }),
  1806. postcssDirPseudoClass(),
  1807. autoprefixer({ overrideBrowserslist: ["last 1 versions"] }),
  1808. ])
  1809. )
  1810. .pipe(gulp.dest(cssDir)),
  1811. ]);
  1812. });
  1813. gulp.task(
  1814. "dev-sandbox",
  1815. gulp.series(
  1816. function scriptingDevSandbox() {
  1817. const defines = builder.merge(DEFINES, { GENERIC: true, TESTING: true });
  1818. return createTemporaryScriptingBundle(defines, {
  1819. disableVersionInfo: true,
  1820. });
  1821. },
  1822. function createDevSandbox() {
  1823. console.log();
  1824. console.log("### Building development sandbox");
  1825. const defines = builder.merge(DEFINES, { GENERIC: true, TESTING: true });
  1826. const sandboxDir = BUILD_DIR + "dev-sandbox/";
  1827. rimraf.sync(sandboxDir);
  1828. return createSandboxBundle(defines, {
  1829. disableVersionInfo: true,
  1830. }).pipe(gulp.dest(sandboxDir));
  1831. }
  1832. )
  1833. );
  1834. gulp.task(
  1835. "server",
  1836. gulp.parallel(
  1837. function watchDevCSS() {
  1838. gulp.watch(
  1839. ["web/*.css", "web/images/*"],
  1840. { ignoreInitial: false },
  1841. gulp.series("dev-css")
  1842. );
  1843. },
  1844. function watchDevFitCurve() {
  1845. gulp.watch(
  1846. ["src/display/editor/*"],
  1847. { ignoreInitial: false },
  1848. gulp.series("dev-fitCurve")
  1849. );
  1850. },
  1851. function watchDevSandbox() {
  1852. gulp.watch(
  1853. [
  1854. "src/pdf.{sandbox,sandbox.external,scripting}.js",
  1855. "src/scripting_api/*.js",
  1856. "src/shared/scripting_utils.js",
  1857. "external/quickjs/*.js",
  1858. ],
  1859. { ignoreInitial: false },
  1860. gulp.series("dev-sandbox")
  1861. );
  1862. },
  1863. function createServer() {
  1864. console.log();
  1865. console.log("### Starting local server");
  1866. const WebServer = require("./test/webserver.js").WebServer;
  1867. const server = new WebServer();
  1868. server.port = 8888;
  1869. server.start();
  1870. }
  1871. )
  1872. );
  1873. gulp.task("clean", function (done) {
  1874. console.log();
  1875. console.log("### Cleaning up project builds");
  1876. rimraf(BUILD_DIR, done);
  1877. });
  1878. gulp.task("importl10n", function (done) {
  1879. const locales = require("./external/importL10n/locales.js");
  1880. console.log();
  1881. console.log("### Importing translations from mozilla-central");
  1882. if (!fs.existsSync(L10N_DIR)) {
  1883. fs.mkdirSync(L10N_DIR);
  1884. }
  1885. locales.downloadL10n(L10N_DIR, done);
  1886. });
  1887. function ghPagesPrepare() {
  1888. console.log();
  1889. console.log("### Creating web site");
  1890. rimraf.sync(GH_PAGES_DIR);
  1891. // 'vfs' because web/viewer.html needs its BOM.
  1892. return merge([
  1893. vfs
  1894. .src(GENERIC_DIR + "**/*", { base: GENERIC_DIR, stripBOM: false })
  1895. .pipe(gulp.dest(GH_PAGES_DIR)),
  1896. vfs
  1897. .src(GENERIC_LEGACY_DIR + "**/*", {
  1898. base: GENERIC_LEGACY_DIR,
  1899. stripBOM: false,
  1900. })
  1901. .pipe(gulp.dest(GH_PAGES_DIR + "legacy/")),
  1902. gulp
  1903. .src(JSDOC_BUILD_DIR + "**/*", { base: JSDOC_BUILD_DIR })
  1904. .pipe(gulp.dest(GH_PAGES_DIR + "api/draft/")),
  1905. ]);
  1906. }
  1907. gulp.task("wintersmith", function (done) {
  1908. const wintersmith = require("wintersmith");
  1909. const env = wintersmith("docs/config.json");
  1910. env.build(GH_PAGES_DIR, function (error) {
  1911. if (error) {
  1912. done(error);
  1913. return;
  1914. }
  1915. replaceInFile(
  1916. GH_PAGES_DIR + "/getting_started/index.html",
  1917. /STABLE_VERSION/g,
  1918. config.stableVersion
  1919. );
  1920. console.log("Done building with wintersmith.");
  1921. done();
  1922. });
  1923. });
  1924. function ghPagesGit(done) {
  1925. const VERSION = getVersionJSON().version;
  1926. const reason = process.env.PDFJS_UPDATE_REASON;
  1927. safeSpawnSync("git", ["init"], { cwd: GH_PAGES_DIR });
  1928. safeSpawnSync("git", ["remote", "add", "origin", REPO], {
  1929. cwd: GH_PAGES_DIR,
  1930. });
  1931. safeSpawnSync("git", ["add", "-A"], { cwd: GH_PAGES_DIR });
  1932. safeSpawnSync(
  1933. "git",
  1934. [
  1935. "commit",
  1936. "-am",
  1937. "gh-pages site created via gulpfile.js script",
  1938. "-m",
  1939. "PDF.js version " + VERSION + (reason ? " - " + reason : ""),
  1940. ],
  1941. { cwd: GH_PAGES_DIR }
  1942. );
  1943. safeSpawnSync("git", ["branch", "-m", "gh-pages"], { cwd: GH_PAGES_DIR });
  1944. console.log();
  1945. console.log("Website built in " + GH_PAGES_DIR);
  1946. done();
  1947. }
  1948. gulp.task(
  1949. "web",
  1950. gulp.series(
  1951. "generic",
  1952. "generic-legacy",
  1953. "jsdoc",
  1954. ghPagesPrepare,
  1955. "wintersmith",
  1956. ghPagesGit
  1957. )
  1958. );
  1959. function packageJson() {
  1960. const VERSION = getVersionJSON().version;
  1961. const DIST_NAME = "pdfjs-dist";
  1962. const DIST_DESCRIPTION = "Generic build of Mozilla's PDF.js library.";
  1963. const DIST_KEYWORDS = ["Mozilla", "pdf", "pdf.js"];
  1964. const DIST_HOMEPAGE = "http://mozilla.github.io/pdf.js/";
  1965. const DIST_BUGS_URL = "https://github.com/mozilla/pdf.js/issues";
  1966. const DIST_LICENSE = "Apache-2.0";
  1967. const npmManifest = {
  1968. name: DIST_NAME,
  1969. version: VERSION,
  1970. main: "build/pdf.js",
  1971. types: "types/src/pdf.d.ts",
  1972. description: DIST_DESCRIPTION,
  1973. keywords: DIST_KEYWORDS,
  1974. homepage: DIST_HOMEPAGE,
  1975. bugs: DIST_BUGS_URL,
  1976. license: DIST_LICENSE,
  1977. optionalDependencies: {
  1978. canvas: "^2.11.0",
  1979. },
  1980. dependencies: {
  1981. "web-streams-polyfill": "^3.2.1",
  1982. },
  1983. browser: {
  1984. canvas: false,
  1985. fs: false,
  1986. http: false,
  1987. https: false,
  1988. url: false,
  1989. zlib: false,
  1990. },
  1991. format: "amd", // to not allow system.js to choose 'cjs'
  1992. repository: {
  1993. type: "git",
  1994. url: DIST_REPO_URL,
  1995. },
  1996. };
  1997. return createStringSource(
  1998. "package.json",
  1999. JSON.stringify(npmManifest, null, 2)
  2000. );
  2001. }
  2002. gulp.task(
  2003. "dist-pre",
  2004. gulp.series(
  2005. "generic",
  2006. "generic-legacy",
  2007. "components",
  2008. "components-legacy",
  2009. "image_decoders",
  2010. "image_decoders-legacy",
  2011. "lib",
  2012. "minified",
  2013. "minified-legacy",
  2014. "types",
  2015. function createDist() {
  2016. console.log();
  2017. console.log("### Cloning baseline distribution");
  2018. rimraf.sync(DIST_DIR);
  2019. mkdirp.sync(DIST_DIR);
  2020. safeSpawnSync("git", ["clone", "--depth", "1", DIST_REPO_URL, DIST_DIR]);
  2021. console.log();
  2022. console.log("### Overwriting all files");
  2023. rimraf.sync(path.join(DIST_DIR, "*"));
  2024. return merge([
  2025. packageJson().pipe(gulp.dest(DIST_DIR)),
  2026. vfs
  2027. .src("external/dist/**/*", { base: "external/dist", stripBOM: false })
  2028. .pipe(gulp.dest(DIST_DIR)),
  2029. gulp.src(GENERIC_DIR + "LICENSE").pipe(gulp.dest(DIST_DIR)),
  2030. gulp
  2031. .src(GENERIC_DIR + "web/cmaps/**/*", { base: GENERIC_DIR + "web" })
  2032. .pipe(gulp.dest(DIST_DIR)),
  2033. gulp
  2034. .src(GENERIC_DIR + "web/standard_fonts/**/*", {
  2035. base: GENERIC_DIR + "web",
  2036. })
  2037. .pipe(gulp.dest(DIST_DIR)),
  2038. gulp
  2039. .src([
  2040. GENERIC_DIR + "build/{pdf,pdf.worker,pdf.sandbox}.js",
  2041. GENERIC_DIR + "build/{pdf,pdf.worker,pdf.sandbox}.js.map",
  2042. SRC_DIR + "pdf.worker.entry.js",
  2043. ])
  2044. .pipe(gulp.dest(DIST_DIR + "build/")),
  2045. gulp
  2046. .src([
  2047. GENERIC_LEGACY_DIR + "build/{pdf,pdf.worker,pdf.sandbox}.js",
  2048. GENERIC_LEGACY_DIR + "build/{pdf,pdf.worker,pdf.sandbox}.js.map",
  2049. SRC_DIR + "pdf.worker.entry.js",
  2050. ])
  2051. .pipe(gulp.dest(DIST_DIR + "legacy/build/")),
  2052. gulp
  2053. .src(MINIFIED_DIR + "build/pdf.js")
  2054. .pipe(rename("pdf.min.js"))
  2055. .pipe(gulp.dest(DIST_DIR + "build/")),
  2056. gulp
  2057. .src(MINIFIED_DIR + "build/pdf.worker.js")
  2058. .pipe(rename("pdf.worker.min.js"))
  2059. .pipe(gulp.dest(DIST_DIR + "build/")),
  2060. gulp
  2061. .src(MINIFIED_DIR + "build/pdf.sandbox.js")
  2062. .pipe(rename("pdf.sandbox.min.js"))
  2063. .pipe(gulp.dest(DIST_DIR + "build/")),
  2064. gulp
  2065. .src(MINIFIED_DIR + "image_decoders/pdf.image_decoders.js")
  2066. .pipe(rename("pdf.image_decoders.min.js"))
  2067. .pipe(gulp.dest(DIST_DIR + "image_decoders/")),
  2068. gulp
  2069. .src(MINIFIED_LEGACY_DIR + "build/pdf.js")
  2070. .pipe(rename("pdf.min.js"))
  2071. .pipe(gulp.dest(DIST_DIR + "legacy/build/")),
  2072. gulp
  2073. .src(MINIFIED_LEGACY_DIR + "build/pdf.worker.js")
  2074. .pipe(rename("pdf.worker.min.js"))
  2075. .pipe(gulp.dest(DIST_DIR + "legacy/build/")),
  2076. gulp
  2077. .src(MINIFIED_LEGACY_DIR + "build/pdf.sandbox.js")
  2078. .pipe(rename("pdf.sandbox.min.js"))
  2079. .pipe(gulp.dest(DIST_DIR + "legacy/build/")),
  2080. gulp
  2081. .src(MINIFIED_LEGACY_DIR + "image_decoders/pdf.image_decoders.js")
  2082. .pipe(rename("pdf.image_decoders.min.js"))
  2083. .pipe(gulp.dest(DIST_DIR + "legacy/image_decoders/")),
  2084. gulp
  2085. .src(COMPONENTS_DIR + "**/*", { base: COMPONENTS_DIR })
  2086. .pipe(gulp.dest(DIST_DIR + "web/")),
  2087. gulp
  2088. .src(COMPONENTS_LEGACY_DIR + "**/*", { base: COMPONENTS_LEGACY_DIR })
  2089. .pipe(gulp.dest(DIST_DIR + "legacy/web/")),
  2090. gulp
  2091. .src(IMAGE_DECODERS_DIR + "**/*", { base: IMAGE_DECODERS_DIR })
  2092. .pipe(gulp.dest(DIST_DIR + "image_decoders/")),
  2093. gulp
  2094. .src(IMAGE_DECODERS_LEGACY_DIR + "**/*", {
  2095. base: IMAGE_DECODERS_LEGACY_DIR,
  2096. })
  2097. .pipe(gulp.dest(DIST_DIR + "legacy/image_decoders/")),
  2098. gulp
  2099. .src(LIB_DIR + "**/*", { base: LIB_DIR })
  2100. .pipe(gulp.dest(DIST_DIR + "lib/")),
  2101. gulp
  2102. .src(TYPES_DIR + "**/*", { base: TYPES_DIR })
  2103. .pipe(gulp.dest(DIST_DIR + "types/")),
  2104. ]);
  2105. }
  2106. )
  2107. );
  2108. gulp.task(
  2109. "dist-install",
  2110. gulp.series("dist-pre", function createDistInstall(done) {
  2111. let distPath = DIST_DIR;
  2112. const opts = {};
  2113. const installPath = process.env.PDFJS_INSTALL_PATH;
  2114. if (installPath) {
  2115. opts.cwd = installPath;
  2116. distPath = path.relative(installPath, distPath);
  2117. }
  2118. safeSpawnSync("npm", ["install", distPath], opts);
  2119. done();
  2120. })
  2121. );
  2122. gulp.task(
  2123. "dist",
  2124. gulp.series("dist-pre", function createDist(done) {
  2125. const VERSION = getVersionJSON().version;
  2126. console.log();
  2127. console.log("### Committing changes");
  2128. let reason = process.env.PDFJS_UPDATE_REASON;
  2129. // Attempt to work-around the broken link, see https://github.com/mozilla/pdf.js/issues/10391
  2130. if (typeof reason === "string") {
  2131. const reasonParts =
  2132. /^(See )(mozilla\/pdf\.js)@tags\/(v\d+\.\d+\.\d+)\s*$/.exec(reason);
  2133. if (reasonParts) {
  2134. reason =
  2135. reasonParts[1] +
  2136. "https://github.com/" +
  2137. reasonParts[2] +
  2138. "/releases/tag/" +
  2139. reasonParts[3];
  2140. }
  2141. }
  2142. const message =
  2143. "PDF.js version " + VERSION + (reason ? " - " + reason : "");
  2144. safeSpawnSync("git", ["add", "*"], { cwd: DIST_DIR });
  2145. safeSpawnSync("git", ["commit", "-am", message], { cwd: DIST_DIR });
  2146. safeSpawnSync("git", ["tag", "-a", "v" + VERSION, "-m", message], {
  2147. cwd: DIST_DIR,
  2148. });
  2149. console.log();
  2150. console.log("Done. Push with");
  2151. console.log(
  2152. " cd " + DIST_DIR + "; git push --tags " + DIST_REPO_URL + " master"
  2153. );
  2154. console.log();
  2155. done();
  2156. })
  2157. );
  2158. gulp.task(
  2159. "mozcentralbaseline",
  2160. gulp.series(createBaseline, function createMozcentralBaseline(done) {
  2161. console.log();
  2162. console.log("### Creating mozcentral baseline environment");
  2163. // Create a mozcentral build.
  2164. rimraf.sync(BASELINE_DIR + BUILD_DIR);
  2165. const workingDirectory = path.resolve(process.cwd(), BASELINE_DIR);
  2166. safeSpawnSync("gulp", ["mozcentral"], {
  2167. env: process.env,
  2168. cwd: workingDirectory,
  2169. stdio: "inherit",
  2170. });
  2171. // Copy the mozcentral build to the mozcentral baseline directory.
  2172. rimraf.sync(MOZCENTRAL_BASELINE_DIR);
  2173. mkdirp.sync(MOZCENTRAL_BASELINE_DIR);
  2174. gulp
  2175. .src([BASELINE_DIR + BUILD_DIR + "mozcentral/**/*"])
  2176. .pipe(gulp.dest(MOZCENTRAL_BASELINE_DIR))
  2177. .on("end", function () {
  2178. // Commit the mozcentral baseline.
  2179. safeSpawnSync("git", ["init"], { cwd: MOZCENTRAL_BASELINE_DIR });
  2180. safeSpawnSync("git", ["add", "."], { cwd: MOZCENTRAL_BASELINE_DIR });
  2181. safeSpawnSync("git", ["commit", "-m", '"mozcentral baseline"'], {
  2182. cwd: MOZCENTRAL_BASELINE_DIR,
  2183. });
  2184. done();
  2185. });
  2186. })
  2187. );
  2188. gulp.task(
  2189. "mozcentraldiff",
  2190. gulp.series(
  2191. "mozcentral",
  2192. "mozcentralbaseline",
  2193. function createMozcentralDiff(done) {
  2194. console.log();
  2195. console.log("### Creating mozcentral diff");
  2196. // Create the diff between the current mozcentral build and the
  2197. // baseline mozcentral build, which both exist at this point.
  2198. // The mozcentral baseline directory is a Git repository, so we
  2199. // remove all files and copy the current mozcentral build files
  2200. // into it to create the diff.
  2201. rimraf.sync(MOZCENTRAL_BASELINE_DIR + "*");
  2202. gulp
  2203. .src([BUILD_DIR + "mozcentral/**/*"])
  2204. .pipe(gulp.dest(MOZCENTRAL_BASELINE_DIR))
  2205. .on("end", function () {
  2206. safeSpawnSync("git", ["add", "-A"], { cwd: MOZCENTRAL_BASELINE_DIR });
  2207. const diff = safeSpawnSync(
  2208. "git",
  2209. ["diff", "--binary", "--cached", "--unified=8"],
  2210. { cwd: MOZCENTRAL_BASELINE_DIR }
  2211. ).stdout;
  2212. createStringSource(MOZCENTRAL_DIFF_FILE, diff)
  2213. .pipe(gulp.dest(BUILD_DIR))
  2214. .on("end", function () {
  2215. console.log(
  2216. "Result diff can be found at " +
  2217. BUILD_DIR +
  2218. MOZCENTRAL_DIFF_FILE
  2219. );
  2220. done();
  2221. });
  2222. });
  2223. }
  2224. )
  2225. );
  2226. gulp.task("externaltest", function (done) {
  2227. console.log();
  2228. console.log("### Running test-fixtures.js");
  2229. safeSpawnSync("node", ["external/builder/test-fixtures.js"], {
  2230. stdio: "inherit",
  2231. });
  2232. console.log();
  2233. console.log("### Running test-fixtures_esprima.js");
  2234. safeSpawnSync("node", ["external/builder/test-fixtures_esprima.js"], {
  2235. stdio: "inherit",
  2236. });
  2237. done();
  2238. });
  2239. gulp.task(
  2240. "ci-test",
  2241. gulp.series(gulp.parallel("lint", "externaltest", "unittestcli"), "typestest")
  2242. );