Further improve RTL /ActualText handling in CPDF_TextPage

Refactor CPDF_TextPage::ProcessMarkedContent() to return a boolean
indicating whether marked content text is RTL or preceded by an RTL text
object. Then call ReverseTempTextBufs() in its caller to reorder if
needed to ensure /ActualText characters are processed in logical forward
order. This better aligns the ProcessMarkedContent() behavior with the
ProcessTextObjectItems() behavior. Then update
FPDFTextEmbedderTest.ActualTextRtl test expectations to match.

TAG=agy
CONV=117163e4-c12b-43d7-9709-b718d4450d3e

Bug: 525087036
Change-Id: Ia87c2ff5f52f056e73abea7b0245999ff0c53131
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/152930
Reviewed-by: Andy Phan <andyphan@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/core/fpdftext/cpdf_textpage.cpp b/core/fpdftext/cpdf_textpage.cpp
index 50647ac..8b02e87 100644
--- a/core/fpdftext/cpdf_textpage.cpp
+++ b/core/fpdftext/cpdf_textpage.cpp
@@ -995,7 +995,7 @@
   return MarkedContentState::kDelay;
 }
 
-void CPDF_TextPage::ProcessMarkedContent(const TransformedTextObject& obj) {
+bool CPDF_TextPage::ProcessMarkedContent(const TransformedTextObject& obj) {
   CPDF_TextObject* const text_obj = obj.text_obj_;
   const CPDF_ContentMarks* marks = text_obj->GetContentMarks();
   const size_t content_marks_count = marks->CountItems();
@@ -1008,10 +1008,14 @@
     }
   }
   if (actual_text.IsEmpty()) {
-    return;
+    return false;
   }
 
-  const bool is_rtl = IsRightToLeft(*text_obj);
+  const bool is_rtl =
+      IsRightToLeft(*text_obj) ||
+      (prev_text_obj_ && IsRightToLeft(*prev_text_obj_) &&
+       CFX_BidiString(actual_text, /*auto_order=*/true).OverallDirection() ==
+           CFX_BidiChar::Direction::kRight);
   CFX_Matrix matrix = text_obj->GetTextMatrix() * obj.form_matrix_;
   CFX_FloatRect rect = text_obj->GetRect();
   float step = 0;
@@ -1041,6 +1045,7 @@
         CharInfo(CharType::kActualText, CPDF_Font::kInvalidCharCode, wc,
                  text_obj->GetPos(), char_box, matrix, text_obj));
   }
+  return is_rtl;
 }
 
 void CPDF_TextPage::FindPreviousTextObject() {
@@ -1100,8 +1105,16 @@
       curline_rect_ = text_obj->GetRect();
     }
 
+    // Save these before ProcessMarkedContent() or ProcessTextObjectItems()
+    // modifies the containers.
+    const size_t orig_char_list_index = temp_char_list_.size();
+    const size_t orig_buf_index = temp_text_buf_.GetLength();
     if (ePreMKC == MarkedContentState::kDelay) {
-      ProcessMarkedContent(obj);
+      if (ProcessMarkedContent(obj)) {
+        // TODO(thestig): It would be nicer if there was a way to avoid doing
+        // this second reversal.
+        ReverseTempTextBufs(orig_char_list_index, orig_buf_index);
+      }
       prev_text_obj_ = text_obj;
       prev_matrix_ = form_matrix;
       continue;
@@ -1111,10 +1124,6 @@
     prev_matrix_ = form_matrix;
 
     const CFX_Matrix matrix = text_obj->GetTextMatrix() * form_matrix;
-    // Save these before ProcessTextObjectItems() modifies the containers.
-    const size_t orig_char_list_index = temp_char_list_.size();
-    const size_t orig_buf_index = temp_text_buf_.GetLength();
-
     if (ProcessTextObjectItems(text_obj, form_matrix, matrix)) {
       ReverseTempTextBufs(orig_char_list_index, orig_buf_index);
     }
diff --git a/core/fpdftext/cpdf_textpage.h b/core/fpdftext/cpdf_textpage.h
index be83cc9..e941a19 100644
--- a/core/fpdftext/cpdf_textpage.h
+++ b/core/fpdftext/cpdf_textpage.h
@@ -173,7 +173,11 @@
                         CPDF_TextObject* text_obj2) const;
   void CloseTempLine();
   MarkedContentState PreMarkedContent(const CPDF_TextObject* text_obj);
-  void ProcessMarkedContent(const TransformedTextObject& obj);
+  // Processes `/ActualText` marked content for `obj` and appends character info
+  // and text to `temp_char_list_` and `temp_text_buf_`.
+  // Returns true if the caller needs to call ReverseTempTextBufs() because the
+  // text is RTL, requiring the newly appended character order to be reversed.
+  bool ProcessMarkedContent(const TransformedTextObject& obj);
   void FindPreviousTextObject();
   void AddCharInfo(wchar_t wc, const CharInfo& info, bool is_rtl);
   TextOrientation GetTextObjectWritingMode(
diff --git a/fpdfsdk/fpdf_text_embeddertest.cpp b/fpdfsdk/fpdf_text_embeddertest.cpp
index f55d823..89db486 100644
--- a/fpdfsdk/fpdf_text_embeddertest.cpp
+++ b/fpdfsdk/fpdf_text_embeddertest.cpp
@@ -2564,9 +2564,8 @@
        // מים (logical order):
        0x05de, 0x05d9, 0x05dd, '\r', '\n',
        // Literal RTL text followed by RTL /ActualText case:
-       // TODO(crbug.com/525087036): Should be in logical order.
-       // םולש (/ActualText):
-       0x05dd, 0x05d5, 0x05dc, 0x05e9,
+       // שלום (/ActualText, logical order):
+       0x05e9, 0x05dc, 0x05d5, 0x05dd,
        // בן (literal text):
        0x05d1, 0x05df, '\0'});
   static constexpr int kExpectedTextSize = std::size(kExpectedText);