ladybird/Libraries/LibWeb/Layout/TextOffsetMapping.cpp
Tim Ledbetter b67d73a661 LibWeb: Apply ::first-letter pseudo-element styles
We now apply first letter styles by splitting text with a first-letter
style applied into 2 `TextSliceNode` objects.  The
`DOM::Text` layout  node always points at the non first-letter slice
and the first-letter slice is  reachable via
`TextSliceNode::first_letter_slice()`.

First letter splitting works by `TreeBuilder` walking a block
container's inline descendants to find the first typographic letter
unit per the pattern given in  css-pseudo level 4, which is then
wrapped in an anonymous inline box styled with the `::first-letter`
computed properties.

Consumers that map between DOM offsets and layout geometry
are updated to visit all slices of a `DOM::Text` through
`TextOffsetMapping`.
2026-05-20 12:09:19 +01:00

34 lines
993 B
C++

/*
* Copyright (c) 2026, Tim Ledbetter <tim.ledbetter@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/DOM/Text.h>
#include <LibWeb/Layout/TextNode.h>
#include <LibWeb/Layout/TextOffsetMapping.h>
namespace Web::Layout {
TextOffsetMapping::TextOffsetMapping(DOM::Text const& text)
{
m_primary = as_if<TextNode>(text.unsafe_layout_node());
if (auto* primary_slice = as_if<TextSliceNode>(m_primary.ptr()))
m_first_letter_slice = primary_slice->first_letter_slice();
}
TextNode const* TextOffsetMapping::fragment_containing(size_t dom_offset) const
{
auto contains = [&](TextNode const& fragment) {
auto const start = fragment.dom_start_offset();
return dom_offset >= start && dom_offset <= start + fragment.dom_length();
};
if (m_first_letter_slice && contains(*m_first_letter_slice))
return m_first_letter_slice;
if (m_primary && contains(*m_primary))
return m_primary;
return nullptr;
}
}