Преглед изворни кода

LibPDF: Implement support for vertical text for Type0

For Identity-V only for now.
Nico Weber пре 1 година
родитељ
комит
c69797fda9

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

@@ -13,5 +13,6 @@ ENUMERATE_COMMON_NAMES(ENUMERATE)
 #undef ENUMERATE
 
 DeprecatedFlyString CommonNames::IdentityH = "Identity-H";
+DeprecatedFlyString CommonNames::IdentityV = "Identity-V";
 
 }

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

@@ -206,7 +206,9 @@ public:
     ENUMERATE_COMMON_NAMES(ENUMERATE)
 #undef ENUMERATE
 
+    // Special cases where the string value isn't identical to the name.
     static DeprecatedFlyString IdentityH;
+    static DeprecatedFlyString IdentityV;
 };
 
 }

+ 42 - 9
Userland/Libraries/LibPDF/Fonts/Type0Font.cpp

@@ -239,6 +239,11 @@ Type0Font::~Type0Font() = default;
 
 class IdentityType0CMap : public Type0CMap {
 public:
+    IdentityType0CMap(WritingMode writing_mode)
+        : Type0CMap(writing_mode)
+    {
+    }
+
     virtual PDFErrorOr<NonnullOwnPtr<CIDIterator>> iterate(ReadonlyBytes bytes) const override
     {
         // 9.7.5.2 Predefined CMaps:
@@ -278,10 +283,12 @@ static PDFErrorOr<NonnullOwnPtr<Type0CMap>> make_cmap(NonnullRefPtr<Object> cons
         return Error::rendering_unsupported_error("Type0 font: support for general type 0 cmaps not yet implemented");
 
     auto cmap_name = cmap_value->cast<NameObject>()->name();
-    if (cmap_name != CommonNames::IdentityH)
+    if (cmap_name != CommonNames::IdentityH && cmap_name != CommonNames::IdentityV)
         return Error::rendering_unsupported_error("Type0 font: unimplemented named type 0 cmap {}", cmap_name);
 
-    return make<IdentityType0CMap>();
+    WritingMode writing_mode = cmap_name.ends_with("-H"sv) ? WritingMode::Horizontal : WritingMode::Vertical;
+
+    return make<IdentityType0CMap>(writing_mode);
 }
 
 PDFErrorOr<void> Type0Font::initialize(Document* document, NonnullRefPtr<DictObject> const& dict, float font_size)
@@ -438,10 +445,14 @@ PDFErrorOr<Gfx::FloatPoint> Type0Font::draw_string(Gfx::Painter& painter, Gfx::F
     auto character_spacing = renderer.text_state().character_spacing;
     auto word_spacing = renderer.text_state().word_spacing;
 
+    WritingMode writing_mode = m_cmap->writing_mode();
+
     auto cids = TRY(m_cmap->iterate(string.bytes()));
     while (cids->has_next()) {
         auto cid = cids->next();
 
+        // FIGURE 5.5 Metrics for horizontal and vertical writing modes
+
         // Use the width specified in the font's dictionary if available,
         // and use the default width for the given font otherwise.
         float glyph_width;
@@ -450,16 +461,35 @@ PDFErrorOr<Gfx::FloatPoint> Type0Font::draw_string(Gfx::Painter& painter, Gfx::F
         else
             glyph_width = font_size * m_missing_width / 1000.0f;
 
-        Gfx::FloatPoint glyph_render_position = text_rendering_matrix.map(glyph_position);
-        TRY(m_cid_font_type->draw_glyph(painter, glyph_render_position, glyph_width, cid, renderer));
+        VerticalMetric vertical_metric;
+        float vertical_displacement_vector_y;
+        float position_vector_x = 0.0f;
+        float position_vector_y = 0.0f;
+        if (writing_mode == WritingMode::Vertical) {
+            if (auto metric = m_vertical_metrics.get(cid); metric.has_value()) {
+                vertical_metric = metric.value();
+                vertical_displacement_vector_y = renderer.text_state().font_size * vertical_metric.vertical_displacement_vector_y / 1000.0f;
+                position_vector_x = vertical_metric.position_vector_x / 1000.0f;
+                position_vector_y = vertical_metric.position_vector_y / 1000.0f;
+            } else {
+                vertical_displacement_vector_y = renderer.text_state().font_size * m_default_displacement_vector_y / 1000.0f;
+                position_vector_x = glyph_width / 2.0f / font_size;
+                position_vector_y = m_default_position_vector_y / 1000.0f;
+            }
+        }
 
-        // FIXME: Honor encoding's WMode for vertical text.
+        Gfx::FloatPoint glyph_render_position = text_rendering_matrix.map(glyph_position - Gfx::FloatPoint { position_vector_x, position_vector_y });
+        TRY(m_cid_font_type->draw_glyph(painter, glyph_render_position, glyph_width, cid, renderer));
 
         // glyph_width is scaled by `text_rendering_matrix.x_scale() * renderer.text_state().font_size / horizontal_scaling`,
         // but it should only be scaled by `renderer.text_state().font_size`.
         // FIXME: Having to divide here isn't pretty. Refactor things so that this isn't needed.
-        auto tx = glyph_width / text_rendering_matrix.x_scale() * horizontal_scaling;
-        tx += character_spacing;
+        float displacement;
+        if (writing_mode == WritingMode::Horizontal)
+            displacement = glyph_width / text_rendering_matrix.x_scale() * horizontal_scaling;
+        else
+            displacement = vertical_displacement_vector_y;
+        displacement += character_spacing;
 
         // ISO 32000 (PDF 2.0), 9.3.3 Wordspacing
         // "Word spacing shall be applied to every occurrence of the single-byte character code 32
@@ -468,9 +498,12 @@ PDFErrorOr<Gfx::FloatPoint> Type0Font::draw_string(Gfx::Painter& painter, Gfx::F
         // FIXME: Identity-H always uses 2 bytes, but this will be true once we support more encodings.
         bool was_single_byte_code = false;
         if (cid == ' ' && was_single_byte_code)
-            tx += word_spacing;
+            displacement += word_spacing;
 
-        glyph_position += { tx, 0.0f };
+        if (writing_mode == WritingMode::Horizontal)
+            glyph_position += { displacement, 0.0f };
+        else
+            glyph_position += { 0.0f, displacement };
     }
     return glyph_position;
 }

+ 18 - 0
Userland/Libraries/LibPDF/Fonts/Type0Font.h

@@ -27,10 +27,28 @@ public:
     virtual u32 next() = 0;
 };
 
+enum class WritingMode {
+    Horizontal,
+    Vertical,
+};
+
 class Type0CMap {
 public:
     virtual ~Type0CMap() = default;
+
+    // "(Writing mode is specified as part of the CMap because, in some cases, different shapes are used when writing horizontally and vertically.
+    //  In such cases, the horizontal and vertical variants of a CMap specify different CIDs for a given character code.)"
+    WritingMode writing_mode() const { return m_writing_mode; }
+
     virtual PDFErrorOr<NonnullOwnPtr<CIDIterator>> iterate(ReadonlyBytes) const = 0;
+
+protected:
+    Type0CMap(WritingMode writing_mode)
+        : m_writing_mode(writing_mode)
+    {
+    }
+
+    WritingMode m_writing_mode;
 };
 
 class Type0Font : public PDFFont {

+ 2 - 2
Userland/Libraries/LibPDF/Renderer.cpp

@@ -1036,9 +1036,9 @@ PDFErrorOr<void> Renderer::show_text(ByteString const& string)
     auto end_position = TRY(text_state().font->draw_string(m_painter, start_position, string, *this));
 
     // Update text matrix.
-    auto delta_x = end_position.x() - start_position.x();
+    auto delta = end_position - start_position;
     m_text_rendering_matrix_is_dirty = true;
-    m_text_matrix.translate(delta_x * text_state().horizontal_scaling, 0.0f);
+    m_text_matrix.translate(delta);
     return {};
 }