Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

text: Fix getCharBoundaries for justified text #18851

Merged
merged 2 commits into from
Dec 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 22 additions & 7 deletions core/src/html/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1128,9 +1128,22 @@ impl<'gc> LayoutLine<'gc> {
pub fn char_x_bounds(&self, position: usize) -> Option<(Twips, Twips)> {
let box_index = self.find_box_index_by_position(position)?;
let layout_box = self.boxes.get(box_index)?;
let origin_x = layout_box.bounds().origin().x();
let (start, end) = layout_box.char_x_bounds(position)?;
Some((origin_x + start, origin_x + end))
let (start, mut end) = layout_box.char_x_bounds(position)?;

// If we're getting bounds of a space in justified text,
// we have to take into account that the character is stretched,
// and its bounds will end where the next character starts.
// If it's not a space or the text is not justified, it won't change the value.
// TODO [KJ] We need to test this behavior with letter spacing or kerning enabled.
if layout_box.end() == position + 1 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to be sure, can you clarify how if layout_box.end() == position + 1 { indicates that it's a space? I'm not nitpicking and this isn't blocking, I'm still trying to better wrap my head around the abstractions.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure thing!

It doesn't check for a space at all. It just uses the next character's start position as this character's end position when the character is at the end of a box. For non-spaces or non-justified text it just won't change the value.

Now thinking about this, the logic might be different when we take letter spacing or kerning into account, but there are no tests for that yet so we'll see. I'm just trying not to touch every possible aspect I can think of at the same time though 😅

Updated the comment

if let Some(next_box) = self.boxes.get(box_index + 1) {
if let Some(next_start) = next_box.char_x_bounds(position + 1).map(|(s, _)| s) {
end = next_start;
}
}
}

Some((start, end))
}

/// Returns char bounds of the given char relative to the whole layout.
Expand Down Expand Up @@ -1421,20 +1434,22 @@ impl<'gc> LayoutBox<'gc> {
}
}

/// Return x-axis char bounds of the given char relative to this layout box.
/// Return x-axis char bounds of the given char relative to the whole layout.
pub fn char_x_bounds(&self, position: usize) -> Option<(Twips, Twips)> {
let relative_position = position.checked_sub(self.start())?;

let LayoutContent::Text { char_end_pos, .. } = &self.content else {
return None;
};

let origin_x = self.bounds().origin().x();

Some(if relative_position == 0 {
(Twips::ZERO, *char_end_pos.get(0)?)
(origin_x, origin_x + *char_end_pos.get(0)?)
} else {
(
*char_end_pos.get(relative_position - 1)?,
*char_end_pos.get(relative_position)?,
origin_x + *char_end_pos.get(relative_position - 1)?,
origin_x + *char_end_pos.get(relative_position)?,
)
})
}
Expand Down
3 changes: 3 additions & 0 deletions tests/tests/swfs/avm2/edittext_getcharboundaries/Test.as
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ public class Test extends Sprite {
text.defaultTextFormat = tf;

testHtml("<p><font size='+2'>xyM</font></p><p><font size='+4'>xyM</font></p><p><font size='-2'>xyM</font></p>", 0);

text.wordWrap = true;
testHtml("<p align='justify'>xxxx y zzzz xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</p>", 0);
}

