From 73faf28aafb092e63bbe1d5c3c84514743e154f4 Mon Sep 17 00:00:00 2001 From: jesus Date: Sun, 24 Feb 2013 19:44:59 +0000 Subject: [PATCH] LazReport, implements Justified Alignment, issue #23796 git-svn-id: trunk@40397 - --- components/lazreport/source/lr_class.pas | 143 ++++++++++++++++++++--- components/lazreport/source/lr_utils.pas | 51 ++++++++ 2 files changed, 179 insertions(+), 15 deletions(-) diff --git a/components/lazreport/source/lr_class.pas b/components/lazreport/source/lr_class.pas index 3bcaea9adb..058940aeaa 100644 --- a/components/lazreport/source/lr_class.pas +++ b/components/lazreport/source/lr_class.pas @@ -373,6 +373,7 @@ type function GetHideDuplicates: Boolean; function GetHideZeroValues: Boolean; function GetIsLastValueSet: boolean; + function GetJustify: boolean; function GetLayout: TTextLayout; function GetWordBreak: Boolean; function GetWordWrap: Boolean; @@ -389,6 +390,7 @@ type procedure SetHideDuplicates(const AValue: Boolean); procedure SetHideZeroValues(AValue: Boolean); procedure SetIsLastValueSet(const AValue: boolean); + procedure SetJustify(AValue: boolean); procedure SetLayout(const AValue: TTextLayout); procedure SetWordBreak(AValue: Boolean); procedure SetWordWrap(const AValue: Boolean); @@ -431,6 +433,7 @@ type procedure DefinePopupMenu(Popup: TPopupMenu); override; procedure MonitorFontChanges; + property Justify: boolean read GetJustify write SetJustify; published property Font : TFont read fFont write SetFont; @@ -1749,6 +1752,88 @@ begin result := copy(result, 3, Length(result)); end; +procedure CanvasTextRectJustify(const Canvas:TCanvas; + const ARect: TRect; X1, X2, Y: integer; const Text: string; + Trimmed: boolean); +var + WordCount,SpcCount,SpcSize:Integer; + Arr: TArrUTF8Item; + PxSpc,RxSpc,Extra: Integer; + i: Integer; + Cini,Cend: Integer; + SpaceWidth, AvailWidth: Integer; + s:string; +begin + + AvailWidth := (X2-X1); + // count words + Arr := UTF8CountWords(Text, WordCount, SpcCount, SpcSize); + + // handle trimmed text + s := Text; + if (SpcCount>0) then + begin + Cini := 0; + CEnd := Length(Arr)-1; + if Trimmed then + begin + s := Trim(Text); + if Arr[Cini].Space then + begin + Inc(Cini); + Dec(SpcCount); + end; + if Arr[CEnd].Space then + begin + Dec(CEnd); + Dec(SpcCount); + end; + end; + AvailWidth := AvailWidth - Canvas.TextWidth(s); + end; + + // check if long way is needed + if (SpcCount>0) and (AvailWidth>0) then + begin + + SpaceWidth := Canvas.TextWidth(' '); + PxSpc := AvailWidth div SpcCount; + RxSpc := AvailWidth mod SpcCount; + if PxSPC=0 then + begin + PxSPC := 1; + RxSpc := 0; + end; + + for i:=CIni to CEnd do + if Arr[i].Space then + begin + X1 := X1 + Arr[i].Count * SpaceWidth; + if AvailWidth>0 then + begin + Extra := PxSpc; + if RxSpc>0 then + begin + Inc(Extra); + Dec(RxSpc); + end; + X1 := X1 + Extra; + Dec(AvailWidth, Extra); + end; + end + else + begin + s := Copy(Text, Arr[i].Index, Arr[i].Count); + Canvas.TextRect(ARect, X1, Y, s); + X1 := X1 + Canvas.TextWidth(s); + end; + + end else + Canvas.TextRect(ARect, X1, Y, s); + + SetLength(Arr, 0); +end; + { TfrReportDesigner } procedure TfrReportDesigner.SetModified(AValue: Boolean); @@ -2697,6 +2782,14 @@ begin end; end; +procedure TfrMemoView.SetJustify(AValue: boolean); +begin + // only if AValue=true change Adjust to reflect justify + // otherwise let it alone, so previous value of alignment is respected + if Avalue then + Adjust := Adjust or %11; +end; + procedure TfrMemoView.SetLayout(const AValue: TTextLayout); begin if Layout<>AValue then @@ -3073,6 +3166,7 @@ var n, {nw, w, }curx, lasty: Integer; lastyf: Double; Ts: TTextStyle; + ParaEnd: boolean; begin lastyf := curyf + thf - LineSpc - 1; lastY := Round(lastyf); @@ -3085,11 +3179,14 @@ var begin n := Length(St); //w := Ord(St[n - 1]) * 256 + Ord(St[n]); + ParaEnd := true; SetLength(St, n - 2); if Length(St) > 0 then begin if St[Length(St)] = #1 then - SetLength(St, Length(St) - 1); + SetLength(St, Length(St) - 1) + else + ParaEnd := false; end; // handle any alignment with same code @@ -3128,7 +3225,12 @@ var end; if not Exporting then - Canvas.TextRect(DR, CurX, round(curYf), St) + begin + if Justify and not ParaEnd then + CanvasTextRectJustify(Canvas, DR, x+gapx, x+dx-1-gapx, round(CurYf), St, true) + else + Canvas.TextRect(DR, CurX, round(curYf), St); + end else CurReport.InternalOnExportText(X, round(curYf), St, Self); @@ -3575,8 +3677,7 @@ procedure TfrMemoView.LoadFromStream(Stream: TStream); var w: Word; i: Integer; - TmpAlign: TAlignment; - TmpLayout: TTextLayout; + tmpLayout: TTextLayout; tmpAngle: Byte; begin {$IFDEF DebugLR} @@ -3606,13 +3707,13 @@ begin end; if frVersion>23 then begin - Read(TmpAlign{%H-},SizeOf(TmpAlign)); + Read(tmpAngle, SizeOf(tmpAngle)); + Adjust := (Adjust and not 3) or (tmpAngle and %11); Read(TmpLayout{%H-},SizeOf(TmpLayout)); tmpAngle := 0; Read(tmpAngle,SizeOf(tmpAngle)); BeginUpdate; - Alignment := tmpAlign; Layout := tmpLayout; Angle := tmpAngle; EndUpdate; @@ -3643,15 +3744,16 @@ begin RestoreProperty('Alignment',XML.GetValue(Path+'Alignment/Value','')); RestoreProperty('Layout',XML.GetValue(Path+'Layout/Value','')); Angle := XML.GetValue(Path+'Angle/Value'{%H-}, 0); + Justify := XML.GetValue(Path+'Justify/Value', false); end; procedure TfrMemoView.SaveToStream(Stream: TStream); var i: Integer; w: Word; - tmpAlign: TAlignment; tmpLayout: TTextLayout; tmpAngle: Byte; + tmpByteAlign: Byte; begin inherited SaveToStream(Stream); frWriteString(Stream, Font.Name); @@ -3670,14 +3772,11 @@ begin Write(Highlight, 10); frWriteString(Stream, HighlightStr); end; - - if (Adjust and %11 = %11) then - tmpAlign := taLeftJustify - else - tmpAlign := Alignment; + + tmpByteAlign := Adjust and %11; tmpLayout := Layout; tmpAngle := Angle; - Write(tmpAlign,SizeOf(tmpAlign)); + Write(tmpByteAlign, SizeOf(tmpByteAlign)); Write(tmpLayout,SizeOf(tmpLayout)); Write(tmpAngle,SizeOf(tmpAngle)); end; @@ -3702,6 +3801,7 @@ begin XML.SetValue(Path+'Alignment/Value',GetSaveProperty('Alignment')); XML.SetValue(Path+'Layout/Value', GetSaveProperty('Layout')); XML.SetValue(Path+'Angle/Value'{%H-}, Angle); + XML.SetValue(Path+'Justify/Value', Justify); end; procedure TfrMemoView.GetBlob(b: TfrTField); @@ -3832,6 +3932,11 @@ begin result := FLastValue<>nil; end; +function TfrMemoView.GetJustify: boolean; +begin + result := (Adjust and %11) = %11; +end; + function TfrMemoView.GetLayout: TTextLayout; begin result := TTextLayout((adjust shr 3) and %11); @@ -3844,7 +3949,10 @@ end; function TfrMemoView.GetAlignment: TAlignment; begin - Result:=Classes.TAlignment(Adjust and %11); + if (Adjust and %11) = %11 then + result := taLeftJustify + else + Result:=Classes.TAlignment(Adjust and %11); end; function TfrMemoView.GetAngle: Byte; @@ -3915,11 +4023,16 @@ begin end; procedure TfrMemoView.SetAlignment(const AValue: TAlignment); +var + b: byte; begin if Alignment<>AValue then begin BeforeChange; - Adjust := (Adjust and not 3) or ord(AValue); + // just in case, check for crazy value stored by alignment=justify + // in previous versions. + b := byte(AValue) and %11; + Adjust := (Adjust and not 3) or b; AfterChange; end; end; diff --git a/components/lazreport/source/lr_utils.pas b/components/lazreport/source/lr_utils.pas index 2b177b35ed..cd297d97d9 100644 --- a/components/lazreport/source/lr_utils.pas +++ b/components/lazreport/source/lr_utils.pas @@ -22,6 +22,17 @@ uses {$ENDIF} LCLType,LCLIntf,LCLProc,LConvEncoding; +type + TUTF8Item=packed record + Index: Integer; + UTF8Index: Integer; + Count: byte; + UTF8Count: byte; + Space: boolean; + end; + TArrUTF8Item=array of TUTF8Item; + + procedure frReadMemo(Stream: TStream; l: TStrings); procedure frReadMemo22(Stream: TStream; l: TStrings); @@ -64,6 +75,7 @@ function UTF8Index(index:integer; desc:string): Integer; function UTF8CharIn(ch:TUTF8Char; const arrstr:array of string): boolean; function UTF8QuotedStr(s:string; Quote: TUTF8Char; desc:string=''): string; function PosLast(SubChr:char; const Source:string):integer; +function UTF8CountWords(const str:string; out WordCount,SpcCount,SpcSize:Integer): TArrUTF8Item; implementation @@ -814,4 +826,43 @@ begin Result:=-1; end; +function UTF8CountWords(const str:string; out WordCount,SpcCount,SpcSize:Integer): TArrUTF8Item; +var + b,i,j,n,len: Integer; + spc: boolean; +begin + i := 1; + len := 0; + SetLength(result, 0); + WordCount := 0; + SpcCount := 0; + SpcSize := 0; + while i<=Length(str) do + begin + b := UTF8CharacterStrictLength(@Str[i]); + spc := (b=1) and (str[i]=' '); + inc(len); + j := Length(result)-1; + if (j<0) or (result[j].Space<>Spc) then + begin + inc(j); + SetLength(result, j+1); + result[j].Count:=0; + result[j].UTF8Count:=0; + result[j].Index:=i; + result[j].UTF8Index:=len; + if not spc then + Inc(WordCount) + else + Inc(SpcCount); + end; + result[j].Space := Spc; + result[j].UTF8Count := result[j].UTF8Count + 1; + result[j].Count := result[j].Count + b; + inc(i,b); + if Spc then + Inc(SpcSize); + end; +end; + end.