encrypt.js

const hummus = require('hummus');
const fs = require('fs');


/**
 * Encryption user access permissions
 *
 * This function supplies the numeric value for the encrypt function's 'userProtectionFlag'
 * option. When no argument is given, the default 'print' value is used.
 *
 * @name permission
 * @function
 * @memberof Recipe
 * @param {string} flags from the list print, modify, copy, edit, fillform, extract, assemble, and printbest
 * More than one may be specified by using a comma to separate the names in the input string.
 */
exports.permission = function permission(flags='print') {

    // https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/PDF32000_2008.pdf

    const userAccessPermissions = {  // see table on page 61 of above document
        print    : 1<<2,   // allow printing
        modify   : 1<<3,   // allow template creation, signing, filling form fields
        copy     : 1<<4,   // allow content copying and copying for accessibility
        edit     : 1<<5,   // allow commenting
        fillform : 1<<8,   // allow filling of form fields
        extract  : 1<<9,   // allow content copying for accessibility
        assemble : 1<<10,  // unused
        printbest: 1<<11   // allow high resolution printing when 'print' is allowed
    };

    const perms = flags.split(',').map((x)=>{return x.trim();});
    let access = 0;
    perms.forEach((perm) => {
        if (!userAccessPermissions[perm]) {
            throw new Error(`Unknown user access permission (${perm})`);
        }
        access += userAccessPermissions[perm];
    });

    return access;
};

exports._getEncryptOptions = function _getEncryptOptions(options, addPermissions=true) {
    const encryptOptions = {};

    const password = options.password || options.ownerPassword;
    if (password) {
        encryptOptions.password = password;
        encryptOptions.ownerPassword = password;
    }

    if (options.userPassword) {
        encryptOptions.userPassword = options.userPassword;
        if (!encryptOptions.password) {
            encryptOptions.password = options.userPassword;
        }
    }

    if (addPermissions) {
        if (options.userProtectionFlag) {
            encryptOptions.userProtectionFlag = options.userProtectionFlag;
        }
    }

    // Only attach encryption mechanism when attributes
    // have been explicitly given in the incoming options.
    if (Object.keys(encryptOptions) > 0 && !encryptOptions.userPassword) {
        encryptOptions.userPassword = '';
        if (!encryptOptions.userProtectionFlag) {
            encryptOptions.userProtectionFlag = this.permission();
        }
    }

    return encryptOptions;
};

/**
 * Encrypt the pdf
 * @name encrypt
 * @function
 * @memberof Recipe
 * @param {Object} options - The options
 * @param {string} [options.password] - The permission password.
 * @param {string} [options.ownerPassword] - The password for editing.
 * @param {string} [options.userPassword] - The password for viewing & encryption.
 * @param {number} [options.userProtectionFlag] - The flag for the security level.
 */
exports.encrypt = function encrypt(options = {}) {
    this.needToEncrypt = true;
    this.encryption_ = this._getEncryptOptions(options);

    return this;
};

// http://pdfhummus.com/post/147451287581/hummus-1058-and-pdf-writer-updates-encryption
exports._encrypt = function _encrypt() {
    if (!this.encryption_) {
        return;
    }

    const tmp = this.output + '.tmp.pdf';
    fs.renameSync(this.output, tmp);
    hummus.recrypt(tmp, this.output, this.encryption_);
    fs.unlinkSync(tmp);
};