Ignore:
Timestamp:
Feb 25, 2019, 8:34:42 PM (6 years ago)
Author:
ataylor
Message:

Experimental new logic to try and fix DBCS text wrapping.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Components/RichTextLayoutUnit.pas

    r395 r418  
    7373
    7474    FRichTextSettings: TRichTextSettings;
     75
     76    Codepage:    ULong;       // ALT
    7577
    7678    // Drawing functions
     
    169171var
    170172  DefaultFontSpec: TFontSpec;
     173  CpSize:          ULong;       // ALT
    171174Begin
    172175  Inherited Create;
     
    199202                       DefaultFontSpec );
    200203  FFontManager.DefaultFontSpec := DefaultFontSpec;
     204
     205  DosQueryCp( sizeof( Codepage ), Codepage, CpSize );       // ALT
    201206
    202207  Layout;
     
    284289  NextP: PChar;
    285290  NextP2: PChar;
     291  NextP3: PChar;
    286292
    287293  WordStart: PChar;
     
    307313
    308314  DoWrap: boolean;
     315
     316  InsideDBC:   Boolean;     // ALT
    309317
    310318  // Nested procedure
     
    383391  WordStarted := false;
    384392  DisplayedCharsSinceFontChange := false;
     393  InsideDBC := false;
    385394
    386395  repeat
    387396    CurrentElement := ExtractNextTextElement( P, NextP );
    388397    assert( NextP > P );
     398    CheckSpecialElementType( CurrentElement.Character, CurrentElement.ElementType, InsideDBC, Codepage );   // ALT
    389399
    390400    OnBreak := false;
     
    395405        CurrentCharWidth := FFontManager.CharWidth( ' ' );
    396406        OnBreak := true;
     407        InsideDBC := false;
    397408      end;
    398409
     
    404415        WordStart := NextP;
    405416        WordX := 0;
     417        InsideDBC := false;
    406418
    407419        P := NextP;
     
    413425      begin
    414426        DoLine( P, NextP, WordStartX + WordX );
     427        InsideDBC := false;
    415428
    416429        // end of text, done
     
    446459        CurrentCharWidth := FFontManager.CharWidth( CurrentElement.Character );
    447460        WordStarted := true;
    448       end;
     461        InsideDBC := false;
     462      end;
     463
     464      // ALT begins
     465      //
     466      teWrapChar:
     467      begin
     468        // This is a legal break character, but not a space (so we still display it).
     469        CurrentCharWidth := FFontManager.CharWidth( CurrentElement.Character );
     470
     471        // Treat as the start of a new word (for the sake of wrapping).
     472        WordStarted := true;
     473        inc( WordStartX, WordX + CurrentCharWidth );
     474        WordX := 0;
     475        WordStart := NextP;
     476      end;
     477
     478      teLeadByte:
     479      begin
     480        // Leading byte of a double-byte character.
     481        // Get the complete character width for our wrapping calculations.
     482        if ( NextP > P ) then
     483          CurrentCharWidth := FFontManager.CJKTextWidth( 2, P )
     484        else
     485          CurrentCharWidth := FFontManager.CJKCharWidth;
     486        WordStarted := true;
     487      end;
     488
     489      teSecondByte:
     490      begin
     491        // Secondary byte of a double-byte character.
     492        // The full character width was already assigned to the leading byte.
     493        CurrentCharWidth := 0;
     494
     495        // We treat each double-byte character as a complete word for the sake
     496        // of the wrapping algorithm.
     497        inc( LineWordsCompleted );
     498        WordStarted := true;
     499        inc( WordStartX, WordX + CurrentCharWidth );
     500        WordX := 0;
     501        WordStart := NextP;
     502      end;
     503      //
     504      // ALT ends
    449505
    450506      teStyle:
    451507      begin
     508        InsideDBC := false;
    452509        case CurrentElement.Tag.TagType of
    453510          ttBeginLink:
     
    582639       + WordX
    583640       + CurrentCharWidth
    584        >= WrapX then
     641       >= WrapX  then
    585642    begin
    586643      // reached right hand side before finding end of word
     
    588645        // always wrap after at least one word displayed
    589646        DoWrap := true
    590       else if not FRichTextSettings.AtLeastOneWordBeforeWrap then
     647
     648      else if ( CurrentElement.ElementType = teWrapChar ) or
     649              ( CurrentElement.ElementType = teLeadByte ) then
     650        DoWrap := true                                                  // ALT
     651
     652      else if ( not FRichTextSettings.AtLeastOneWordBeforeWrap ) then
    591653        // only wrap during the first word, if the "at least 1 word" flag is not set.
    592654        DoWrap := true;
     
    606668
    607669          NextElement := ExtractNextTextElement( NextP, NextP2 );
     670
     671          // ALT
     672          if InsideDBC then
     673          begin
     674            // we're in the middle of a double-byte character, so keep the next byte too
     675            InsideDBC := false;
     676            NextP := NextP2;
     677            NextElement := ExtractNextTextElement( NextP2, NextP3 );
     678            NextP2 := NextP3;
     679          end;
     680          // /ALT
     681
    608682          if NextElement.ElementType <> teLineBreak then
    609683            // there is still more on the line...
     
    645719        CurrentLine.Wrapped := true;
    646720
    647         // take the width of the last space of the
    648         // previous word off the line width
    649         DoLine( WordStart, // current line ends at start of this word
    650                 WordStart, // next line starts at start of this word
    651                 WordStartX - FFontManager.CharWidth( ' ' ) );
    652         if CurrentElement.ElementType = teImage then
    653           if Bitmap <> nil then
    654             if BitmapHeight > CurrentLine.Height then
    655               CurrentLine.Height := BitmapHeight;
     721
     722        if ( CurrentElement.ElementType = teLeadByte ) or
     723           ( CurrentElement.ElementType = teWrapChar ) then     // ALT
     724        begin
     725          // draw up to but not including this 'word' (ALT)
     726          DoLine( WordStart,
     727                  WordStart,
     728                  WordStartX );
     729        end
     730        else
     731        begin                                                   // ALT
     732          // take the width of the last space of the
     733          // previous word off the line width
     734          DoLine( WordStart, // current line ends at start of this word
     735                  WordStart, // next line starts at start of this word
     736                  WordStartX - FFontManager.CharWidth( ' ' ) );
     737          if CurrentElement.ElementType = teImage then
     738            if Bitmap <> nil then
     739              if BitmapHeight > CurrentLine.Height then
     740                CurrentLine.Height := BitmapHeight;
     741        end;                                                    // ALT
    656742
    657743        // do NOT reset WordX to zero; as we are continuing
     
    727813  NewMarginX: longint;
    728814  StartedDrawing: boolean;
     815  InsideDBC: boolean;       // ALT
    729816begin
    730817  Line := FLines[ LineIndex ];
     
    736823
    737824  StartedDrawing := false;
     825  InsideDBC := false;       // ALT
    738826
    739827  Link := '';
     
    746834  begin
    747835    Element := ExtractNextTextElement( P, NextP );
     836    CheckSpecialElementType( Element.Character, Element.ElementType, InsideDBC, Codepage );   // ALT
    748837
    749838    case Element.ElementType of
    750839      teWordBreak,
    751840      teText,
     841      teLeadByte,           // ALT
     842      teWrapChar,           // ALT
    752843      teImage:
    753844      begin
     
    772863
    773864        // Now find out how wide the thing is
    774         inc( X, GetElementWidth( Element ) );
     865        if (( Element.ElementType = teLeadByte ) And ( EndP > P )) then    // ALT
     866          inc( X, FFontManager.CJKTextWidth( 2, P ))
     867        else
     868          inc( X, GetElementWidth( Element ) );
    775869
    776870        if X div FontWidthPrecisionFactor
     
    10231117    end;
    10241118
    1025     teText, teWordBreak:
     1119    teText, teWordBreak, teWrapChar:        // ALT
    10261120      Result := FFontManager.CharWidth( Element.Character );
     1121
     1122    teLeadByte:                             // ALT - should not be reached
     1123      Result := FFontManager.CJKCharWidth;
     1124
     1125    teSecondByte:                           // ALT
     1126      Result := 0;
    10271127
    10281128    else
Note: See TracChangeset for help on using the changeset viewer.