mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-14 22:31:29 +03:00
5e1499d104
This commit un-deprecates DeprecatedString, and repurposes it as a byte string. As the null state has already been removed, there are no other particularly hairy blockers in repurposing this type as a byte string (what it _really_ is). This commit is auto-generated: $ xs=$(ack -l \bDeprecatedString\b\|deprecated_string AK Userland \ Meta Ports Ladybird Tests Kernel) $ perl -pie 's/\bDeprecatedString\b/ByteString/g; s/deprecated_string/byte_string/g' $xs $ clang-format --style=file -i \ $(git diff --name-only | grep \.cpp\|\.h) $ gn format $(git ls-files '*.gn' '*.gni')
241 lines
7.2 KiB
C++
241 lines
7.2 KiB
C++
/*
|
||
* 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 {
|
||
|
||
PDFErrorOr<NonnullRefPtr<Type1FontProgram>> PS1FontProgram::create(ReadonlyBytes const& bytes, RefPtr<Encoding> encoding, 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");
|
||
|
||
auto font_program = adopt_ref(*new PS1FontProgram());
|
||
if (encoding) {
|
||
// 9.6.6.2 Encodings for Type 1 Fonts:
|
||
// An Encoding entry may override a Type 1 font’s mapping from character codes to character names.
|
||
font_program->set_encoding(move(encoding));
|
||
} else {
|
||
if (TRY(parse_word(reader)) == "StandardEncoding") {
|
||
font_program->set_encoding(Encoding::standard_encoding());
|
||
} else {
|
||
auto encoding = Encoding::create();
|
||
while (reader.remaining()) {
|
||
auto word = TRY(parse_word(reader));
|
||
if (word == "readonly") {
|
||
break;
|
||
} else if (word == "dup") {
|
||
u8 char_code = TRY(parse_int(reader));
|
||
auto name = TRY(parse_word(reader));
|
||
encoding->set(char_code, name.starts_with('/') ? name.substring_view(1) : name.view());
|
||
}
|
||
}
|
||
font_program->set_encoding(move(encoding));
|
||
}
|
||
}
|
||
|
||
bool found_font_matrix = seek_name(reader, "FontMatrix");
|
||
if (found_font_matrix) {
|
||
auto array = TRY(parse_number_array(reader, 6));
|
||
font_program->set_font_matrix({ array[0], array[1], array[2], array[3], array[4], array[5] });
|
||
} else {
|
||
font_program->set_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));
|
||
TRY(font_program->parse_encrypted_portion(decrypted));
|
||
return font_program;
|
||
}
|
||
|
||
PDFErrorOr<void> PS1FontProgram::parse_encrypted_portion(ByteBuffer const& buffer)
|
||
{
|
||
Reader reader(buffer);
|
||
|
||
if (seek_name(reader, "lenIV"))
|
||
m_lenIV = TRY(parse_int(reader));
|
||
|
||
Vector<ByteBuffer> subroutines;
|
||
if (seek_name(reader, "Subrs"))
|
||
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 glyph_name = word.substring_view(1);
|
||
GlyphParserState state;
|
||
TRY(add_glyph(glyph_name, TRY(parse_glyph(line, subroutines, {}, state, false))));
|
||
}
|
||
}
|
||
}
|
||
|
||
consolidate_glyphs();
|
||
return {};
|
||
}
|
||
|
||
PDFErrorOr<Vector<ByteBuffer>> PS1FontProgram::parse_subroutines(Reader& reader) const
|
||
{
|
||
if (!reader.matches_number())
|
||
return error("Expected array length");
|
||
|
||
auto length = TRY(parse_int(reader));
|
||
VERIFY(length >= 0);
|
||
|
||
Vector<ByteBuffer> array;
|
||
TRY(array.try_resize(length));
|
||
|
||
while (reader.remaining()) {
|
||
auto word = TRY(parse_word(reader));
|
||
VERIFY(!word.is_empty());
|
||
|
||
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" || word == "def" || word == "ND") {
|
||
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<ByteString> 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(ByteString(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, ByteString 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;
|
||
}
|
||
|
||
}
|