modules/viewerpreferences.js

/**
 * @license
 * jsPDF viewerPreferences Plugin
 * @author Aras Abbasi (github.com/arasabbasi)
 * Licensed under the MIT License.
 * http://opensource.org/licenses/mit-license
 */

import { jsPDF } from "../jspdf.js";

/**
 * Adds the ability to set ViewerPreferences and by thus
 * controlling the way the document is to be presented on the
 * screen or in print.
 * @name viewerpreferences
 * @module
 */
(function(jsPDFAPI) {
  "use strict";
  /**
   * Set the ViewerPreferences of the generated PDF
   *
   * @name viewerPreferences
   * @function
   * @public
   * @param {Object} options Array with the ViewerPreferences<br />
   * Example: doc.viewerPreferences({"FitWindow":true});<br />
   * <br />
   * You can set following preferences:<br />
   * <br/>
   * <b>HideToolbar</b> <i>(boolean)</i><br />
   * Default value: false<br />
   * <br />
   * <b>HideMenubar</b> <i>(boolean)</i><br />
   * Default value: false.<br />
   * <br />
   * <b>HideWindowUI</b> <i>(boolean)</i><br />
   * Default value: false.<br />
   * <br />
   * <b>FitWindow</b> <i>(boolean)</i><br />
   * Default value: false.<br />
   * <br />
   * <b>CenterWindow</b> <i>(boolean)</i><br />
   * Default value: false<br />
   * <br />
   * <b>DisplayDocTitle</b> <i>(boolean)</i><br />
   * Default value: false.<br />
   * <br />
   * <b>NonFullScreenPageMode</b> <i>(string)</i><br />
   * Possible values: UseNone, UseOutlines, UseThumbs, UseOC<br />
   * Default value: UseNone<br/>
   * <br />
   * <b>Direction</b> <i>(string)</i><br />
   * Possible values: L2R, R2L<br />
   * Default value: L2R.<br />
   * <br />
   * <b>ViewArea</b> <i>(string)</i><br />
   * Possible values: MediaBox, CropBox, TrimBox, BleedBox, ArtBox<br />
   * Default value: CropBox.<br />
   * <br />
   * <b>ViewClip</b> <i>(string)</i><br />
   * Possible values: MediaBox, CropBox, TrimBox, BleedBox, ArtBox<br />
   * Default value: CropBox<br />
   * <br />
   * <b>PrintArea</b> <i>(string)</i><br />
   * Possible values: MediaBox, CropBox, TrimBox, BleedBox, ArtBox<br />
   * Default value: CropBox<br />
   * <br />
   * <b>PrintClip</b> <i>(string)</i><br />
   * Possible values: MediaBox, CropBox, TrimBox, BleedBox, ArtBox<br />
   * Default value: CropBox.<br />
   * <br />
   * <b>PrintScaling</b> <i>(string)</i><br />
   * Possible values: AppDefault, None<br />
   * Default value: AppDefault.<br />
   * <br />
   * <b>Duplex</b> <i>(string)</i><br />
   * Possible values: Simplex, DuplexFlipLongEdge, DuplexFlipShortEdge
   * Default value: none<br />
   * <br />
   * <b>PickTrayByPDFSize</b> <i>(boolean)</i><br />
   * Default value: false<br />
   * <br />
   * <b>PrintPageRange</b> <i>(Array)</i><br />
   * Example: [[1,5], [7,9]]<br />
   * Default value: as defined by PDF viewer application<br />
   * <br />
   * <b>NumCopies</b> <i>(Number)</i><br />
   * Possible values: 1, 2, 3, 4, 5<br />
   * Default value: 1<br />
   * <br />
   * For more information see the PDF Reference, sixth edition on Page 577
   * @param {boolean} doReset True to reset the settings
   * @function
   * @returns jsPDF jsPDF-instance
   * @example
   * var doc = new jsPDF()
   * doc.text('This is a test', 10, 10)
   * doc.viewerPreferences({'FitWindow': true}, true)
   * doc.save("viewerPreferences.pdf")
   *
   * // Example printing 10 copies, using cropbox, and hiding UI.
   * doc.viewerPreferences({
   *   'HideWindowUI': true,
   *   'PrintArea': 'CropBox',
   *   'NumCopies': 10
   * })
   */
  jsPDFAPI.viewerPreferences = function(options, doReset) {
    options = options || {};
    doReset = doReset || false;

    var configuration;
    var configurationTemplate = {
      HideToolbar: {
        defaultValue: false,
        value: false,
        type: "boolean",
        explicitSet: false,
        valueSet: [true, false],
        pdfVersion: 1.3
      },
      HideMenubar: {
        defaultValue: false,
        value: false,
        type: "boolean",
        explicitSet: false,
        valueSet: [true, false],
        pdfVersion: 1.3
      },
      HideWindowUI: {
        defaultValue: false,
        value: false,
        type: "boolean",
        explicitSet: false,
        valueSet: [true, false],
        pdfVersion: 1.3
      },
      FitWindow: {
        defaultValue: false,
        value: false,
        type: "boolean",
        explicitSet: false,
        valueSet: [true, false],
        pdfVersion: 1.3
      },
      CenterWindow: {
        defaultValue: false,
        value: false,
        type: "boolean",
        explicitSet: false,
        valueSet: [true, false],
        pdfVersion: 1.3
      },
      DisplayDocTitle: {
        defaultValue: false,
        value: false,
        type: "boolean",
        explicitSet: false,
        valueSet: [true, false],
        pdfVersion: 1.4
      },
      NonFullScreenPageMode: {
        defaultValue: "UseNone",
        value: "UseNone",
        type: "name",
        explicitSet: false,
        valueSet: ["UseNone", "UseOutlines", "UseThumbs", "UseOC"],
        pdfVersion: 1.3
      },
      Direction: {
        defaultValue: "L2R",
        value: "L2R",
        type: "name",
        explicitSet: false,
        valueSet: ["L2R", "R2L"],
        pdfVersion: 1.3
      },
      ViewArea: {
        defaultValue: "CropBox",
        value: "CropBox",
        type: "name",
        explicitSet: false,
        valueSet: ["MediaBox", "CropBox", "TrimBox", "BleedBox", "ArtBox"],
        pdfVersion: 1.4
      },
      ViewClip: {
        defaultValue: "CropBox",
        value: "CropBox",
        type: "name",
        explicitSet: false,
        valueSet: ["MediaBox", "CropBox", "TrimBox", "BleedBox", "ArtBox"],
        pdfVersion: 1.4
      },
      PrintArea: {
        defaultValue: "CropBox",
        value: "CropBox",
        type: "name",
        explicitSet: false,
        valueSet: ["MediaBox", "CropBox", "TrimBox", "BleedBox", "ArtBox"],
        pdfVersion: 1.4
      },
      PrintClip: {
        defaultValue: "CropBox",
        value: "CropBox",
        type: "name",
        explicitSet: false,
        valueSet: ["MediaBox", "CropBox", "TrimBox", "BleedBox", "ArtBox"],
        pdfVersion: 1.4
      },
      PrintScaling: {
        defaultValue: "AppDefault",
        value: "AppDefault",
        type: "name",
        explicitSet: false,
        valueSet: ["AppDefault", "None"],
        pdfVersion: 1.6
      },
      Duplex: {
        defaultValue: "",
        value: "none",
        type: "name",
        explicitSet: false,
        valueSet: [
          "Simplex",
          "DuplexFlipShortEdge",
          "DuplexFlipLongEdge",
          "none"
        ],
        pdfVersion: 1.7
      },
      PickTrayByPDFSize: {
        defaultValue: false,
        value: false,
        type: "boolean",
        explicitSet: false,
        valueSet: [true, false],
        pdfVersion: 1.7
      },
      PrintPageRange: {
        defaultValue: "",
        value: "",
        type: "array",
        explicitSet: false,
        valueSet: null,
        pdfVersion: 1.7
      },
      NumCopies: {
        defaultValue: 1,
        value: 1,
        type: "integer",
        explicitSet: false,
        valueSet: null,
        pdfVersion: 1.7
      }
    };

    var configurationKeys = Object.keys(configurationTemplate);

    var rangeArray = [];
    var i = 0;
    var j = 0;
    var k = 0;
    var isValid;

    var method;
    var value;

    function arrayContainsElement(array, element) {
      var iterator;
      var result = false;

      for (iterator = 0; iterator < array.length; iterator += 1) {
        if (array[iterator] === element) {
          result = true;
        }
      }
      return result;
    }

    if (this.internal.viewerpreferences === undefined) {
      this.internal.viewerpreferences = {};
      this.internal.viewerpreferences.configuration = JSON.parse(
        JSON.stringify(configurationTemplate)
      );
      this.internal.viewerpreferences.isSubscribed = false;
    }
    configuration = this.internal.viewerpreferences.configuration;

    if (options === "reset" || doReset === true) {
      var len = configurationKeys.length;

      for (k = 0; k < len; k += 1) {
        configuration[configurationKeys[k]].value =
          configuration[configurationKeys[k]].defaultValue;
        configuration[configurationKeys[k]].explicitSet = false;
      }
    }

    if (typeof options === "object") {
      for (method in options) {
        value = options[method];
        if (
          arrayContainsElement(configurationKeys, method) &&
          value !== undefined
        ) {
          if (
            configuration[method].type === "boolean" &&
            typeof value === "boolean"
          ) {
            configuration[method].value = value;
          } else if (
            configuration[method].type === "name" &&
            arrayContainsElement(configuration[method].valueSet, value)
          ) {
            configuration[method].value = value;
          } else if (
            configuration[method].type === "integer" &&
            Number.isInteger(value)
          ) {
            configuration[method].value = value;
          } else if (configuration[method].type === "array") {
            for (i = 0; i < value.length; i += 1) {
              isValid = true;
              if (value[i].length === 1 && typeof value[i][0] === "number") {
                rangeArray.push(String(value[i] - 1));
              } else if (value[i].length > 1) {
                for (j = 0; j < value[i].length; j += 1) {
                  if (typeof value[i][j] !== "number") {
                    isValid = false;
                  }
                }
                if (isValid === true) {
                  rangeArray.push([value[i][0] - 1, value[i][1] - 1].join(" "));
                }
              }
            }
            configuration[method].value = "[" + rangeArray.join(" ") + "]";
          } else {
            configuration[method].value = configuration[method].defaultValue;
          }

          configuration[method].explicitSet = true;
        }
      }
    }

    if (this.internal.viewerpreferences.isSubscribed === false) {
      this.internal.events.subscribe("putCatalog", function() {
        var pdfDict = [];
        var vPref;
        for (vPref in configuration) {
          if (configuration[vPref].explicitSet === true) {
            if (configuration[vPref].type === "name") {
              pdfDict.push("/" + vPref + " /" + configuration[vPref].value);
            } else {
              pdfDict.push("/" + vPref + " " + configuration[vPref].value);
            }
          }
        }
        if (pdfDict.length !== 0) {
          this.internal.write(
            "/ViewerPreferences\n<<\n" + pdfDict.join("\n") + "\n>>"
          );
        }
      });
      this.internal.viewerpreferences.isSubscribed = true;
    }

    this.internal.viewerpreferences.configuration = configuration;
    return this;
  };
})(jsPDF.API);