Конвертировать LTR в RTL?

Как вы знаете, многие компоненты пользовательского интерфейса и инструменты разработки не поддерживают RTL, мы можем назвать это перелистыванием текста, потому что результатом является тот же пример:

LTR

سلام salam متن راهنما word

РТЛ

word متن راهنما salam  سلام

в любом случае, чтобы конвертировать этот LTR в RTL, я понятия не имею, и язык не имеет значения

На самом деле я ищу решение, чтобы сделать это в приложении RAD Studio Firemonkey, так как вы, возможно, знаете, что приложения Firemonkey не поддерживают RTL, оно находится в дорожной карте Rad Studio, но еще не реализовано

2 ответа

Решение

Под Windows вы можете сделать это через API UniScribe.

Я использовал это, чтобы преобразовать текст Unicode в набор глифов для нашего Open Source PDF Writer.

У вас есть пример исходного кода в модуле SynPdf.pas. Увидеть TPdfWrite.AddUnicodeHexTextUniScribe метод:

function TPdfWrite.AddUnicodeHexTextUniScribe(PW: PWideChar;
  WinAnsiTTF: TPdfFontTrueType; NextLine: boolean; Canvas: TPdfCanvas): boolean;
var L, i,j: integer;
    res: HRESULT;
    max, count, numSp: integer;
    Sp: PScriptPropertiesArray;
    W: PWideChar;
    items: array of TScriptItem;
    level: array of byte;
    VisualToLogical: array of integer;
    psc: pointer; // opaque Uniscribe font metric cache
    complex,R2L: boolean;
    complexs: array of byte;
    glyphs: array of TScriptVisAttr;
    glyphsCount: integer;
    OutGlyphs, LogClust: array of word;
procedure Append(i: Integer);
// local procedure used to add glyphs from items[i] to the PDF content stream
var L: integer;
    W: PWideChar;
procedure DefaultAppend;
var tmpU: array of WideChar;
begin
  SetLength(tmpU,L+1); // we need the text to be ending with #0
  move(W^,tmpU[0],L*2);
  AddUnicodeHexTextNoUniScribe(pointer(tmpU),WinAnsiTTF,false,Canvas);
end;
begin
  L := items[i+1].iCharPos-items[i].iCharPos; // length of this shapeable item
  if L=0 then
    exit; // nothing to append
  W := PW+items[i].iCharPos;
  if not GetBit(complexs[0],i) then begin
    // not complex items are rendered as fast as possible
    DefaultAppend;
    exit;
  end;
  res := ScriptShape(0,psc,W,L,max,@items[i].a,
    pointer(OutGlyphs),pointer(LogClust),pointer(glyphs),glyphsCount);
  case res of
    E_OUTOFMEMORY: begin // max was not big enough (should never happen)
      DefaultAppend;
      exit;
    end;
    E_PENDING, USP_E_SCRIPT_NOT_IN_FONT: begin // need HDC and a selected font object
      res := ScriptShape(Canvas.FDoc.GetDCWithFont(WinAnsiTTF),
        psc,W,L,max,@items[i].a,
        pointer(OutGlyphs),pointer(LogClust),pointer(glyphs),glyphsCount);
      if res<>0 then begin // we won't change font if necessary, sorry
        // we shall implement the complex technic as stated by
        // http://msdn.microsoft.com/en-us/library/dd374105(v=VS.85).aspx
        DefaultAppend;
        exit;
      end;
    end;
    0: ; // success -> will add glyphs just below
    else exit;
  end;
  // add glyphs to the PDF content
  // (NextLine has already been handled: not needed here)
  AddGlyphs(pointer(OutGlyphs),glyphsCount,Canvas);
end;
begin
  result := false; // on UniScribe error, handle as Unicode
  // 1. Breaks a Unicode string into individually shapeable items
  L := StrLenW(PW)+1; // include last #0
  max := L+2; // should be big enough
  SetLength(items,max);
  count := 0;
  if ScriptItemize(PW,L,max,nil,nil,pointer(items),count)<>0 then
    exit; // error trying processing Glyph Shaping -> fast return
  // 2. guess if requiring glyph shaping or layout
  SetLength(complexs,(count shr 3)+1);
  ScriptGetProperties(sP,numSp);
  complex := false;
  R2L := false;
  for i := 0 to Count-2 do // don't need Count-1 = Terminator
    if fComplex in sP^[items[i].a.eScript and (1 shl 10-1)]^.fFlags then begin
      complex := true;
      SetBit(complexs[0],i);
    end else
      if fRTL in items[i].a.fFlags then
        R2L := true;
  if not complex then begin
    // no glyph shaping -> fast append as normal Unicode Text
    if R2L then begin
      // handle Right To Left but not Complex text
      W := pointer(items); // there is enough temp space in items[]
      W[L] := #0;
      dec(L);
      for i := 0 to L do
        W[i] := PW[L-i];
      AddUnicodeHexTextNoUniScribe(W,WinAnsiTTF,NextLine,Canvas);
      result := true; // mark handled here
    end;
    exit;
  end;
  // 3. get Visual Order, i.e. how to render the content from left to right
  SetLength(level,count);
  for i := 0 to Count-1 do
    level[i] := items[i].a.s.uBidiLevel;
  SetLength(VisualToLogical,count);
  if ScriptLayout(Count,pointer(level),pointer(VisualToLogical),nil)<>0 then
    exit;
  // 4. now we have enough information to start drawing
  result := true;
  if NextLine then
    Canvas.MoveToNextLine; // manual NextLine handling
  // 5. add glyphs for all shapeable items
  max := (L*3)shr 1+32; // should be big enough - allocate only once
  SetLength(glyphs,max);
  SetLength(OutGlyphs,max);
  SetLength(LogClust,max);
  psc := nil; // cached for the same character style used
  if Canvas.RightToLeftText then
    // append from right to left visual order
    for j := Count-2 downto 0 do // Count-2: ignore last ending item
      Append(VisualToLogical[j]) else
    // append from left to right visual order
    for j := 0 to Count-2 do // Count-2: ignore last ending item
      Append(VisualToLogical[j]);
end;

Конечно, это только под Windows. Так что он не будет работать на Mac OS X. Вам придется использовать другую библиотеку под Mac OS X...

Это сложно. Если вы хотите сделать это правильно, вы должны использовать Библиотеку Bidi из International Components for Unicode.

Если вы используете MFC, вот как установить как правое направление, так и выравнивание. Предполагая, что ваш элемент управления CEdit называется m_TextEdit:

void MyDialog::SetLangDirection(bool RTL)
{
    DWORD w_dwStyle;

    w_dwStyle = GetWindowLong(m_TextEdit.GetSafeHwnd(), GWL_EXSTYLE);

    if (RTL)
    {
        w_dwStyle -= WS_EX_LEFT | WS_EX_LTRREADING;
        w_dwStyle |= WS_EX_RIGHT | WS_EX_RTLREADING;
    }
    else
    {
        w_dwStyle -= WS_EX_RIGHT | WS_EX_RTLREADING;
        w_dwStyle |= WS_EX_LEFT | WS_EX_LTRREADING;
    }

    SetWindowLong(m_TextEdit.GetSafeHwnd(), GWL_EXSTYLE, w_dwStyle);
}

Смотрите мой совет.

Другие вопросы по тегам