protected function computeEncryptionKey($password = '')
    {
        $revision = $this->revision;

        // TODO: The password string is generated from OS codepage characters by first
        // converting the string to PDFDocEncoding. If the input is Unicode, first convert
        // to a codepage encoding, and then to PDFDocEncoding for backward compatibility.


        // Algorithm 2: Computing an encryption key
        // a) Pad or truncate the password string to exactly 32 bytes.
        // b) Initialize the MD5 hash function and pass the result of step (a) as input to this function.
        $s = substr($password . $this->padding, 0, 32);

        // c) Pass the value of the encryption dictionary’s O entry to the MD5 hash function.
        //    ("Algorithm 3: Computing the encryption dictionary’s O (owner password) value" shows how the O value is computed.)
        $s .= $this->oValue;

        // d) Convert the integer value of the P entry to a 32-bit unsigned binary number and pass these
        //    bytes to the MD5 hash function, low-order byte first.
        $pValue = (int)(float)$this->pValue;
        $s .= pack("V", $pValue);

        // e) Pass the first element of the file’s file identifier array (the value of the ID
        //    entry in the document’s trailer dictionary; see Table 15) to the MD5 hash function.
        $s .= $this->fileIdentifier;

        // f) (Security handlers of revision 4 or greater) If document metadata is not
        //    being encrypted, pass 4 bytes with the value 0xFFFFFFFF to the MD5 hash function.
        // ...

        // g) Finish the hash.
        $s = md5($s, true);

        // h) (Security handlers of revision 3 or greater) Do the following 50 times:
        //    Take the output from the previous MD5 hash and pass the first n bytes of
        //    the output as input into a new MD5 hash, where n is the number of bytes
        //    of the encryption key as defined by the value of the encryption dictionary’s
        //    Length entry.
        if ($revision >= 3) {
            for ($i = 0; $i < 50; $i++)
                $s = md5(substr($s, 0, $this->keyLength), true);
        }

        // i) Set the encryption key to the first n bytes of the output from the final
        //    MD5 hash, where n shall always be 5 for security handlers of revision 2 but,
        //    for security handlers of revision 3 or greater, shall depend on the value of
        //    the encryption dictionary’s Length entry.

        return substr($s, 0, $this->keyLength); // key length is calculated automatically if it is missing (5)
    }