Get the rotation angle of a character.
Leveraging the transformation matrix attribute of a character, we can
easily find the angle by which it is rotated. The angle is used in a
separate change on Chromium. For more information, see
https://chromium-review.googlesource.com/c/chromium/src/+/1718989
Bug: chromium:985604
Change-Id: Ifc2e46f0e1eeca8f1e10218f0d1b8ff9fcef3d29
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/58233
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
diff --git a/fpdfsdk/fpdf_text.cpp b/fpdfsdk/fpdf_text.cpp
index cc7ca5b..2421ea2 100644
--- a/fpdfsdk/fpdf_text.cpp
+++ b/fpdfsdk/fpdf_text.cpp
@@ -121,6 +121,27 @@
return length;
}
+FPDF_EXPORT double FPDF_CALLCONV FPDFText_GetCharAngle(FPDF_TEXTPAGE text_page,
+ int index) {
+ CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
+ if (!textpage)
+ return -1;
+
+ FPDF_CHAR_INFO charinfo;
+ textpage->GetCharInfo(index, &charinfo);
+ // On the left is our current Matrix and on the right a generic rotation
+ // matrix for our coordinate space.
+ // | a b 0 | | cos(t) -sin(t) 0 |
+ // | c d 0 | | sin(t) cos(t) 0 |
+ // | e f 1 | | 0 0 1 |
+ // Calculate the angle of the vector
+ double angle = atan2(charinfo.m_Matrix.c, charinfo.m_Matrix.a);
+ if (angle < 0)
+ angle = 2 * FX_PI + angle;
+
+ return angle;
+}
+
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFText_GetCharBox(FPDF_TEXTPAGE text_page,
int index,
double* left,
diff --git a/fpdfsdk/fpdf_text_embeddertest.cpp b/fpdfsdk/fpdf_text_embeddertest.cpp
index e211d2d..521831e 100644
--- a/fpdfsdk/fpdf_text_embeddertest.cpp
+++ b/fpdfsdk/fpdf_text_embeddertest.cpp
@@ -1213,3 +1213,41 @@
FPDFText_ClosePage(text_page);
UnloadPage(page);
}
+
+TEST_F(FPDFTextEmbedderTest, GetCharAngle) {
+ ASSERT_TRUE(OpenDocument("rotated_text.pdf"));
+ FPDF_PAGE page = LoadPage(0);
+ ASSERT_TRUE(page);
+
+ FPDF_TEXTPAGE text_page = FPDFText_LoadPage(page);
+ ASSERT_TRUE(text_page);
+
+ static constexpr int kSubstringsSize[] = {FX_ArraySize("Hello,"),
+ FX_ArraySize(" world!\r\n"),
+ FX_ArraySize("Goodbye,")};
+
+ // -1 for CountChars not including the \0, but +1 for the extra control
+ // character.
+ EXPECT_EQ(kHelloGoodbyeTextSize, FPDFText_CountChars(text_page));
+
+ EXPECT_EQ(-1, FPDFText_GetCharAngle(nullptr, 0));
+ EXPECT_EQ(-1, FPDFText_GetCharAngle(text_page, -1));
+ EXPECT_EQ(-1, FPDFText_GetCharAngle(text_page, kHelloGoodbyeTextSize + 1));
+
+ // Test GetCharAngle for every quadrant
+ EXPECT_NEAR(FX_PI / 4.0, FPDFText_GetCharAngle(text_page, 0), 0.001);
+ EXPECT_NEAR(3 * FX_PI / 4.0,
+ FPDFText_GetCharAngle(text_page, kSubstringsSize[0]), 0.001);
+ EXPECT_NEAR(
+ 5 * FX_PI / 4.0,
+ FPDFText_GetCharAngle(text_page, kSubstringsSize[0] + kSubstringsSize[1]),
+ 0.001);
+ EXPECT_NEAR(
+ 7 * FX_PI / 4.0,
+ FPDFText_GetCharAngle(text_page, kSubstringsSize[0] + kSubstringsSize[1] +
+ kSubstringsSize[2]),
+ 0.001);
+
+ FPDFText_ClosePage(text_page);
+ UnloadPage(page);
+}
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
index a9897ee..9f58b8b 100644
--- a/fpdfsdk/fpdf_view_c_api_test.c
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -324,6 +324,7 @@
CHK(FPDFText_FindPrev);
CHK(FPDFText_FindStart);
CHK(FPDFText_GetBoundedText);
+ CHK(FPDFText_GetCharAngle);
CHK(FPDFText_GetCharBox);
CHK(FPDFText_GetCharIndexAtPos);
CHK(FPDFText_GetCharOrigin);
diff --git a/public/fpdf_text.h b/public/fpdf_text.h
index 7bd84bf..63793bb 100644
--- a/public/fpdf_text.h
+++ b/public/fpdf_text.h
@@ -112,6 +112,20 @@
unsigned long buflen,
int* flags);
+// Experimental API.
+// Function: FPDFText_GetCharAngle
+// Get character rotation angle.
+// Parameters:
+// text_page - Handle to a text page information structure.
+// Returned by FPDFText_LoadPage function.
+// index - Zero-based index of the character.
+// Return Value:
+// On success, return the angle value in radian. Value will always be
+// greater or equal to 0. If |text_page| is invalid, or if |index| is
+// out of bounds, then return -1.
+FPDF_EXPORT double FPDF_CALLCONV FPDFText_GetCharAngle(FPDF_TEXTPAGE text_page,
+ int index);
+
// Function: FPDFText_GetCharBox
// Get bounding box of a particular character.
// Parameters:
diff --git a/testing/resources/rotated_text.in b/testing/resources/rotated_text.in
new file mode 100644
index 0000000..6cd5b23
--- /dev/null
+++ b/testing/resources/rotated_text.in
@@ -0,0 +1,58 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /MediaBox [ 0 0 200 200 ]
+ /Count 1
+ /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 4 0 R
+ >>
+ >>
+ /Contents 5 0 R
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Times-Roman
+>>
+endobj
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+BT
+0 0 Td
+/F1 12 Tf
+0.70710678118 -0.70710678118 0.70710678118 0.70710678118 100 100 Tm
+(Hello,) Tj
+0 0 Td
+/F1 12 Tf
+-0.70710678118 -0.70710678118 0.70710678118 -0.70710678118 100 100 Tm
+( world!\r\n) Tj
+0 0 Td
+/F1 12 Tf
+-0.70710678118 0.70710678118 -0.70710678118 -0.70710678118 100 100 Tm
+(Goodbye,) Tj
+0 0 Td
+/F1 12 Tf
+0.70710678118 0.70710678118 -0.70710678118 0.70710678118 100 100 Tm
+( world!) Tj
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/rotated_text.pdf b/testing/resources/rotated_text.pdf
new file mode 100644
index 0000000..d6d8db8
--- /dev/null
+++ b/testing/resources/rotated_text.pdf
@@ -0,0 +1,70 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /MediaBox [ 0 0 200 200 ]
+ /Count 1
+ /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 4 0 R
+ >>
+ >>
+ /Contents 5 0 R
+>>
+endobj
+4 0 obj <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Times-Roman
+>>
+endobj
+5 0 obj <<
+ /Length 406
+>>
+stream
+BT
+0 0 Td
+/F1 12 Tf
+0.70710678118 -0.70710678118 0.70710678118 0.70710678118 100 100 Tm
+(Hello,) Tj
+0 0 Td
+/F1 12 Tf
+-0.70710678118 -0.70710678118 0.70710678118 -0.70710678118 100 100 Tm
+( world!\r\n) Tj
+0 0 Td
+/F1 12 Tf
+-0.70710678118 0.70710678118 -0.70710678118 -0.70710678118 100 100 Tm
+(Goodbye,) Tj
+0 0 Td
+/F1 12 Tf
+0.70710678118 0.70710678118 -0.70710678118 0.70710678118 100 100 Tm
+( world!) Tj
+ET
+endstream
+endobj
+xref
+0 6
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000161 00000 n
+0000000287 00000 n
+0000000365 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 6
+>>
+startxref
+823
+%%EOF