private function testHtml(htmlText:String, xPrecision:int = 0):void {
Expand Down
69 changes: 69 additions & 0 deletions tests/tests/swfs/avm2/edittext_getcharboundaries/output.txt
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,72 @@ Text: <p><font size='+2'>xyM</font></p><p><font size='+4'>xyM</font></p><p><font
text.getCharBoundaries(10) = (x=12.3, y=32.15, w=9.05, h=10.05)
text.getCharBoundaries(11) = null
text.getCharBoundaries(12) = null
Text: <p align='justify'>xxxx y zzzz xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</p>
text.getCharBoundaries(-5) = null
text.getCharBoundaries(-1) = null
text.getCharBoundaries(0) = (x=2, y=2, w=6.35, h=12)
text.getCharBoundaries(1) = (x=8.35, y=2, w=6.35, h=12)
text.getCharBoundaries(2) = (x=14.7, y=2, w=6.35, h=12)
text.getCharBoundaries(3) = (x=21.05, y=2, w=6.35, h=12)
text.getCharBoundaries(4) = (x=27.4, y=2, w=161.05, h=12)
text.getCharBoundaries(5) = (x=188.45, y=2, w=6.1, h=12)
text.getCharBoundaries(6) = (x=194.55, y=2, w=161.05, h=12)
text.getCharBoundaries(7) = (x=355.6, y=2, w=5.6, h=12)
text.getCharBoundaries(8) = (x=361.2, y=2, w=5.6, h=12)
text.getCharBoundaries(9) = (x=366.8, y=2, w=5.6, h=12)
text.getCharBoundaries(10) = (x=372.4, y=2, w=5.6, h=12)
text.getCharBoundaries(11) = (x=378, y=2, w=3.1, h=12)
text.getCharBoundaries(12) = (x=2, y=14, w=6.35, h=12)
text.getCharBoundaries(13) = (x=8.35, y=14, w=6.35, h=12)
text.getCharBoundaries(14) = (x=14.7, y=14, w=6.35, h=12)
text.getCharBoundaries(15) = (x=21.05, y=14, w=6.35, h=12)
text.getCharBoundaries(16) = (x=27.4, y=14, w=6.35, h=12)
text.getCharBoundaries(17) = (x=33.75, y=14, w=6.35, h=12)
text.getCharBoundaries(18) = (x=40.1, y=14, w=6.35, h=12)
text.getCharBoundaries(19) = (x=46.45, y=14, w=6.35, h=12)
text.getCharBoundaries(20) = (x=52.8, y=14, w=6.35, h=12)
text.getCharBoundaries(21) = (x=59.15, y=14, w=6.35, h=12)
text.getCharBoundaries(22) = (x=65.5, y=14, w=6.35, h=12)
text.getCharBoundaries(23) = (x=71.85, y=14, w=6.35, h=12)
text.getCharBoundaries(24) = (x=78.2, y=14, w=6.35, h=12)
text.getCharBoundaries(25) = (x=84.55, y=14, w=6.35, h=12)
text.getCharBoundaries(26) = (x=90.9, y=14, w=6.35, h=12)
text.getCharBoundaries(27) = (x=97.25, y=14, w=6.35, h=12)
text.getCharBoundaries(28) = (x=103.6, y=14, w=6.35, h=12)
text.getCharBoundaries(29) = (x=109.95, y=14, w=6.35, h=12)
text.getCharBoundaries(30) = (x=116.3, y=14, w=6.35, h=12)
text.getCharBoundaries(31) = (x=122.65, y=14, w=6.35, h=12)
text.getCharBoundaries(32) = (x=129, y=14, w=6.35, h=12)
text.getCharBoundaries(33) = (x=135.35, y=14, w=6.35, h=12)
text.getCharBoundaries(34) = (x=141.7, y=14, w=6.35, h=12)
text.getCharBoundaries(35) = (x=148.05, y=14, w=6.35, h=12)
text.getCharBoundaries(36) = (x=154.4, y=14, w=6.35, h=12)
text.getCharBoundaries(37) = (x=160.75, y=14, w=6.35, h=12)
text.getCharBoundaries(38) = (x=167.1, y=14, w=6.35, h=12)
text.getCharBoundaries(39) = (x=173.45, y=14, w=6.35, h=12)
text.getCharBoundaries(40) = (x=179.8, y=14, w=6.35, h=12)
text.getCharBoundaries(41) = (x=186.15, y=14, w=6.35, h=12)
text.getCharBoundaries(42) = (x=192.5, y=14, w=6.35, h=12)
text.getCharBoundaries(43) = (x=198.85, y=14, w=6.35, h=12)
text.getCharBoundaries(44) = (x=205.2, y=14, w=6.35, h=12)
text.getCharBoundaries(45) = (x=211.55, y=14, w=6.35, h=12)
text.getCharBoundaries(46) = (x=217.9, y=14, w=6.35, h=12)
text.getCharBoundaries(47) = (x=224.25, y=14, w=6.35, h=12)
text.getCharBoundaries(48) = (x=230.6, y=14, w=6.35, h=12)
text.getCharBoundaries(49) = (x=236.95, y=14, w=6.35, h=12)
text.getCharBoundaries(50) = (x=243.3, y=14, w=6.35, h=12)
text.getCharBoundaries(51) = (x=249.65, y=14, w=6.35, h=12)
text.getCharBoundaries(52) = (x=256, y=14, w=6.35, h=12)
text.getCharBoundaries(53) = (x=262.35, y=14, w=6.35, h=12)
text.getCharBoundaries(54) = (x=268.7, y=14, w=6.35, h=12)
text.getCharBoundaries(55) = (x=275.05, y=14, w=6.35, h=12)
text.getCharBoundaries(56) = (x=281.4, y=14, w=6.35, h=12)
text.getCharBoundaries(57) = (x=287.75, y=14, w=6.35, h=12)
text.getCharBoundaries(58) = (x=294.1, y=14, w=6.35, h=12)
text.getCharBoundaries(59) = (x=300.45, y=14, w=6.35, h=12)
text.getCharBoundaries(60) = (x=306.8, y=14, w=6.35, h=12)
text.getCharBoundaries(61) = (x=313.15, y=14, w=6.35, h=12)
text.getCharBoundaries(62) = (x=319.5, y=14, w=6.35, h=12)
text.getCharBoundaries(63) = (x=325.85, y=14, w=6.35, h=12)
text.getCharBoundaries(64) = null
text.getCharBoundaries(65) = null
Binary file modified tests/tests/swfs/avm2/edittext_getcharboundaries/test.swf
Binary file not shown.