⚝
One Hat Cyber Team
⚝
Your IP:
192.168.31.6
Server IP:
192.168.1.11
Server:
Linux pams 6.1.0-29-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.123-1 (2025-01-02) x86_64
PHP Version:
8.2.28
Buat File
|
Buat Folder
Upload File
Eksekusi
Dir :
~
/
var
/
www
/
pusaka
/
public
/
assets
/
pdfjs
/
src
/
core
/
Edit File: type1_parser.js
/* Copyright 2012 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { getEncoding } from "./encodings.js"; import { isWhiteSpace } from "./core_utils.js"; import { Stream } from "./stream.js"; import { warn } from "../shared/util.js"; // Hinting is currently disabled due to unknown problems on windows // in tracemonkey and various other pdfs with type1 fonts. const HINTING_ENABLED = false; const COMMAND_MAP = { hstem: [1], vstem: [3], vmoveto: [4], rlineto: [5], hlineto: [6], vlineto: [7], rrcurveto: [8], callsubr: [10], flex: [12, 35], drop: [12, 18], endchar: [14], rmoveto: [21], hmoveto: [22], vhcurveto: [30], hvcurveto: [31], }; /** * CharStrings are encoded following the CharString Encoding sequence * describe in Chapter 6 of the "Adobe Type1 Font Format" specification. * The value in a byte indicates a command, a number, or subsequent bytes * that are to be interpreted in a special way. * * CharString Number Encoding: * A CharString byte containing the values from 32 through 255 inclusive * indicate an integer. These values are decoded in four ranges. * * 1. A CharString byte containing a value, v, between 32 and 246 inclusive, * indicate the integer v - 139. Thus, the integer values from -107 through * 107 inclusive may be encoded in single byte. * * 2. A CharString byte containing a value, v, between 247 and 250 inclusive, * indicates an integer involving the next byte, w, according to the formula: * [(v - 247) x 256] + w + 108 * * 3. A CharString byte containing a value, v, between 251 and 254 inclusive, * indicates an integer involving the next byte, w, according to the formula: * -[(v - 251) * 256] - w - 108 * * 4. A CharString containing the value 255 indicates that the next 4 bytes * are a two complement signed integer. The first of these bytes contains the * highest order bits, the second byte contains the next higher order bits * and the fourth byte contain the lowest order bits. * * * CharString Command Encoding: * CharStrings commands are encoded in 1 or 2 bytes. * * Single byte commands are encoded in 1 byte that contains a value between * 0 and 31 inclusive. * If a command byte contains the value 12, then the value in the next byte * indicates a command. This "escape" mechanism allows many extra commands * to be encoded and this encoding technique helps to minimize the length of * the charStrings. */ class Type1CharString { constructor() { this.width = 0; this.lsb = 0; this.flexing = false; this.output = []; this.stack = []; } convert(encoded, subrs, seacAnalysisEnabled) { const count = encoded.length; let error = false; let wx, sbx, subrNumber; for (let i = 0; i < count; i++) { let value = encoded[i]; if (value < 32) { if (value === 12) { value = (value << 8) + encoded[++i]; } switch (value) { case 1: // hstem if (!HINTING_ENABLED) { this.stack = []; break; } error = this.executeCommand(2, COMMAND_MAP.hstem); break; case 3: // vstem if (!HINTING_ENABLED) { this.stack = []; break; } error = this.executeCommand(2, COMMAND_MAP.vstem); break; case 4: // vmoveto if (this.flexing) { if (this.stack.length < 1) { error = true; break; } // Add the dx for flex and but also swap the values so they are // the right order. const dy = this.stack.pop(); this.stack.push(0, dy); break; } error = this.executeCommand(1, COMMAND_MAP.vmoveto); break; case 5: // rlineto error = this.executeCommand(2, COMMAND_MAP.rlineto); break; case 6: // hlineto error = this.executeCommand(1, COMMAND_MAP.hlineto); break; case 7: // vlineto error = this.executeCommand(1, COMMAND_MAP.vlineto); break; case 8: // rrcurveto error = this.executeCommand(6, COMMAND_MAP.rrcurveto); break; case 9: // closepath // closepath is a Type1 command that does not take argument and is // useless in Type2 and it can simply be ignored. this.stack = []; break; case 10: // callsubr if (this.stack.length < 1) { error = true; break; } subrNumber = this.stack.pop(); if (!subrs[subrNumber]) { error = true; break; } error = this.convert(subrs[subrNumber], subrs, seacAnalysisEnabled); break; case 11: // return return error; case 13: // hsbw if (this.stack.length < 2) { error = true; break; } // To convert to type2 we have to move the width value to the // first part of the charstring and then use hmoveto with lsb. wx = this.stack.pop(); sbx = this.stack.pop(); this.lsb = sbx; this.width = wx; this.stack.push(wx, sbx); error = this.executeCommand(2, COMMAND_MAP.hmoveto); break; case 14: // endchar this.output.push(COMMAND_MAP.endchar[0]); break; case 21: // rmoveto if (this.flexing) { break; } error = this.executeCommand(2, COMMAND_MAP.rmoveto); break; case 22: // hmoveto if (this.flexing) { // Add the dy for flex. this.stack.push(0); break; } error = this.executeCommand(1, COMMAND_MAP.hmoveto); break; case 30: // vhcurveto error = this.executeCommand(4, COMMAND_MAP.vhcurveto); break; case 31: // hvcurveto error = this.executeCommand(4, COMMAND_MAP.hvcurveto); break; case (12 << 8) + 0: // dotsection // dotsection is a Type1 command to specify some hinting feature // for dots that do not take a parameter and it can safely be // ignored for Type2. this.stack = []; break; case (12 << 8) + 1: // vstem3 if (!HINTING_ENABLED) { this.stack = []; break; } // [vh]stem3 are Type1 only and Type2 supports [vh]stem with // multiple parameters, so instead of returning [vh]stem3 take a // shortcut and return [vhstem] instead. error = this.executeCommand(2, COMMAND_MAP.vstem); break; case (12 << 8) + 2: // hstem3 if (!HINTING_ENABLED) { this.stack = []; break; } // See vstem3. error = this.executeCommand(2, COMMAND_MAP.hstem); break; case (12 << 8) + 6: // seac // seac is like type 2's special endchar but it doesn't use the // first argument asb, so remove it. if (seacAnalysisEnabled) { const asb = this.stack.at(-5); this.seac = this.stack.splice(-4, 4); this.seac[0] += this.lsb - asb; error = this.executeCommand(0, COMMAND_MAP.endchar); } else { error = this.executeCommand(4, COMMAND_MAP.endchar); } break; case (12 << 8) + 7: // sbw if (this.stack.length < 4) { error = true; break; } // To convert to type2 we have to move the width value to the // first part of the charstring and then use rmoveto with // (dx, dy). The height argument will not be used for vmtx and // vhea tables reconstruction -- ignoring it. this.stack.pop(); // wy wx = this.stack.pop(); const sby = this.stack.pop(); sbx = this.stack.pop(); this.lsb = sbx; this.width = wx; this.stack.push(wx, sbx, sby); error = this.executeCommand(3, COMMAND_MAP.rmoveto); break; case (12 << 8) + 12: // div if (this.stack.length < 2) { error = true; break; } const num2 = this.stack.pop(); const num1 = this.stack.pop(); this.stack.push(num1 / num2); break; case (12 << 8) + 16: // callothersubr if (this.stack.length < 2) { error = true; break; } subrNumber = this.stack.pop(); const numArgs = this.stack.pop(); if (subrNumber === 0 && numArgs === 3) { const flexArgs = this.stack.splice(-17, 17); this.stack.push( flexArgs[2] + flexArgs[0], // bcp1x + rpx flexArgs[3] + flexArgs[1], // bcp1y + rpy flexArgs[4], // bcp2x flexArgs[5], // bcp2y flexArgs[6], // p2x flexArgs[7], // p2y flexArgs[8], // bcp3x flexArgs[9], // bcp3y flexArgs[10], // bcp4x flexArgs[11], // bcp4y flexArgs[12], // p3x flexArgs[13], // p3y flexArgs[14] // flexDepth // 15 = finalx unused by flex // 16 = finaly unused by flex ); error = this.executeCommand(13, COMMAND_MAP.flex, true); this.flexing = false; this.stack.push(flexArgs[15], flexArgs[16]); } else if (subrNumber === 1 && numArgs === 0) { this.flexing = true; } break; case (12 << 8) + 17: // pop // Ignore this since it is only used with othersubr. break; case (12 << 8) + 33: // setcurrentpoint // Ignore for now. this.stack = []; break; default: warn('Unknown type 1 charstring command of "' + value + '"'); break; } if (error) { break; } continue; } else if (value <= 246) { value -= 139; } else if (value <= 250) { value = (value - 247) * 256 + encoded[++i] + 108; } else if (value <= 254) { value = -((value - 251) * 256) - encoded[++i] - 108; } else { value = ((encoded[++i] & 0xff) << 24) | ((encoded[++i] & 0xff) << 16) | ((encoded[++i] & 0xff) << 8) | ((encoded[++i] & 0xff) << 0); } this.stack.push(value); } return error; } executeCommand(howManyArgs, command, keepStack) { const stackLength = this.stack.length; if (howManyArgs > stackLength) { return true; } const start = stackLength - howManyArgs; for (let i = start; i < stackLength; i++) { let value = this.stack[i]; if (Number.isInteger(value)) { this.output.push(28, (value >> 8) & 0xff, value & 0xff); } else { // fixed point value = (65536 * value) | 0; this.output.push( 255, (value >> 24) & 0xff, (value >> 16) & 0xff, (value >> 8) & 0xff, value & 0xff ); } } this.output.push(...command); if (keepStack) { this.stack.splice(start, howManyArgs); } else { this.stack.length = 0; } return false; } } /** * Decrypt a Sequence of Ciphertext Bytes to Produce the Original Sequence * of Plaintext Bytes. The function takes a key as a parameter which can be * for decrypting the eexec block or for decoding charStrings. */ const EEXEC_ENCRYPT_KEY = 55665; const CHAR_STRS_ENCRYPT_KEY = 4330; function isHexDigit(code) { return ( (code >= 48 && code <= 57) || // '0'-'9' (code >= 65 && code <= 70) || // 'A'-'F' (code >= 97 && code <= 102) // 'a'-'f' ); } function decrypt(data, key, discardNumber) { if (discardNumber >= data.length) { return new Uint8Array(0); } const c1 = 52845, c2 = 22719; let r = key | 0, i, j; for (i = 0; i < discardNumber; i++) { r = ((data[i] + r) * c1 + c2) & ((1 << 16) - 1); } const count = data.length - discardNumber; const decrypted = new Uint8Array(count); for (i = discardNumber, j = 0; j < count; i++, j++) { const value = data[i]; decrypted[j] = value ^ (r >> 8); r = ((value + r) * c1 + c2) & ((1 << 16) - 1); } return decrypted; } function decryptAscii(data, key, discardNumber) { const c1 = 52845, c2 = 22719; let r = key | 0; const count = data.length, maybeLength = count >>> 1; const decrypted = new Uint8Array(maybeLength); let i, j; for (i = 0, j = 0; i < count; i++) { const digit1 = data[i]; if (!isHexDigit(digit1)) { continue; } i++; let digit2; while (i < count && !isHexDigit((digit2 = data[i]))) { i++; } if (i < count) { const value = parseInt(String.fromCharCode(digit1, digit2), 16); decrypted[j++] = value ^ (r >> 8); r = ((value + r) * c1 + c2) & ((1 << 16) - 1); } } return decrypted.slice(discardNumber, j); } function isSpecial(c) { return ( c === /* '/' = */ 0x2f || c === /* '[' = */ 0x5b || c === /* ']' = */ 0x5d || c === /* '{' = */ 0x7b || c === /* '}' = */ 0x7d || c === /* '(' = */ 0x28 || c === /* ')' = */ 0x29 ); } /** * Type1Parser encapsulate the needed code for parsing a Type1 font program. * Some of its logic depends on the Type2 charstrings structure. * NOTE: This doesn't really parse the font since that would require evaluation * of PostScript, but it is possible in most cases to extract what we need * without a full parse. */ class Type1Parser { constructor(stream, encrypted, seacAnalysisEnabled) { if (encrypted) { const data = stream.getBytes(); const isBinary = !( (isHexDigit(data[0]) || isWhiteSpace(data[0])) && isHexDigit(data[1]) && isHexDigit(data[2]) && isHexDigit(data[3]) && isHexDigit(data[4]) && isHexDigit(data[5]) && isHexDigit(data[6]) && isHexDigit(data[7]) ); stream = new Stream( isBinary ? decrypt(data, EEXEC_ENCRYPT_KEY, 4) : decryptAscii(data, EEXEC_ENCRYPT_KEY, 4) ); } this.seacAnalysisEnabled = !!seacAnalysisEnabled; this.stream = stream; this.nextChar(); } readNumberArray() { this.getToken(); // read '[' or '{' (arrays can start with either) const array = []; while (true) { const token = this.getToken(); if (token === null || token === "]" || token === "}") { break; } array.push(parseFloat(token || 0)); } return array; } readNumber() { const token = this.getToken(); return parseFloat(token || 0); } readInt() { // Use '| 0' to prevent setting a double into length such as the double // does not flow into the loop variable. const token = this.getToken(); return parseInt(token || 0, 10) | 0; } readBoolean() { const token = this.getToken(); // Use 1 and 0 since that's what type2 charstrings use. return token === "true" ? 1 : 0; } nextChar() { return (this.currentChar = this.stream.getByte()); } prevChar() { this.stream.skip(-2); return (this.currentChar = this.stream.getByte()); } getToken() { // Eat whitespace and comments. let comment = false; let ch = this.currentChar; while (true) { if (ch === -1) { return null; } if (comment) { if (ch === 0x0a || ch === 0x0d) { comment = false; } } else if (ch === /* '%' = */ 0x25) { comment = true; } else if (!isWhiteSpace(ch)) { break; } ch = this.nextChar(); } if (isSpecial(ch)) { this.nextChar(); return String.fromCharCode(ch); } let token = ""; do { token += String.fromCharCode(ch); ch = this.nextChar(); } while (ch >= 0 && !isWhiteSpace(ch) && !isSpecial(ch)); return token; } readCharStrings(bytes, lenIV) { if (lenIV === -1) { // This isn't in the spec, but Adobe's tx program handles -1 // as plain text. return bytes; } return decrypt(bytes, CHAR_STRS_ENCRYPT_KEY, lenIV); } /* * Returns an object containing a Subrs array and a CharStrings * array extracted from and eexec encrypted block of data */ extractFontProgram(properties) { const stream = this.stream; const subrs = [], charstrings = []; const privateData = Object.create(null); privateData.lenIV = 4; const program = { subrs: [], charstrings: [], properties: { privateData, }, }; let token, length, data, lenIV; while ((token = this.getToken()) !== null) { if (token !== "/") { continue; } token = this.getToken(); switch (token) { case "CharStrings": // The number immediately following CharStrings must be greater or // equal to the number of CharStrings. this.getToken(); this.getToken(); // read in 'dict' this.getToken(); // read in 'dup' this.getToken(); // read in 'begin' while (true) { token = this.getToken(); if (token === null || token === "end") { break; } if (token !== "/") { continue; } const glyph = this.getToken(); length = this.readInt(); this.getToken(); // read in 'RD' or '-|' data = length > 0 ? stream.getBytes(length) : new Uint8Array(0); lenIV = program.properties.privateData.lenIV; const encoded = this.readCharStrings(data, lenIV); this.nextChar(); token = this.getToken(); // read in 'ND' or '|-' if (token === "noaccess") { this.getToken(); // read in 'def' } else if (token === "/") { // The expected 'ND' or '|-' token is missing, avoid swallowing // the start of the next glyph (fixes issue14462_reduced.pdf). this.prevChar(); } charstrings.push({ glyph, encoded, }); } break; case "Subrs": this.readInt(); // num this.getToken(); // read in 'array' while (this.getToken() === "dup") { const index = this.readInt(); length = this.readInt(); this.getToken(); // read in 'RD' or '-|' data = length > 0 ? stream.getBytes(length) : new Uint8Array(0); lenIV = program.properties.privateData.lenIV; const encoded = this.readCharStrings(data, lenIV); this.nextChar(); token = this.getToken(); // read in 'NP' or '|' if (token === "noaccess") { this.getToken(); // read in 'put' } subrs[index] = encoded; } break; case "BlueValues": case "OtherBlues": case "FamilyBlues": case "FamilyOtherBlues": const blueArray = this.readNumberArray(); // *Blue* values may contain invalid data: disables reading of // those values when hinting is disabled. if ( blueArray.length > 0 && blueArray.length % 2 === 0 && HINTING_ENABLED ) { program.properties.privateData[token] = blueArray; } break; case "StemSnapH": case "StemSnapV": program.properties.privateData[token] = this.readNumberArray(); break; case "StdHW": case "StdVW": program.properties.privateData[token] = this.readNumberArray()[0]; break; case "BlueShift": case "lenIV": case "BlueFuzz": case "BlueScale": case "LanguageGroup": program.properties.privateData[token] = this.readNumber(); break; case "ExpansionFactor": // Firefox doesn't render correctly a font with a null factor on // Windows (see issue 15289), hence we just reset it to its default // value (0.06). program.properties.privateData[token] = this.readNumber() || 0.06; break; case "ForceBold": program.properties.privateData[token] = this.readBoolean(); break; } } for (const { encoded, glyph } of charstrings) { const charString = new Type1CharString(); const error = charString.convert( encoded, subrs, this.seacAnalysisEnabled ); let output = charString.output; if (error) { // It seems when FreeType encounters an error while evaluating a glyph // that it completely ignores the glyph so we'll mimic that behaviour // here and put an endchar to make the validator happy. output = [14]; } const charStringObject = { glyphName: glyph, charstring: output, width: charString.width, lsb: charString.lsb, seac: charString.seac, }; if (glyph === ".notdef") { // Make sure .notdef is at index zero (issue #11477). program.charstrings.unshift(charStringObject); } else { program.charstrings.push(charStringObject); } // Attempt to replace missing widths, from the font dictionary /Widths // entry, with ones from the font data (fixes issue11150_reduced.pdf). if (properties.builtInEncoding) { const index = properties.builtInEncoding.indexOf(glyph); if ( index > -1 && properties.widths[index] === undefined && index >= properties.firstChar && index <= properties.lastChar ) { properties.widths[index] = charString.width; } } } return program; } extractFontHeader(properties) { let token; while ((token = this.getToken()) !== null) { if (token !== "/") { continue; } token = this.getToken(); switch (token) { case "FontMatrix": const matrix = this.readNumberArray(); properties.fontMatrix = matrix; break; case "Encoding": const encodingArg = this.getToken(); let encoding; if (!/^\d+$/.test(encodingArg)) { // encoding name is specified encoding = getEncoding(encodingArg); } else { encoding = []; const size = parseInt(encodingArg, 10) | 0; this.getToken(); // read in 'array' for (let j = 0; j < size; j++) { token = this.getToken(); // skipping till first dup or def (e.g. ignoring for statement) while (token !== "dup" && token !== "def") { token = this.getToken(); if (token === null) { return; // invalid header } } if (token === "def") { break; // read all array data } const index = this.readInt(); this.getToken(); // read in '/' const glyph = this.getToken(); encoding[index] = glyph; this.getToken(); // read the in 'put' } } properties.builtInEncoding = encoding; break; case "FontBBox": const fontBBox = this.readNumberArray(); // adjusting ascent/descent properties.ascent = Math.max(fontBBox[3], fontBBox[1]); properties.descent = Math.min(fontBBox[1], fontBBox[3]); properties.ascentScaled = true; break; } } } } export { Type1Parser };
Simpan