Fix FPDFText_GetLooseCharBox() to handle rotation
Change the cpdf_textpage.cpp code that implements the character box
calculations used by FPDFText_GetLooseCharBox() to properly handle the
transformation matrix. Then FPDFText_GetLooseCharBox() will no longer
return dimensions of 0 when matrix.a is 0, and it will no longer return
negative dimensions when the text is flipped.
Bug: 42270642
Change-Id: Iab85cbf64cd1f8266ab20b63ad35bddefd05733c
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/127995
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Thomas Sepez <tsepez@google.com>
diff --git a/core/fpdftext/cpdf_textpage.cpp b/core/fpdftext/cpdf_textpage.cpp
index 518b744..6d982d4 100644
--- a/core/fpdftext/cpdf_textpage.cpp
+++ b/core/fpdftext/cpdf_textpage.cpp
@@ -290,15 +290,18 @@
int ascent = charinfo.text_object()->GetFont()->GetTypeAscent();
int descent = charinfo.text_object()->GetFont()->GetTypeDescent();
if (ascent != descent) {
- float width = charinfo.matrix().a *
- charinfo.text_object()->GetCharWidth(charinfo.char_code());
- float font_scale = charinfo.matrix().a * font_size / (ascent - descent);
+ float width = charinfo.text_object()->GetCharWidth(charinfo.char_code());
+ float font_scale = font_size / (ascent - descent);
- float left = charinfo.origin().x;
- float right = charinfo.origin().x + (is_vert_writing ? -width : width);
- float bottom = charinfo.origin().y + descent * font_scale;
- float top = charinfo.origin().y + ascent * font_scale;
- return CFX_FloatRect(left, bottom, right, top);
+ CFX_Matrix inverse_matrix = charinfo.matrix().GetInverse();
+ CFX_PointF original_origin = inverse_matrix.Transform(charinfo.origin());
+
+ float left = original_origin.x;
+ float right = original_origin.x + (is_vert_writing ? -width : width);
+ float bottom = original_origin.y + descent * font_scale;
+ float top = original_origin.y + ascent * font_scale;
+ CFX_FloatRect char_box(left, bottom, right, top);
+ return charinfo.matrix().TransformRect(char_box);
}
}
diff --git a/fpdfsdk/fpdf_text_embeddertest.cpp b/fpdfsdk/fpdf_text_embeddertest.cpp
index fde1b18..92ac221 100644
--- a/fpdfsdk/fpdf_text_embeddertest.cpp
+++ b/fpdfsdk/fpdf_text_embeddertest.cpp
@@ -1736,7 +1736,7 @@
static constexpr double kExpectedCharWidth = 8.460;
static constexpr double kExpectedCharHeight = 6.600;
static constexpr float kExpectedLooseCharWidth = 8.664f;
- static constexpr float kExpectedLooseCharHeight = 12.0f;
+ static constexpr float kExpectedLooseCharHeight = 10.0f;
ASSERT_TRUE(OpenDocument("font_matrix.pdf"));
ScopedEmbedderTestPage page = LoadScopedPage(0);
@@ -1833,25 +1833,24 @@
EXPECT_NEAR(10.055, top - bottom, 0.001);
// Check the loose character box size.
- // TODO(crbug.com/42270642): Sizes should be bigger than the
- // FPDFText_GetCharBox() sizes, and certainly not negative.
+ static constexpr float kExpectedLooseCharDimension = 14.612f;
FS_RECTF rect;
ASSERT_TRUE(FPDFText_GetLooseCharBox(
text_page.get(), GetRotatedTextFirstCharIndexForQuadrant(0), &rect));
- EXPECT_NEAR(6.126f, rect.right - rect.left, 0.001f);
- EXPECT_NEAR(8.485f, rect.top - rect.bottom, 0.001f);
+ EXPECT_NEAR(kExpectedLooseCharDimension, rect.right - rect.left, 0.001f);
+ EXPECT_NEAR(kExpectedLooseCharDimension, rect.top - rect.bottom, 0.001f);
ASSERT_TRUE(FPDFText_GetLooseCharBox(
text_page.get(), GetRotatedTextFirstCharIndexForQuadrant(1), &rect));
- EXPECT_NEAR(-6.126f, rect.right - rect.left, 0.001f);
- EXPECT_NEAR(-8.485f, rect.top - rect.bottom, 0.001f);
+ EXPECT_NEAR(kExpectedLooseCharDimension, rect.right - rect.left, 0.001f);
+ EXPECT_NEAR(kExpectedLooseCharDimension, rect.top - rect.bottom, 0.001f);
ASSERT_TRUE(FPDFText_GetLooseCharBox(
text_page.get(), GetRotatedTextFirstCharIndexForQuadrant(2), &rect));
- EXPECT_NEAR(-6.126f, rect.right - rect.left, 0.001f);
- EXPECT_NEAR(-8.485f, rect.top - rect.bottom, 0.001f);
+ EXPECT_NEAR(kExpectedLooseCharDimension, rect.right - rect.left, 0.001f);
+ EXPECT_NEAR(kExpectedLooseCharDimension, rect.top - rect.bottom, 0.001f);
ASSERT_TRUE(FPDFText_GetLooseCharBox(
text_page.get(), GetRotatedTextFirstCharIndexForQuadrant(3), &rect));
- EXPECT_NEAR(6.126f, rect.right - rect.left, 0.001f);
- EXPECT_NEAR(8.485f, rect.top - rect.bottom, 0.001f);
+ EXPECT_NEAR(kExpectedLooseCharDimension, rect.right - rect.left, 0.001f);
+ EXPECT_NEAR(kExpectedLooseCharDimension, rect.top - rect.bottom, 0.001f);
}
TEST_F(FPDFTextEmbedderTest, CharBoxForRotated90DegreesText) {
@@ -1903,25 +1902,25 @@
EXPECT_NEAR(8.604, top - bottom, 0.001);
// Check the loose character box size.
- // TODO(crbug.com/42270642): Sizes should be bigger than the
- // FPDFText_GetCharBox() sizes, and certainly not negative nor zero.
+ static constexpr float kExpectedLooseCharWidth = 8.664f;
+ static constexpr float kExpectedLooseCharHeight = 12.0f;
FS_RECTF rect;
ASSERT_TRUE(FPDFText_GetLooseCharBox(
text_page.get(), GetRotatedText90FirstCharIndexForQuadrant(0), &rect));
- EXPECT_NEAR(8.664f, rect.right - rect.left, 0.001f);
- EXPECT_NEAR(12.0f, rect.top - rect.bottom, 0.001f);
+ EXPECT_NEAR(kExpectedLooseCharWidth, rect.right - rect.left, 0.001f);
+ EXPECT_NEAR(kExpectedLooseCharHeight, rect.top - rect.bottom, 0.001f);
ASSERT_TRUE(FPDFText_GetLooseCharBox(
text_page.get(), GetRotatedText90FirstCharIndexForQuadrant(1), &rect));
- EXPECT_NEAR(0.0f, rect.right - rect.left, 0.001f);
- EXPECT_NEAR(0.0f, rect.top - rect.bottom, 0.001f);
+ EXPECT_NEAR(kExpectedLooseCharHeight, rect.right - rect.left, 0.001f);
+ EXPECT_NEAR(kExpectedLooseCharWidth, rect.top - rect.bottom, 0.001f);
ASSERT_TRUE(FPDFText_GetLooseCharBox(
text_page.get(), GetRotatedText90FirstCharIndexForQuadrant(2), &rect));
- EXPECT_NEAR(-8.664f, rect.right - rect.left, 0.001f);
- EXPECT_NEAR(-12.0f, rect.top - rect.bottom, 0.001f);
+ EXPECT_NEAR(kExpectedLooseCharWidth, rect.right - rect.left, 0.001f);
+ EXPECT_NEAR(kExpectedLooseCharHeight, rect.top - rect.bottom, 0.001f);
ASSERT_TRUE(FPDFText_GetLooseCharBox(
text_page.get(), GetRotatedText90FirstCharIndexForQuadrant(3), &rect));
- EXPECT_NEAR(0.0f, rect.right - rect.left, 0.001f);
- EXPECT_NEAR(0.0f, rect.top - rect.bottom, 0.001f);
+ EXPECT_NEAR(kExpectedLooseCharHeight, rect.right - rect.left, 0.001f);
+ EXPECT_NEAR(kExpectedLooseCharWidth, rect.top - rect.bottom, 0.001f);
}
TEST_F(FPDFTextEmbedderTest, SmallType3Glyph) {