mirror of
https://github.com/RGBCube/serenity
synced 2025-05-14 05:54:58 +00:00
LibPDF: Implement support for vertical text for Type0
For Identity-V only for now.
This commit is contained in:
parent
6348a857ea
commit
c69797fda9
5 changed files with 65 additions and 11 deletions
|
@ -13,5 +13,6 @@ ENUMERATE_COMMON_NAMES(ENUMERATE)
|
|||
#undef ENUMERATE
|
||||
|
||||
DeprecatedFlyString CommonNames::IdentityH = "Identity-H";
|
||||
DeprecatedFlyString CommonNames::IdentityV = "Identity-V";
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {};
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue