Menu

#1435 Scintilla5 in Notepad++: Text is mirrored in RTL

Won't_Implement
closed
nobody
5
2022-05-22
2022-04-07
Yaron
No

STR:
Paste Hello in Notepad++.
Change direction to RTL.

Result:
The text is mirrored.

https://github.com/notepad-plus-plus/notepad-plus-plus/blob/b4a58429c3c8f7fe3250aa4a0e57743c2bceaf3a/PowerEditor/src/NppCommands.cpp#L3530

Does not fix the issue with Scintilla5.

Discussion

  • Neil Hodgson

    Neil Hodgson - 2022-04-07

    Scintilla only (experimentally) supports bidirectional text with the SCI_SETBIDIRECTIONAL API.

    I suspect what you are seeing is Notepad++ setting a text direction with WS_EX_LAYOUTRTL or WS_EX_RTLREADING or similar on a Scintilla window. Scintilla has never actively supported this and any past success with these modes was accidental. May have worked with GDI drawing but not DirectWrite.

     
  • rdipardo

    rdipardo - 2022-04-07

    I suspect what you are seeing is Notepad++ setting a text direction with WS_EX_LAYOUTRTL . . .

    Browsing commit 7fa6fb083b0 confirms that is the case:

    void ScintillaEditView::changeTextDirection(bool isRTL)
    {
        long exStyle = static_cast<long>(::GetWindowLongPtr(_hSelf, GWL_EXSTYLE));
        exStyle = isRTL ? (exStyle | WS_EX_LAYOUTRTL) : (exStyle & (~WS_EX_LAYOUTRTL));
        ::SetWindowLongPtr(_hSelf, GWL_EXSTYLE, exStyle);
        // ...
    }
    
     
  • Zufu Liu

    Zufu Liu - 2022-04-07

    That does not work as expected, see https://github.com/zufuliu/notepad2/issues/392

     
  • Yaron

    Yaron - 2022-04-08

    Neil,

    Thank you for the detailed and useful reply.
    I'd like to thank you also for the Scintilla project. I appreciate your work.

    Please consider this thread as another request for a better RTL support. :)


    Robert & Zufu,

    Thank you for the additional info.

     
  • Neil Hodgson

    Neil Hodgson - 2022-04-08
    • labels: --> scintilla, bidirectional
    • Group: Initial --> Won't_Implement
     
  • Neil Hodgson

    Neil Hodgson - 2022-04-08

    I don't think this is describing a feature that can be implemented.

     
  • Neil Hodgson

    Neil Hodgson - 2022-04-09

    May be fixed with [fa80f5].

     

    Related

    Commit: [fa80f5]

  • Zufu Liu

    Zufu Liu - 2022-04-10

    It seems adding a 90 degrees transform (in ScintillaWin::EnsureRenderTarget()) will make Technology::DirectWriteDC rendered into same thing as GDI.

    // test code, need to load D2D1MakeRotateMatrix() and check window layout
    #pragma comment(lib, "D2d1.lib")
    pRenderTarget->SetTransform(D2D1::Matrix3x2F::Rotation(90.0f, D2D1::Point2F(0.0f, 0.0f)));
    

    Edit: this does work, nothing is rendered.

     

    Last edit: Zufu Liu 2022-04-10
    • Neil Hodgson

      Neil Hodgson - 2022-04-21

      A 180 degree rotation is more likely wanted than 90 but that inverts the y axis.

      This code inverts x for Technology::DirectWriteDC but then the text is no longer on the right:

      D2D1::Matrix3x2F invertX = D2D1::Matrix3x2F(-1, 0, 0, 1, 0, 0);
      D2D1::Matrix3x2F moveX = D2D1::Matrix3x2F::Translation(rc.right - rc.left, 0);
      pRenderTarget->SetTransform(invertX * moveX);
      

      For WS_EX_LAYOUTRTL the IDWriteTextFormat::SetReadingDirection API is likely the key.
      https://docs.microsoft.com/en-us/windows/win32/directwrite/how-to-ensure-text-is-displayed-with-the-correct-reading-direction

      Code from Raghda's bidi work:

       void SurfaceD2D::DrawTextCommon(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text, UINT fuOptions) {
          SetFont(font_);
      
      @@ -1542,6 +1994,11 @@
                  D2D1_RECT_F rcClip = {rc.left, rc.top, rc.right, rc.bottom};
                  pRenderTarget->PushAxisAlignedClip(rcClip, D2D1_ANTIALIAS_MODE_ALIASED);
              }
      +       if (bidiR2L) {
      +           pTextFormat->SetReadingDirection(DWRITE_READING_DIRECTION_RIGHT_TO_LEFT);
      +       } else {
      +           pTextFormat->SetReadingDirection(DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
      +       }
      
              // Explicitly creating a text layout appears a little faster
              IDWriteTextLayout *pTextLayout;
      
       
      • Zufu Liu

        Zufu Liu - 2022-04-21

        SetReadingDirection() has no effect.

        pfm->pTextFormat->SetReadingDirection(mode.bidiR2L ? DWRITE_READING_DIRECTION_RIGHT_TO_LEFT : DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
        

        however SetLayout(hdc, LAYOUT_BITMAPORIENTATIONPRESERVED); can be added into ScintillaWin::PaintDC() to make all DirectWrite technologies behaviors same (no RTL), which is documented at https://docs.microsoft.com/en-us/windows/win32/api/d2d1/nn-d2d1-id2d1dcrendertarget#id2d1dcrendertargets--gdi-transforms--and-right-to-left-language-builds-of-windows

         
        • Neil Hodgson

          Neil Hodgson - 2022-04-22

          SetReadingDirection inside SurfaceD2D::DrawTextCommon can only affect one lexeme.

          Here is a mixed language and mixed direction comment which is a single lexeme with DWRITE_READING_DIRECTION_LEFT_TO_RIGHT (top) and DWRITE_READING_DIRECTION_RIGHT_TO_LEFT (bottom):
          SetReadingDirection

           
          • Zufu Liu

            Zufu Liu - 2022-04-22

            move rotation local into SurfaceD2D::DrawTextCommon() has some effects for SC_TECHNOLOGY_DIRECTWRITEDC.

            const D2D1_POINT_2F origin = DPointFromPoint(Point(rc.left, ybase - pfm->yAscent));
            //DWRITE_TEXT_METRICS textMetrics{};
            //pTextLayout->GetMetrics(&textMetrics);
            //const FLOAT width = textMetrics.widthIncludingTrailingWhitespace + 24.0f;
            const FLOAT width = static_cast<FLOAT>(rc.Width());
            D2D1::Matrix3x2F invertX = D2D1::Matrix3x2F(-1, 0, 0, 1, 0, 0);
            D2D1::Matrix3x2F moveX = D2D1::Matrix3x2F::Translation(width, 0);
            pRenderTarget->SetTransform(invertX * moveX);
            //pRenderTarget->SetTransform(D2D1::Matrix3x2F::Rotation(180.0f, DPointFromPoint(rc.Centre())));
            //pRenderTarget->SetTransform(D2D1::Matrix3x2F::Rotation(180.0f, origin));
            pRenderTarget->DrawTextLayout(origin, pTextLayout, pBrush, d2dDrawTextOptions);
            ReleaseUnknown(pTextLayout);
            pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
            
             
  • Yaron

    Yaron - 2022-04-10

    @nyamatongwe,

    Thanks again for the fix. I appreciate it.

    @zufuliu,

    Thank you for the interesting info.

     
  • Neil Hodgson

    Neil Hodgson - 2022-05-22
    • status: open --> closed
     

Log in to post a comment.