Просмотр исходного кода

LibPDF: Use Font /Widths array to derive character widths

This makes the spacing between chars _much_ better!
Matthew Olsson 3 лет назад
Родитель
Сommit
d2771eafc5

+ 1 - 0
Userland/Libraries/LibPDF/CommonNames.h

@@ -81,6 +81,7 @@
     A(ML)                         \
     A(Matrix)                     \
     A(MediaBox)                   \
+    A(MissingWidth)               \
     A(N)                          \
     A(Next)                       \
     A(O)                          \

+ 31 - 2
Userland/Libraries/LibPDF/Fonts.cpp

@@ -63,12 +63,29 @@ PDFErrorOr<NonnullRefPtr<Type1Font>> Type1Font::create(Document* document, Nonnu
     if (dict->contains(CommonNames::ToUnicode))
         to_unicode = MUST(dict->get_stream(document, CommonNames::ToUnicode));
 
-    return adopt_ref(*new Type1Font(to_unicode, encoding.release_nonnull()));
+    auto first_char = dict->get_value(CommonNames::FirstChar).get<int>();
+    auto last_char = dict->get_value(CommonNames::LastChar).get<int>();
+    auto widths_array = MUST(dict->get_array(document, CommonNames::Widths));
+
+    VERIFY(widths_array->size() == static_cast<size_t>(last_char - first_char + 1));
+
+    HashMap<u16, u16> widths;
+    for (size_t i = 0; i < widths_array->size(); i++)
+        widths.set(first_char + i, widths_array->at(i).get<int>());
+
+    u16 missing_width = 0;
+    auto descriptor = MUST(dict->get_dict(document, CommonNames::FontDescriptor));
+    if (descriptor->contains(CommonNames::MissingWidth))
+        missing_width = descriptor->get_value(CommonNames::MissingWidth).get<int>();
+
+    return adopt_ref(*new Type1Font(to_unicode, encoding.release_nonnull(), widths, missing_width));
 }
 
-Type1Font::Type1Font(RefPtr<StreamObject> to_unicode, NonnullRefPtr<Encoding> encoding)
+Type1Font::Type1Font(RefPtr<StreamObject> to_unicode, NonnullRefPtr<Encoding> encoding, HashMap<u16, u16> const& widths, u16 missing_width)
     : m_to_unicode(to_unicode)
     , m_encoding(encoding)
+    , m_widths(widths)
+    , m_missing_width(missing_width)
 {
 }
 
@@ -81,4 +98,16 @@ u32 Type1Font::char_code_to_code_point(u16 char_code) const
     return descriptor.code_point;
 }
 
+float Type1Font::get_char_width(u16 char_code) const
+{
+    u16 width;
+    if (auto char_code_width = m_widths.get(char_code); char_code_width.has_value()) {
+        width = char_code_width.value();
+    } else {
+        width = m_missing_width;
+    }
+
+    return static_cast<float>(width) / 1000.0f;
+}
+
 }

+ 5 - 1
Userland/Libraries/LibPDF/Fonts.h

@@ -18,20 +18,24 @@ public:
     virtual ~PDFFont() = default;
 
     virtual u32 char_code_to_code_point(u16 char_code) const = 0;
+    virtual float get_char_width(u16 char_code) const = 0;
 };
 
 class Type1Font : public PDFFont {
 public:
     static PDFErrorOr<NonnullRefPtr<Type1Font>> create(Document*, NonnullRefPtr<DictObject>);
 
-    Type1Font(RefPtr<StreamObject> to_unicode, NonnullRefPtr<Encoding>);
+    Type1Font(RefPtr<StreamObject> to_unicode, NonnullRefPtr<Encoding>, HashMap<u16, u16> const& m_widths, u16 missing_width);
     ~Type1Font() override = default;
 
     u32 char_code_to_code_point(u16 char_code) const override;
+    float get_char_width(u16 char_code) const override;
 
 private:
     RefPtr<StreamObject> m_to_unicode;
     NonnullRefPtr<Encoding> m_encoding;
+    HashMap<u16, u16> m_widths;
+    u16 m_missing_width;
 };
 
 }

+ 5 - 3
Userland/Libraries/LibPDF/Renderer.cpp

@@ -641,8 +641,9 @@ void Renderer::show_text(String const& string, float shift)
 {
     auto& text_rendering_matrix = calculate_text_rendering_matrix();
 
-    auto font_size = static_cast<int>(text_rendering_matrix.x_scale() * text_state().font_size);
-    auto font = Gfx::FontDatabase::the().get(text_state().font_family, text_state().font_variant, font_size);
+    auto font_size = text_rendering_matrix.x_scale() * text_state().font_size;
+    auto font_size_int = static_cast<int>(text_rendering_matrix.x_scale() * text_state().font_size);
+    auto font = Gfx::FontDatabase::the().get(text_state().font_family, text_state().font_variant, font_size_int);
     VERIFY(font);
 
     auto glyph_position = text_rendering_matrix.map(Gfx::FloatPoint { 0.0f, 0.0f });
@@ -653,11 +654,12 @@ void Renderer::show_text(String const& string, float shift)
 
     for (auto char_code : string.bytes()) {
         auto code_point = text_state().font->char_code_to_code_point(char_code);
+        auto char_width = text_state().font->get_char_width(char_code);
 
         if (code_point != 0x20)
             m_painter.draw_glyph(glyph_position.to_type<int>(), code_point, *font, state().paint_color);
 
-        auto glyph_width = static_cast<float>(font->glyph_width(code_point));
+        auto glyph_width = char_width * font_size;
         auto tx = (glyph_width - shift / 1000.0f);
         tx += text_state().character_spacing;