mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 14:32:46 +00:00 
			
		
		
		
	 e2e7c4d574
			
		
	
	
		e2e7c4d574
		
	
	
	
	
		
			
			In a bunch of cases, this actually ends up simplifying the code as
to_number will handle something such as:
```
Optional<I> opt;
if constexpr (IsSigned<I>)
    opt = view.to_int<I>();
else
    opt = view.to_uint<I>();
```
For us.
The main goal here however is to have a single generic number conversion
API between all of the String classes.
		
	
			
		
			
				
	
	
		
			240 lines
		
	
	
	
		
			7.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			240 lines
		
	
	
	
		
			7.3 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_number<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_number<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;
 | ||
| }
 | ||
| 
 | ||
| }
 |