123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599 |
- /*
- * Copyright (c) 2022, Julian Offenhäuser <offenhaeuser@protonmail.com>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
- #include <LibPDF/CommonNames.h>
- #include <LibPDF/Encoding.h>
- #include <LibPDF/Fonts/PS1FontProgram.h>
- #include <LibPDF/Reader.h>
- #include <ctype.h>
- #include <math.h>
- namespace PDF {
- enum Command {
- HStem = 1,
- VStem = 3,
- VMoveTo,
- RLineTo,
- HLineTo,
- VLineTo,
- RRCurveTo,
- ClosePath,
- CallSubr,
- Return,
- Extended,
- HSbW,
- EndChar,
- RMoveTo = 21,
- HMoveTo,
- VHCurveTo = 30,
- HVCurveTo
- };
- enum ExtendedCommand {
- DotSection,
- VStem3,
- HStem3,
- Div = 12,
- CallOtherSubr = 16,
- Pop,
- SetCurrentPoint = 33,
- };
- PDFErrorOr<void> PS1FontProgram::parse(ReadonlyBytes const& bytes, size_t cleartext_length, size_t encrypted_length)
- {
- Reader reader(bytes);
- if (reader.remaining() == 0)
- return error("Empty font program");
- reader.move_to(0);
- if (reader.remaining() < 2 || !reader.matches("%!"))
- return error("Not a font program");
- if (!seek_name(reader, CommonNames::Encoding))
- return error("Missing encoding array");
- if (TRY(parse_word(reader)) == "StandardEncoding") {
- m_encoding = Encoding::standard_encoding();
- } else {
- HashMap<u16, CharDescriptor> descriptors;
- while (reader.remaining()) {
- auto word = TRY(parse_word(reader));
- if (word == "readonly") {
- break;
- } else if (word == "dup") {
- u32 code_point = TRY(parse_int(reader));
- auto name = TRY(parse_word(reader));
- descriptors.set(code_point, { name.starts_with('/') ? name.substring_view(1) : name.view(), code_point });
- }
- }
- m_encoding = TRY(Encoding::create(descriptors));
- }
- bool found_font_matrix = seek_name(reader, "FontMatrix");
- if (found_font_matrix) {
- auto array = TRY(parse_number_array(reader, 6));
- m_font_matrix = { array[0], array[1], array[2], array[3], array[4], array[5] };
- } else {
- m_font_matrix = { 0.001f, 0.0f, 0.0f, 0.001f, 0.0f, 0.0f };
- }
- auto decrypted = TRY(decrypt(reader.bytes().slice(cleartext_length, encrypted_length), 55665, 4));
- return parse_encrypted_portion(decrypted);
- }
- Gfx::Path PS1FontProgram::build_char(u32 code_point, Gfx::FloatPoint const& point, float width)
- {
- if (!m_glyph_map.contains(code_point))
- return {};
- auto glyph = m_glyph_map.get(code_point).value();
- auto scale = width / (m_font_matrix.a() * glyph.width + m_font_matrix.e());
- auto transform = m_font_matrix;
- // Convert character space to device space.
- transform.scale(scale, -scale);
- transform.set_translation(point);
- return glyph.path.copy_transformed(transform);
- }
- PDFErrorOr<PS1FontProgram::Glyph> PS1FontProgram::parse_glyph(ReadonlyBytes const& data, GlyphParserState& state)
- {
- auto push = [&](float value) -> PDFErrorOr<void> {
- if (state.sp >= state.stack.size())
- return error("Operand stack overflow");
- state.stack[state.sp++] = value;
- return {};
- };
- auto pop = [&]() -> float {
- return state.sp ? state.stack[--state.sp] : 0.0f;
- };
- auto& path = state.glyph.path;
- // Parse the stream of parameters and commands that make up a glyph outline.
- for (size_t i = 0; i < data.size(); ++i) {
- auto require = [&](unsigned num) -> PDFErrorOr<void> {
- if (i + num >= data.size())
- return error("Malformed glyph outline definition");
- return {};
- };
- int v = data[i];
- if (v == 255) {
- TRY(require(4));
- int a = data[++i];
- int b = data[++i];
- int c = data[++i];
- int d = data[++i];
- TRY(push((a << 24) + (b << 16) + (c << 8) + d));
- } else if (v >= 251) {
- TRY(require(1));
- auto w = data[++i];
- TRY(push(-((v - 251) * 256) - w - 108));
- } else if (v >= 247) {
- TRY(require(1));
- auto w = data[++i];
- TRY(push(((v - 247) * 256) + w + 108));
- } else if (v >= 32) {
- TRY(push(v - 139));
- } else {
- // Not a parameter but a command byte.
- switch (v) {
- case HStem:
- case VStem:
- state.sp = 0;
- break;
- case VMoveTo: {
- auto dy = pop();
- state.point.translate_by(0.0f, dy);
- if (state.flex_feature) {
- state.flex_sequence[state.flex_index++] = state.point.x();
- state.flex_sequence[state.flex_index++] = state.point.y();
- } else {
- path.move_to(state.point);
- }
- state.sp = 0;
- break;
- }
- case RLineTo: {
- auto dy = pop();
- auto dx = pop();
- state.point.translate_by(dx, dy);
- path.line_to(state.point);
- state.sp = 0;
- break;
- }
- case HLineTo: {
- auto dx = pop();
- state.point.translate_by(dx, 0.0f);
- path.line_to(state.point);
- state.sp = 0;
- break;
- }
- case VLineTo: {
- auto dy = pop();
- state.point.translate_by(0.0f, dy);
- path.line_to(state.point);
- state.sp = 0;
- break;
- }
- case RRCurveTo: {
- auto dy3 = pop();
- auto dx3 = pop();
- auto dy2 = pop();
- auto dx2 = pop();
- auto dy1 = pop();
- auto dx1 = pop();
- auto& point = state.point;
- path.cubic_bezier_curve_to(
- point + Gfx::FloatPoint(dx1, dy1),
- point + Gfx::FloatPoint(dx1 + dx2, dy1 + dy2),
- point + Gfx::FloatPoint(dx1 + dx2 + dx3, dy1 + dy2 + dy3));
- point.translate_by(dx1 + dx2 + dx3, dy1 + dy2 + dy3);
- state.sp = 0;
- break;
- }
- case ClosePath:
- path.close();
- state.sp = 0;
- break;
- case CallSubr: {
- auto subr_number = pop();
- if (static_cast<size_t>(subr_number) >= m_subroutines.size())
- return error("Subroutine index out of range");
- // Subroutines 0-2 handle the flex feature.
- if (subr_number == 0) {
- if (state.flex_index != 14)
- break;
- auto& flex = state.flex_sequence;
- path.cubic_bezier_curve_to(
- { flex[2], flex[3] },
- { flex[4], flex[5] },
- { flex[6], flex[7] });
- path.cubic_bezier_curve_to(
- { flex[8], flex[9] },
- { flex[10], flex[11] },
- { flex[12], flex[13] });
- state.flex_feature = false;
- state.sp = 0;
- } else if (subr_number == 1) {
- state.flex_feature = true;
- state.flex_index = 0;
- state.sp = 0;
- } else if (subr_number == 2) {
- state.sp = 0;
- } else {
- auto subr = m_subroutines[subr_number];
- if (subr.is_empty())
- return error("Empty subroutine");
- TRY(parse_glyph(subr, state));
- }
- break;
- }
- case Return:
- break;
- case Extended: {
- TRY(require(1));
- switch (data[++i]) {
- case DotSection:
- case VStem3:
- case HStem3:
- // FIXME: Do something with these?
- state.sp = 0;
- break;
- case Div: {
- auto num2 = pop();
- auto num1 = pop();
- TRY(push(num2 ? num1 / num2 : 0.0f));
- break;
- }
- case CallOtherSubr: {
- auto othersubr_number = pop();
- auto n = static_cast<int>(pop());
- if (othersubr_number == 0) {
- state.postscript_stack[state.postscript_sp++] = pop();
- state.postscript_stack[state.postscript_sp++] = pop();
- pop();
- } else if (othersubr_number == 3) {
- state.postscript_stack[state.postscript_sp++] = 3;
- } else {
- for (int i = 0; i < n; ++i)
- state.postscript_stack[state.postscript_sp++] = pop();
- }
- (void)othersubr_number;
- break;
- }
- case Pop:
- TRY(push(state.postscript_stack[--state.postscript_sp]));
- break;
- case SetCurrentPoint: {
- auto y = pop();
- auto x = pop();
- state.point = { x, y };
- path.move_to(state.point);
- state.sp = 0;
- break;
- }
- default:
- return error(String::formatted("Unhandled command: 12 {}", data[i]));
- }
- break;
- }
- case HSbW: {
- auto wx = pop();
- auto sbx = pop();
- state.glyph.width = wx;
- state.point = { sbx, 0.0f };
- state.sp = 0;
- break;
- }
- case EndChar:
- break;
- case RMoveTo: {
- auto dy = pop();
- auto dx = pop();
- state.point.translate_by(dx, dy);
- if (state.flex_feature) {
- state.flex_sequence[state.flex_index++] = state.point.x();
- state.flex_sequence[state.flex_index++] = state.point.y();
- } else {
- path.move_to(state.point);
- }
- state.sp = 0;
- break;
- }
- case HMoveTo: {
- auto dx = pop();
- state.point.translate_by(dx, 0.0f);
- if (state.flex_feature) {
- state.flex_sequence[state.flex_index++] = state.point.x();
- state.flex_sequence[state.flex_index++] = state.point.y();
- } else {
- path.move_to(state.point);
- }
- state.sp = 0;
- break;
- }
- case VHCurveTo: {
- auto dx3 = pop();
- auto dy2 = pop();
- auto dx2 = pop();
- auto dy1 = pop();
- auto& point = state.point;
- path.cubic_bezier_curve_to(
- point + Gfx::FloatPoint(0.0f, dy1),
- point + Gfx::FloatPoint(dx2, dy1 + dy2),
- point + Gfx::FloatPoint(dx2 + dx3, dy1 + dy2));
- point.translate_by(dx2 + dx3, dy1 + dy2);
- state.sp = 0;
- break;
- }
- case HVCurveTo: {
- auto dy3 = pop();
- auto dy2 = pop();
- auto dx2 = pop();
- auto dx1 = pop();
- auto& point = state.point;
- path.cubic_bezier_curve_to(
- point + Gfx::FloatPoint(dx1, 0.0f),
- point + Gfx::FloatPoint(dx1 + dx2, dy2),
- point + Gfx::FloatPoint(dx1 + dx2, dy2 + dy3));
- point.translate_by(dx1 + dx2, dy2 + dy3);
- state.sp = 0;
- break;
- }
- default:
- return error(String::formatted("Unhandled command: {}", v));
- }
- }
- }
- return state.glyph;
- }
- PDFErrorOr<void> PS1FontProgram::parse_encrypted_portion(ByteBuffer const& buffer)
- {
- Reader reader(buffer);
- if (seek_name(reader, "lenIV"))
- m_lenIV = TRY(parse_int(reader));
- if (!seek_name(reader, "Subrs"))
- return error("Missing subroutine array");
- m_subroutines = TRY(parse_subroutines(reader));
- if (!seek_name(reader, "CharStrings"))
- return error("Missing char strings array");
- while (reader.remaining()) {
- auto word = TRY(parse_word(reader));
- VERIFY(!word.is_empty());
- if (word == "end")
- break;
- if (word[0] == '/') {
- auto encrypted_size = TRY(parse_int(reader));
- auto rd = TRY(parse_word(reader));
- if (rd == "-|" || rd == "RD") {
- auto line = TRY(decrypt(reader.bytes().slice(reader.offset(), encrypted_size), m_encryption_key, m_lenIV));
- reader.move_by(encrypted_size);
- auto name_mapping = m_encoding->name_mapping();
- auto code_point = name_mapping.ensure(word.substring_view(1));
- GlyphParserState state;
- m_glyph_map.set(code_point, TRY(parse_glyph(line, state)));
- }
- }
- }
- return {};
- }
- PDFErrorOr<Vector<ByteBuffer>> PS1FontProgram::parse_subroutines(Reader& reader)
- {
- if (!reader.matches_number())
- return error("Expected array length");
- auto length = TRY(parse_int(reader));
- VERIFY(length <= 1024);
- Vector<ByteBuffer> array;
- TRY(array.try_resize(length));
- while (reader.remaining()) {
- auto word = TRY(parse_word(reader));
- if (word.is_empty())
- VERIFY(0);
- if (word == "dup") {
- auto index = TRY(parse_int(reader));
- auto entry = TRY(parse_word(reader));
- if (entry.is_empty())
- return error("Empty array entry");
- if (index >= length)
- return error("Array index out of bounds");
- if (isdigit(entry[0])) {
- auto maybe_encrypted_size = entry.to_int();
- if (!maybe_encrypted_size.has_value())
- return error("Malformed array");
- auto rd = TRY(parse_word(reader));
- if (rd == "-|" || rd == "RD") {
- array[index] = TRY(decrypt(reader.bytes().slice(reader.offset(), maybe_encrypted_size.value()), m_encryption_key, m_lenIV));
- reader.move_by(maybe_encrypted_size.value());
- }
- } else {
- array[index] = TRY(ByteBuffer::copy(entry.bytes()));
- }
- } else if (word == "index") {
- break;
- }
- }
- return array;
- }
- PDFErrorOr<Vector<float>> PS1FontProgram::parse_number_array(Reader& reader, size_t length)
- {
- Vector<float> array;
- TRY(array.try_resize(length));
- reader.consume_whitespace();
- if (!reader.consume('['))
- return error("Expected array to start with '['");
- reader.consume_whitespace();
- for (size_t i = 0; i < length; ++i)
- array.at(i) = TRY(parse_float(reader));
- if (!reader.consume(']'))
- return error("Expected array to end with ']'");
- return array;
- }
- PDFErrorOr<String> PS1FontProgram::parse_word(Reader& reader)
- {
- reader.consume_whitespace();
- auto start = reader.offset();
- reader.move_while([&](char c) {
- return !reader.matches_whitespace() && c != '[' && c != ']';
- });
- auto end = reader.offset();
- if (reader.matches_whitespace())
- reader.consume();
- return StringView(reader.bytes().data() + start, end - start);
- }
- PDFErrorOr<float> PS1FontProgram::parse_float(Reader& reader)
- {
- auto word = TRY(parse_word(reader));
- return strtof(String(word).characters(), nullptr);
- }
- PDFErrorOr<int> PS1FontProgram::parse_int(Reader& reader)
- {
- auto maybe_int = TRY(parse_word(reader)).to_int();
- if (!maybe_int.has_value())
- return error("Invalid int");
- return maybe_int.value();
- }
- PDFErrorOr<ByteBuffer> PS1FontProgram::decrypt(ReadonlyBytes const& encrypted, u16 key, size_t skip)
- {
- auto decrypted = TRY(ByteBuffer::create_uninitialized(encrypted.size() - skip));
- u16 R = key;
- u16 c1 = 52845;
- u16 c2 = 22719;
- for (size_t i = 0; i < encrypted.size(); ++i) {
- u8 C = encrypted[i];
- u8 P = C ^ (R >> 8);
- R = (C + R) * c1 + c2;
- if (i >= skip)
- decrypted[i - skip] = P;
- }
- return decrypted;
- }
- bool PS1FontProgram::seek_name(Reader& reader, String const& name)
- {
- auto start = reader.offset();
- reader.move_to(0);
- while (reader.remaining()) {
- if (reader.consume('/') && reader.matches(name.characters())) {
- // Skip name
- reader.move_while([&](char) {
- return reader.matches_regular_character();
- });
- reader.consume_whitespace();
- return true;
- }
- }
- // Jump back to where we started
- reader.move_to(start);
- return false;
- }
- Error PS1FontProgram::error(
- String const& message
- #ifdef PDF_DEBUG
- ,
- SourceLocation loc
- #endif
- )
- {
- #ifdef PDF_DEBUG
- dbgln("\033[31m{} Type 1 font error: {}\033[0m", loc, message);
- #endif
- return Error { Error::Type::MalformedPDF, message };
- }
- }
|