lcvectortextshapes.pas 80 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586
  1. // SPDX-License-Identifier: GPL-3.0-only
  2. unit LCVectorTextShapes;
  3. {$mode objfpc}{$H+}
  4. interface
  5. uses
  6. Classes, SysUtils, LCVectorRectShapes, BGRATextBidi, BGRABitmapTypes, LCVectorOriginal,
  7. BGRAGraphics, BGRABitmap, BGRALayerOriginal, BGRACanvas2D, LCVectorialFill,
  8. BGRASVGShapes, BGRASVGType, BGRAUnits;
  9. type
  10. TTextShape = class;
  11. { TTextShapeFontDiff }
  12. TTextShapeFontDiff = class(TVectorShapeDiff)
  13. protected
  14. FFontBidiModeBefore: TFontBidiMode;
  15. FFontEmHeightBefore: single;
  16. FFontNameBefore: string;
  17. FFontStyleBefore: TFontStyles;
  18. FAliasedBefore: boolean;
  19. FFontBidiModeAfter: TFontBidiMode;
  20. FFontEmHeightAfter: single;
  21. FFontNameAfter: string;
  22. FFontStyleAfter: TFontStyles;
  23. FAliasedAfter: boolean;
  24. public
  25. constructor Create(AStartShape: TVectorShape); override;
  26. procedure ComputeDiff(AEndShape: TVectorShape); override;
  27. procedure Apply(AStartShape: TVectorShape); override;
  28. procedure Unapply(AEndShape: TVectorShape); override;
  29. procedure Append(ADiff: TVectorShapeDiff); override;
  30. function IsIdentity: boolean; override;
  31. end;
  32. { TTextShapePhongDiff }
  33. TTextShapePhongDiff = class(TVectorShapeDiff)
  34. protected
  35. FAltitudePercentBefore: single;
  36. FPenPhongBefore: boolean;
  37. FLightPositionBefore: TPointF;
  38. FAltitudePercentAfter: single;
  39. FPenPhongAfter: boolean;
  40. FLightPositionAfter: TPointF;
  41. public
  42. constructor Create(AStartShape: TVectorShape); override;
  43. procedure ComputeDiff(AEndShape: TVectorShape); override;
  44. procedure Apply(AStartShape: TVectorShape); override;
  45. procedure Unapply(AEndShape: TVectorShape); override;
  46. procedure Append(ADiff: TVectorShapeDiff); override;
  47. function IsIdentity: boolean; override;
  48. end;
  49. { TTextShapeTextDiff }
  50. TTextShapeTextDiff = class(TVectorShapeDiff)
  51. protected
  52. FTextBefore: string;
  53. FSelStartBefore,FSelEndBefore: integer;
  54. FVertAlignBefore: TTextLayout;
  55. FParaAlignBefore: array of TBidiTextAlignment;
  56. FTextAfter: string;
  57. FSelStartAfter,FSelEndAfter: integer;
  58. FVertAlignAfter: TTextLayout;
  59. FParaAlignAfter: array of TBidiTextAlignment;
  60. public
  61. constructor Create(AStartShape: TVectorShape); override;
  62. procedure ComputeDiff(AEndShape: TVectorShape); override;
  63. procedure Apply(AStartShape: TVectorShape); override;
  64. procedure Unapply(AEndShape: TVectorShape); override;
  65. procedure Append(ADiff: TVectorShapeDiff); override;
  66. function IsIdentity: boolean; override;
  67. end;
  68. { TTextShape }
  69. TTextShape = class(TCustomRectShape)
  70. private
  71. FAliased: boolean;
  72. FAltitudePercent: single;
  73. FPenPhong: boolean;
  74. FPenFillIteration: integer;
  75. FLightPosition: TPointF;
  76. FFontBidiMode: TFontBidiMode;
  77. FFontEmHeight: single;
  78. FFontName: string;
  79. FFontStyle: TFontStyles;
  80. FText: string;
  81. FSelStart,FSelEnd: integer;
  82. FVertAlign: TTextLayout;
  83. FEnteringUnicode: boolean;
  84. FUnicodeValue: cardinal;
  85. FUnicodeDigitCount: integer;
  86. FMouseSelecting: boolean;
  87. function GetBidiParagraphAlignment: TBidiTextAlignment;
  88. function GetCanPasteSelection: boolean;
  89. function GetHasSelection: boolean;
  90. function GetParagraphAlignment: TAlignment;
  91. procedure InvalidateParagraphLayout(AFrom, ATo: integer);
  92. procedure LayoutBrokenLinesChanged({%H-}ASender: TObject;
  93. AParagraphIndex: integer; ASubBrokenStart, ASubBrokenChangedCountBefore,
  94. ASubBrokenChangedCountAfter: integer; ASubBrokenTotalCountBefore,
  95. ASubBrokenTotalCountAfter: integer);
  96. procedure LayoutParagraphDeleted({%H-}ASender: TObject; AParagraphIndex: integer);
  97. procedure LayoutParagraphMergedWithNext({%H-}ASender: TObject;
  98. AParagraphIndex: integer);
  99. procedure LayoutParagraphSplit({%H-}ASender: TObject; AParagraphIndex: integer;
  100. {%H-}ASubBrokenIndex, {%H-}ACharIndex: integer);
  101. procedure OnMoveLightPos({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF;
  102. {%H-}AShift: TShiftState);
  103. procedure SetAliased(AValue: boolean);
  104. procedure SetAltitudePercent(AValue: single);
  105. procedure SetPenPhong(AValue: boolean);
  106. procedure SetFontBidiMode(AValue: TFontBidiMode);
  107. procedure SetFontEmHeight(AValue: single);
  108. procedure SetFontName(AValue: string);
  109. procedure SetFontStyle(AValue: TFontStyles);
  110. procedure SetBidiParagraphAlignment(AValue: TBidiTextAlignment);
  111. procedure SetLightPosition(AValue: TPointF);
  112. procedure SetParagraphAlignment(AValue: TAlignment);
  113. procedure SetText(AValue: string);
  114. procedure SetVertAlign(AValue: TTextLayout);
  115. protected
  116. FTextLayout: TBidiTextLayout;
  117. FFontRenderer: TBGRACustomFontRenderer;
  118. FGlobalMatrix: TAffineMatrix;
  119. FCurBrokenLineImageId: int64;
  120. FParagraphLayout: array of record
  121. brokenLines: array of record
  122. penImageId, penMaskId,
  123. outlineMaskId: int64;
  124. end;
  125. end;
  126. procedure SetGlobalMatrix(AMatrix: TAffineMatrix);
  127. function ShowArrows: boolean; override;
  128. function GetTextLayout: TBidiTextLayout;
  129. function GetFontRenderer: TBGRACustomFontRenderer;
  130. function UpdateFontRenderer: boolean;
  131. function GetTextRenderZoom: single;
  132. function GetUntransformedMatrix: TAffineMatrix; //matrix before render transform
  133. function IsTextMirrored(ABox: TAffineBox): boolean;
  134. procedure SetDefaultFont;
  135. function GetCornerPositition: single; override;
  136. procedure DeleteTextBefore(ACount: integer);
  137. procedure DeleteTextAfter(ACount: integer);
  138. procedure InsertText(ATextUTF8: string);
  139. procedure SelectWithMouse(X,Y: single; AExtend: boolean);
  140. procedure SelectWordWithMouse(X,Y: single);
  141. procedure SelectParagraphWithMouse(X,Y: single);
  142. function HasOutline: boolean;
  143. procedure InsertUnicodeValue;
  144. procedure FillChange(ASender: TObject; var ADiff: TCustomVectorialFillDiff); override;
  145. procedure InvalidateAll;
  146. function GetVerticalAlignMatrix(tl: TBidiTextLayout): TAffineMatrix;
  147. public
  148. constructor Create(AContainer: TVectorOriginal); override;
  149. procedure QuickDefine(constref APoint1,APoint2: TPointF); override;
  150. procedure LoadFromStorage(AStorage: TBGRACustomOriginalStorage); override;
  151. procedure SaveToStorage(AStorage: TBGRACustomOriginalStorage); override;
  152. destructor Destroy; override;
  153. class function Fields: TVectorShapeFields; override;
  154. class function PreferPixelCentered: boolean; override;
  155. class function DefaultFontName: string;
  156. class function DefaultFontEmHeight: single;
  157. class function DefaultAltitudePercent: single;
  158. class function CreateEmpty: boolean; override;
  159. class function StorageClassName: RawByteString; override;
  160. class function Usermodes: TVectorShapeUsermodes; override;
  161. function AppendToSVG(AContent: TSVGContent; ADefs: TSVGDefine): TSVGElement; override;
  162. procedure ConfigureCustomEditor(AEditor: TBGRAOriginalEditor); override;
  163. procedure Render(ADest: TBGRABitmap; ARenderOffset: TPoint; AMatrix: TAffineMatrix; ADraft: boolean); overload; override;
  164. function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
  165. function PointInShape(APoint: TPointF): boolean; overload; override;
  166. function PointInShape({%H-}APoint: TPointF; {%H-}ARadius: single): boolean; overload; override;
  167. function PointInPen(APoint: TPointF): boolean; overload; override;
  168. function GetIsSlow(const {%H-}AMatrix: TAffineMatrix): boolean; override;
  169. function GetGenericCost: integer; override;
  170. procedure MouseMove({%H-}Shift: TShiftState; {%H-}X, {%H-}Y: single; var {%H-}ACursor: TOriginalEditorCursor; var {%H-}AHandled: boolean); override;
  171. procedure MouseDown({%H-}RightButton: boolean; {%H-}ClickCount: integer; {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: single; var {%H-}ACursor: TOriginalEditorCursor; var {%H-}AHandled: boolean); override;
  172. procedure MouseUp({%H-}RightButton: boolean; {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: single; var {%H-}ACursor: TOriginalEditorCursor; var {%H-}AHandled: boolean); override;
  173. procedure KeyDown({%H-}Shift: TShiftState; {%H-}Key: TSpecialKey; var {%H-}AHandled: boolean); override;
  174. procedure KeyPress({%H-}UTF8Key: string; var {%H-}AHandled: boolean); override;
  175. procedure KeyUp({%H-}Shift: TShiftState; {%H-}Key: TSpecialKey; var {%H-}AHandled: boolean); override;
  176. procedure SetFontNameAndStyle(AFontName: string; AFontStyle: TFontStyles);
  177. function CopySelection: boolean;
  178. function CutSelection: boolean;
  179. function PasteSelection: boolean;
  180. function DeleteSelection: boolean;
  181. procedure SelectAll;
  182. function GetAlignBounds(const {%H-}ALayoutRect: TRect; const AMatrix: TAffineMatrix): TRectF; override;
  183. procedure Transform(const AMatrix: TAffineMatrix); override;
  184. function AllowShearTransform: boolean; override;
  185. property HasSelection: boolean read GetHasSelection;
  186. property CanPasteSelection: boolean read GetCanPasteSelection;
  187. property Text: string read FText write SetText;
  188. property FontName: string read FFontName write SetFontName;
  189. property FontStyle: TFontStyles read FFontStyle write SetFontStyle;
  190. property FontEmHeight: single read FFontEmHeight write SetFontEmHeight;
  191. property FontBidiMode: TFontBidiMode read FFontBidiMode write SetFontBidiMode;
  192. property BidiParagraphAlignment: TBidiTextAlignment read GetBidiParagraphAlignment write SetBidiParagraphAlignment;
  193. property ParagraphAlignment: TAlignment read GetParagraphAlignment write SetParagraphAlignment;
  194. property VerticalAlignment: TTextLayout read FVertAlign write SetVertAlign;
  195. property PenPhong: boolean read FPenPhong write SetPenPhong;
  196. property LightPosition: TPointF read FLightPosition write SetLightPosition;
  197. property AltitudePercent: single read FAltitudePercent write SetAltitudePercent;
  198. property Aliased: boolean read FAliased write SetAliased;
  199. end;
  200. function FontStyleToStr(AStyle: TFontStyles): string;
  201. function StrToFontStyle(AText: string): TFontStyles;
  202. function FontBidiModeToStr(AMode: TFontBidiMode): string;
  203. function StrToFontBidiMode(AText: string): TFontBidiMode;
  204. implementation
  205. uses BGRATransform, BGRAText, BGRAVectorize, math,
  206. BGRAUTF8, BGRAUnicode, Graphics, Clipbrd, LCLType, LCLIntf,
  207. BGRAGradients, BGRACustomTextFX, LCResourceString, BGRAFillInfo,
  208. BGRAGrayscaleMask, BGRAPath, BGRALzpCommon, BGRADefaultBitmap;
  209. function FontStyleToStr(AStyle: TFontStyles): string;
  210. begin
  211. result := '';
  212. if fsBold in AStyle then result += 'b';
  213. if fsItalic in AStyle then result += 'i';
  214. if fsStrikeOut in AStyle then result += 's';
  215. if fsUnderline in AStyle then result += 'u';
  216. end;
  217. function StrToFontStyle(AText: string): TFontStyles;
  218. var
  219. i: Integer;
  220. begin
  221. result := [];
  222. for i := 1 to length(AText) do
  223. case AText[i] of
  224. 'b': Include(result, fsBold);
  225. 'i': Include(result, fsItalic);
  226. 's': Include(result, fsStrikeOut);
  227. 'u': Include(result, fsUnderline);
  228. end;
  229. end;
  230. function FontBidiModeToStr(AMode: TFontBidiMode): string;
  231. begin
  232. case AMode of
  233. fbmLeftToRight: result := 'ltr';
  234. fbmRightToLeft: result := 'rtl';
  235. else {fbmAuto} result := 'auto';
  236. end;
  237. end;
  238. function StrToFontBidiMode(AText: string): TFontBidiMode;
  239. begin
  240. if CompareText(AText,'ltr')=0 then result := fbmLeftToRight else
  241. if CompareText(AText,'rtl')=0 then result := fbmRightToLeft
  242. else result := fbmAuto;
  243. end;
  244. function GetPointBoundsF(APoints: ArrayOfTPointF): TRectF;
  245. var
  246. i: Integer;
  247. begin
  248. result := EmptyRectF;
  249. i := length(APoints);
  250. while i > 0 do
  251. begin
  252. dec(i);
  253. if not isEmptyPointF(APoints[i]) then
  254. begin
  255. result.TopLeft := APoints[i];
  256. result.BottomRight := APoints[i];
  257. break;
  258. end;
  259. end;
  260. while i > 0 do
  261. begin
  262. dec(i);
  263. result.Include(APoints[i]);
  264. end;
  265. end;
  266. { TTextShapeTextDiff }
  267. constructor TTextShapeTextDiff.Create(AStartShape: TVectorShape);
  268. var
  269. tl: TBidiTextLayout;
  270. i: Integer;
  271. begin
  272. with (AStartShape as TTextShape) do
  273. begin
  274. FTextBefore:= FText;
  275. FVertAlignBefore:= FVertAlign;
  276. tl := GetTextLayout;
  277. FSelStartBefore := FSelStart;
  278. FSelEndBefore:= FSelEnd;
  279. setlength(FParaAlignBefore, tl.ParagraphCount);
  280. for i := 0 to high(FParaAlignBefore) do
  281. FParaAlignBefore[i] := tl.ParagraphAlignment[i];
  282. end;
  283. end;
  284. procedure TTextShapeTextDiff.ComputeDiff(AEndShape: TVectorShape);
  285. var
  286. tl: TBidiTextLayout;
  287. i: Integer;
  288. begin
  289. with (AEndShape as TTextShape) do
  290. begin
  291. FTextAfter:= FText;
  292. FVertAlignAfter:= FVertAlign;
  293. FSelStartAfter := FSelStart;
  294. FSelEndAfter:= FSelEnd;
  295. tl := GetTextLayout;
  296. setlength(FParaAlignAfter, tl.ParagraphCount);
  297. for i := 0 to high(FParaAlignAfter) do
  298. FParaAlignAfter[i] := tl.ParagraphAlignment[i];
  299. end;
  300. end;
  301. procedure TTextShapeTextDiff.Apply(AStartShape: TVectorShape);
  302. var
  303. tl: TBidiTextLayout;
  304. i: Integer;
  305. begin
  306. with (AStartShape as TTextShape) do
  307. begin
  308. BeginUpdate;
  309. FreeAndNil(FTextLayout);
  310. FText := FTextAfter;
  311. FVertAlign := FVertAlignAfter;
  312. FSelStart := FSelStartAfter;
  313. FSelEnd := FSelEndAfter;
  314. tl := GetTextLayout;
  315. for i := 0 to min(length(FParaAlignAfter),tl.ParagraphCount)-1 do
  316. tl.ParagraphAlignment[i] := FParaAlignAfter[i];
  317. EndUpdate;
  318. end;
  319. end;
  320. procedure TTextShapeTextDiff.Unapply(AEndShape: TVectorShape);
  321. var
  322. tl: TBidiTextLayout;
  323. i: Integer;
  324. begin
  325. with (AEndShape as TTextShape) do
  326. begin
  327. BeginUpdate;
  328. FreeAndNil(FTextLayout);
  329. FText := FTextBefore;
  330. FVertAlign := FVertAlignBefore;
  331. FSelStart := FSelStartBefore;
  332. FSelEnd := FSelEndBefore;
  333. tl := GetTextLayout;
  334. for i := 0 to min(length(FParaAlignBefore),tl.ParagraphCount)-1 do
  335. tl.ParagraphAlignment[i] := FParaAlignBefore[i];
  336. EndUpdate;
  337. end;
  338. end;
  339. procedure TTextShapeTextDiff.Append(ADiff: TVectorShapeDiff);
  340. var
  341. next: TTextShapeTextDiff;
  342. i: Integer;
  343. begin
  344. next := ADiff as TTextShapeTextDiff;
  345. FTextAfter := next.FTextAfter;
  346. FVertAlignAfter := next.FVertAlignAfter;
  347. FSelStartAfter := next.FSelStartAfter;
  348. FSelEndAfter := next.FSelEndAfter;
  349. setlength(FParaAlignAfter, length(next.FParaAlignAfter));
  350. for i := 0 to high(FParaAlignAfter) do
  351. FParaAlignAfter[i] := next.FParaAlignAfter[i];
  352. end;
  353. function TTextShapeTextDiff.IsIdentity: boolean;
  354. var
  355. i: Integer;
  356. begin
  357. result := (FTextBefore = FTextAfter) and
  358. (FSelStartBefore = FSelStartAfter) and
  359. (FSelEndBefore = FSelEndAfter) and
  360. (FVertAlignBefore = FVertAlignAfter) and
  361. (length(FParaAlignBefore) = length(FParaAlignAfter));
  362. if result then
  363. begin
  364. for i := 0 to high(FParaAlignBefore) do
  365. if FParaAlignBefore[i] <> FParaAlignAfter[i] then
  366. begin
  367. result := false;
  368. break;
  369. end;
  370. end;
  371. end;
  372. { TTextShapePhongDiff }
  373. constructor TTextShapePhongDiff.Create(AStartShape: TVectorShape);
  374. begin
  375. with (AStartShape as TTextShape) do
  376. begin
  377. FAltitudePercentBefore := FAltitudePercent;
  378. FPenPhongBefore := FPenPhong;
  379. FLightPositionBefore := FLightPosition;
  380. end;
  381. end;
  382. procedure TTextShapePhongDiff.ComputeDiff(AEndShape: TVectorShape);
  383. begin
  384. with (AEndShape as TTextShape) do
  385. begin
  386. FAltitudePercentAfter := FAltitudePercent;
  387. FPenPhongAfter := FPenPhong;
  388. FLightPositionAfter := FLightPosition;
  389. end;
  390. end;
  391. procedure TTextShapePhongDiff.Apply(AStartShape: TVectorShape);
  392. begin
  393. with (AStartShape as TTextShape) do
  394. begin
  395. BeginUpdate;
  396. FAltitudePercent := FAltitudePercentAfter;
  397. FPenPhong := FPenPhongAfter;
  398. FLightPosition := FLightPositionAfter;
  399. EndUpdate;
  400. end;
  401. end;
  402. procedure TTextShapePhongDiff.Unapply(AEndShape: TVectorShape);
  403. begin
  404. with (AEndShape as TTextShape) do
  405. begin
  406. BeginUpdate;
  407. FAltitudePercent := FAltitudePercentBefore;
  408. FPenPhong := FPenPhongBefore;
  409. FLightPosition := FLightPositionBefore;
  410. EndUpdate;
  411. end;
  412. end;
  413. procedure TTextShapePhongDiff.Append(ADiff: TVectorShapeDiff);
  414. var
  415. next: TTextShapePhongDiff;
  416. begin
  417. next := ADiff as TTextShapePhongDiff;
  418. FAltitudePercentAfter:= next.FAltitudePercentAfter;
  419. FPenPhongAfter:= next.FPenPhongAfter;
  420. FLightPositionAfter:= next.FLightPositionAfter;
  421. end;
  422. function TTextShapePhongDiff.IsIdentity: boolean;
  423. begin
  424. result := (FAltitudePercentBefore = FAltitudePercentAfter) and
  425. (FPenPhongBefore = FPenPhongAfter) and
  426. (FLightPositionBefore = FLightPositionAfter);
  427. end;
  428. { TTextShapeFontDiff }
  429. constructor TTextShapeFontDiff.Create(AStartShape: TVectorShape);
  430. begin
  431. with (AStartShape as TTextShape) do
  432. begin
  433. FFontBidiModeBefore:= FFontBidiMode;
  434. FFontEmHeightBefore:= FFontEmHeight;
  435. FFontNameBefore:= FFontName;
  436. FFontStyleBefore:= FFontStyle;
  437. FAliasedBefore := FAliased;
  438. end;
  439. end;
  440. procedure TTextShapeFontDiff.ComputeDiff(AEndShape: TVectorShape);
  441. begin
  442. with (AEndShape as TTextShape) do
  443. begin
  444. FFontBidiModeAfter:= FFontBidiMode;
  445. FFontEmHeightAfter:= FFontEmHeight;
  446. FFontNameAfter:= FFontName;
  447. FFontStyleAfter:= FFontStyle;
  448. FAliasedAfter := FAliased;
  449. end;
  450. end;
  451. procedure TTextShapeFontDiff.Apply(AStartShape: TVectorShape);
  452. begin
  453. with (AStartShape as TTextShape) do
  454. begin
  455. BeginUpdate;
  456. FFontBidiMode := FFontBidiModeAfter;
  457. FFontEmHeight := FFontEmHeightAfter;
  458. FFontName := FFontNameAfter;
  459. FFontStyle := FFontStyleAfter;
  460. FAliased := FAliasedAfter;
  461. if Assigned(FTextLayout) then FTextLayout.InvalidateLayout;
  462. EndUpdate;
  463. end;
  464. end;
  465. procedure TTextShapeFontDiff.Unapply(AEndShape: TVectorShape);
  466. begin
  467. with (AEndShape as TTextShape) do
  468. begin
  469. BeginUpdate;
  470. FFontBidiMode := FFontBidiModeBefore;
  471. FFontEmHeight := FFontEmHeightBefore;
  472. FFontName := FFontNameBefore;
  473. FFontStyle := FFontStyleBefore;
  474. FAliased := FAliasedBefore;
  475. if Assigned(FTextLayout) then FTextLayout.InvalidateLayout;
  476. EndUpdate;
  477. end;
  478. end;
  479. procedure TTextShapeFontDiff.Append(ADiff: TVectorShapeDiff);
  480. var
  481. next: TTextShapeFontDiff;
  482. begin
  483. next := ADiff as TTextShapeFontDiff;
  484. FFontBidiModeAfter := next.FFontBidiModeAfter;
  485. FFontEmHeightAfter := next.FFontEmHeightAfter;
  486. FFontNameAfter := next.FFontNameAfter;
  487. FFontStyleAfter := next.FFontStyleAfter;
  488. FAliasedAfter := next.FAliasedAfter;
  489. end;
  490. function TTextShapeFontDiff.IsIdentity: boolean;
  491. begin
  492. result := (FFontBidiModeBefore = FFontBidiModeAfter) and
  493. (FFontEmHeightBefore = FFontEmHeightAfter) and
  494. (FFontNameBefore = FFontNameAfter) and
  495. (FFontStyleBefore = FFontStyleAfter) and
  496. (FAliasedBefore = FAliasedAfter);
  497. end;
  498. { TTextShape }
  499. procedure TTextShape.SetText(AValue: string);
  500. begin
  501. if FText=AValue then Exit;
  502. BeginUpdate(TTextShapeTextDiff);
  503. FText:=AValue;
  504. FSelStart:=0;
  505. FSelEnd :=0;
  506. FreeAndNil(FTextLayout);
  507. EndUpdate;
  508. end;
  509. procedure TTextShape.SetFontBidiMode(AValue: TFontBidiMode);
  510. begin
  511. if FFontBidiMode=AValue then Exit;
  512. BeginUpdate(TTextShapeFontDiff);
  513. FFontBidiMode:=AValue;
  514. InvalidateAll;
  515. EndUpdate;
  516. end;
  517. function TTextShape.GetBidiParagraphAlignment: TBidiTextAlignment;
  518. var
  519. tl: TBidiTextLayout;
  520. paraIndex: Integer;
  521. begin
  522. tl := GetTextLayout;
  523. paraIndex := tl.GetParagraphAt(FSelEnd);
  524. result := tl.ParagraphAlignment[paraIndex];
  525. end;
  526. function TTextShape.GetCanPasteSelection: boolean;
  527. begin
  528. result := Clipboard.HasFormat(PredefinedClipboardFormat(pcfText));
  529. end;
  530. function TTextShape.GetHasSelection: boolean;
  531. begin
  532. result := FSelEnd <> FSelStart;
  533. end;
  534. function TTextShape.GetParagraphAlignment: TAlignment;
  535. var
  536. tl: TBidiTextLayout;
  537. paraIndex: Integer;
  538. rtl: Boolean;
  539. begin
  540. tl := GetTextLayout;
  541. paraIndex := tl.GetParagraphAt(FSelEnd);
  542. rtl := tl.ParagraphRightToLeft[paraIndex];
  543. case tl.ParagraphAlignment[paraIndex] of
  544. btaCenter: result := taCenter;
  545. btaRightJustify: result := taRightJustify;
  546. btaNatural: if rtl then result := taRightJustify else result := taLeftJustify;
  547. btaOpposite: if rtl then result := taLeftJustify else result := taRightJustify;
  548. else {btaLeftJustify}
  549. result := taLeftJustify;
  550. end;
  551. end;
  552. procedure TTextShape.InvalidateParagraphLayout(AFrom, ATo: integer);
  553. var
  554. i, j: Integer;
  555. begin
  556. for i := AFrom to ATo do
  557. with FParagraphLayout[i] do
  558. begin
  559. for j := 0 to high(brokenLines) do
  560. begin
  561. brokenLines[j].penImageId := 0;
  562. brokenLines[j].penMaskId:= 0;
  563. brokenLines[j].outlineMaskId := 0;
  564. end;
  565. end;
  566. end;
  567. procedure TTextShape.LayoutBrokenLinesChanged(ASender: TObject;
  568. AParagraphIndex: integer; ASubBrokenStart, ASubBrokenChangedCountBefore,
  569. ASubBrokenChangedCountAfter: integer; ASubBrokenTotalCountBefore,
  570. ASubBrokenTotalCountAfter: integer);
  571. var
  572. i: Integer;
  573. begin
  574. if AParagraphIndex >= length(FParagraphLayout) then exit;
  575. if (ASubBrokenTotalCountBefore <> length(FParagraphLayout[AParagraphIndex].brokenLines)) then
  576. begin
  577. InvalidateParagraphLayout(AParagraphIndex,high(FParagraphLayout));
  578. FParagraphLayout[AParagraphIndex].brokenLines := nil;
  579. exit;
  580. end;
  581. with FParagraphLayout[AParagraphIndex] do
  582. begin
  583. if (ASubBrokenChangedCountBefore <> ASubBrokenChangedCountAfter) then
  584. begin
  585. for i := ASubBrokenStart to high(brokenLines) do
  586. begin
  587. brokenLines[i].penImageId := 0;
  588. brokenLines[i].penMaskId := 0;
  589. brokenLines[i].outlineMaskId := 0;
  590. end;
  591. setlength(brokenLines, ASubBrokenTotalCountAfter);
  592. if ASubBrokenChangedCountAfter > ASubBrokenChangedCountBefore then
  593. begin
  594. for i := ASubBrokenTotalCountBefore to high(brokenLines) do
  595. begin
  596. brokenLines[i].penImageId := 0;
  597. brokenLines[i].penMaskId := 0;
  598. brokenLines[i].outlineMaskId := 0;
  599. end;
  600. end;
  601. InvalidateParagraphLayout(AParagraphIndex+1,high(FParagraphLayout));
  602. end else
  603. for i := ASubBrokenStart to ASubBrokenStart+ASubBrokenChangedCountBefore-1 do
  604. begin
  605. brokenLines[i].penImageId := 0;
  606. brokenLines[i].penMaskId := 0;
  607. brokenLines[i].outlineMaskId := 0;
  608. end;
  609. end;
  610. end;
  611. procedure TTextShape.LayoutParagraphDeleted(ASender: TObject;
  612. AParagraphIndex: integer);
  613. begin
  614. InvalidateParagraphLayout(AParagraphIndex, high(FParagraphLayout));
  615. end;
  616. procedure TTextShape.LayoutParagraphMergedWithNext(ASender: TObject;
  617. AParagraphIndex: integer);
  618. begin
  619. InvalidateParagraphLayout(AParagraphIndex, high(FParagraphLayout));
  620. end;
  621. procedure TTextShape.LayoutParagraphSplit(ASender: TObject;
  622. AParagraphIndex: integer; ASubBrokenIndex, ACharIndex: integer);
  623. begin
  624. InvalidateParagraphLayout(AParagraphIndex, high(FParagraphLayout));
  625. end;
  626. procedure TTextShape.OnMoveLightPos(ASender: TObject; APrevCoord,
  627. ANewCoord: TPointF; AShift: TShiftState);
  628. begin
  629. LightPosition := ANewCoord;
  630. end;
  631. procedure TTextShape.SetAliased(AValue: boolean);
  632. begin
  633. if FAliased=AValue then Exit;
  634. BeginUpdate(TTextShapeFontDiff);
  635. FAliased:=AValue;
  636. InvalidateAll;
  637. EndUpdate;
  638. end;
  639. procedure TTextShape.SetAltitudePercent(AValue: single);
  640. begin
  641. if AValue < 0 then AValue := 0;
  642. if AValue > 100 then AValue := 100;
  643. if FAltitudePercent=AValue then Exit;
  644. BeginUpdate(TTextShapePhongDiff);
  645. FAltitudePercent:=AValue;
  646. EndUpdate;
  647. end;
  648. procedure TTextShape.SetPenPhong(AValue: boolean);
  649. begin
  650. if FPenPhong=AValue then Exit;
  651. BeginUpdate(TTextShapePhongDiff);
  652. FPenPhong:=AValue;
  653. EndUpdate;
  654. end;
  655. procedure TTextShape.SetFontEmHeight(AValue: single);
  656. begin
  657. if FFontEmHeight=AValue then Exit;
  658. BeginUpdate(TTextShapeFontDiff);
  659. FFontEmHeight:=AValue;
  660. if Assigned(FTextLayout) then FTextLayout.InvalidateLayout;
  661. EndUpdate;
  662. end;
  663. procedure TTextShape.SetFontName(AValue: string);
  664. begin
  665. if FFontName=AValue then Exit;
  666. BeginUpdate(TTextShapeFontDiff);
  667. FFontName:=AValue;
  668. InvalidateAll;
  669. EndUpdate;
  670. end;
  671. procedure TTextShape.SetFontStyle(AValue: TFontStyles);
  672. begin
  673. if FFontStyle=AValue then Exit;
  674. BeginUpdate(TTextShapeFontDiff);
  675. FFontStyle:=AValue;
  676. InvalidateAll;
  677. EndUpdate;
  678. end;
  679. procedure TTextShape.SetBidiParagraphAlignment(AValue: TBidiTextAlignment);
  680. var
  681. tl: TBidiTextLayout;
  682. paraIndex, paraIndex2, i: Integer;
  683. needUpdate: boolean;
  684. begin
  685. tl := GetTextLayout;
  686. if Usermode <> vsuEditText then
  687. begin
  688. if tl.ParagraphCount = 0 then exit;
  689. paraIndex := 0;
  690. paraIndex2:= tl.ParagraphCount-1;
  691. end else
  692. begin
  693. paraIndex := tl.GetParagraphAt(FSelStart);
  694. paraIndex2 := tl.GetParagraphAt(FSelEnd);
  695. end;
  696. needUpdate := false;
  697. for i := min(paraIndex,paraIndex2) to max(paraIndex,paraIndex2) do
  698. if tl.ParagraphAlignment[i] <> AValue then
  699. begin
  700. if not needUpdate then
  701. begin
  702. BeginUpdate(TTextShapeTextDiff);
  703. needUpdate := true;
  704. end;
  705. tl.ParagraphAlignment[i] := AValue;
  706. end;
  707. if needUpdate then EndUpdate;
  708. end;
  709. procedure TTextShape.SetLightPosition(AValue: TPointF);
  710. begin
  711. if FLightPosition=AValue then Exit;
  712. BeginUpdate(TTextShapePhongDiff);
  713. FLightPosition:=AValue;
  714. EndUpdate;
  715. end;
  716. procedure TTextShape.SetParagraphAlignment(AValue: TAlignment);
  717. var
  718. tl: TBidiTextLayout;
  719. paraIndex, paraIndex2, i: Integer;
  720. bidiAlign: TBidiTextAlignment;
  721. rtl, needUpdate: Boolean;
  722. begin
  723. tl := GetTextLayout;
  724. if UserMode <> vsuEditText then
  725. begin
  726. if tl.ParagraphCount = 0 then exit;
  727. paraIndex := 0;
  728. paraIndex2:= tl.ParagraphCount-1;
  729. end else
  730. begin
  731. paraIndex := tl.GetParagraphAt(FSelStart);
  732. paraIndex2 := tl.GetParagraphAt(FSelEnd);
  733. end;
  734. needUpdate := false;
  735. for i := min(paraIndex,paraIndex2) to max(paraIndex,paraIndex2) do
  736. begin
  737. rtl := tl.ParagraphRightToLeft[i];
  738. case AValue of
  739. taCenter: bidiAlign:= btaCenter;
  740. taRightJustify: if rtl then bidiAlign := btaNatural else bidiAlign := btaOpposite;
  741. else {taLeftJustify}
  742. if rtl then bidiAlign := btaOpposite else bidiAlign := btaNatural;
  743. end;
  744. if tl.ParagraphAlignment[i] <> bidiAlign then
  745. begin
  746. if not needUpdate then
  747. begin
  748. BeginUpdate(TTextShapeTextDiff);
  749. needUpdate := true;
  750. end;
  751. tl.ParagraphAlignment[i] := bidiAlign;
  752. end;
  753. end;
  754. if needUpdate then EndUpdate;
  755. end;
  756. procedure TTextShape.SetVertAlign(AValue: TTextLayout);
  757. begin
  758. if FVertAlign=AValue then Exit;
  759. BeginUpdate(TTextShapeTextDiff);
  760. FVertAlign:=AValue;
  761. InvalidateAll; // could be more subtle
  762. EndUpdate;
  763. end;
  764. procedure TTextShape.SetGlobalMatrix(AMatrix: TAffineMatrix);
  765. begin
  766. if AMatrix = FGlobalMatrix then exit;
  767. FGlobalMatrix := AMatrix;
  768. end;
  769. function TTextShape.AllowShearTransform: boolean;
  770. begin
  771. Result:= true;
  772. end;
  773. function TTextShape.ShowArrows: boolean;
  774. begin
  775. Result:= false;
  776. end;
  777. function TTextShape.GetTextLayout: TBidiTextLayout;
  778. var
  779. box: TAffineBox;
  780. begin
  781. if FTextLayout = nil then
  782. begin
  783. FTextLayout := TBidiTextLayout.Create(GetFontRenderer, FText);
  784. FTextLayout.OnParagraphDeleted:=@LayoutParagraphDeleted;
  785. FTextLayout.OnParagraphMergedWithNext:=@LayoutParagraphMergedWithNext;
  786. FTextLayout.OnParagraphSplit:=@LayoutParagraphSplit;
  787. FTextLayout.OnBrokenLinesChanged:=@LayoutBrokenLinesChanged;
  788. end
  789. else
  790. if UpdateFontRenderer then FTextLayout.InvalidateLayout;
  791. box := GetAffineBox(FGlobalMatrix,false);
  792. FTextLayout.FontBidiMode:= FontBidiMode;
  793. FTextLayout.TopLeft := PointF(0,0);
  794. FTextLayout.AvailableWidth:= box.Width;
  795. FTextLayout.AvailableHeight:= box.Height;
  796. FTextLayout.ParagraphSpacingBelow:= 0.5;
  797. result:= FTextLayout;
  798. end;
  799. function TTextShape.GetFontRenderer: TBGRACustomFontRenderer;
  800. begin
  801. UpdateFontRenderer;
  802. result := FFontRenderer;
  803. end;
  804. function TTextShape.UpdateFontRenderer: boolean;
  805. var
  806. newEmHeight: single;
  807. begin
  808. if FFontRenderer = nil then
  809. begin
  810. FFontRenderer := TBGRAVectorizedFontRenderer.Create;
  811. TBGRAVectorizedFontRenderer(FFontRenderer).QuadraticCurves := true;
  812. TBGRAVectorizedFontRenderer(FFontRenderer).MinFontResolution := 300;
  813. TBGRAVectorizedFontRenderer(FFontRenderer).MaxFontResolution := 300;
  814. end;
  815. newEmHeight := FontEmHeight*GetTextRenderZoom;
  816. if (newEmHeight <> FFontRenderer.FontEmHeight) or
  817. (FFontRenderer.FontName <> FontName) or
  818. (FFontRenderer.FontStyle <> FontStyle) or
  819. (FFontRenderer.FontQuality <> fqFineAntialiasing) then
  820. begin
  821. FFontRenderer.FontEmHeightF := newEmHeight;
  822. FFontRenderer.FontName:= FontName;
  823. FFontRenderer.FontStyle:= FontStyle;
  824. FFontRenderer.FontQuality:= fqFineAntialiasing;
  825. exit(true);
  826. end
  827. else exit(false);
  828. end;
  829. function TTextShape.GetTextRenderZoom: single;
  830. begin
  831. //font to be rendered at a sufficient size to avoid stretching
  832. result := max(VectLen(FGlobalMatrix[1,1],FGlobalMatrix[2,1]),
  833. VectLen(FGlobalMatrix[1,2],FGlobalMatrix[2,2]));
  834. end;
  835. function TTextShape.GetUntransformedMatrix: TAffineMatrix;
  836. var
  837. ab: TAffineBox;
  838. u, v: TPointF;
  839. lenU, lenV: Single;
  840. begin
  841. ab := GetAffineBox(AffineMatrixIdentity, false);
  842. u := ab.TopRight-ab.TopLeft;
  843. lenU := VectLen(u);
  844. if lenU<>0 then u *= (1/lenU);
  845. v := ab.BottomLeft-ab.TopLeft;
  846. lenV := VectLen(v);
  847. if lenV<>0 then v *= (1/lenV);
  848. result := AffineMatrix(u,v,ab.TopLeft);
  849. end;
  850. function TTextShape.IsTextMirrored(ABox: TAffineBox): boolean;
  851. var
  852. u,v: TPointF;
  853. begin
  854. u := ABox.TopRight-ABox.TopLeft;
  855. v := ABox.BottomLeft-ABox.TopLeft;
  856. result := u.x*v.y - u.y*v.x < 0;
  857. end;
  858. procedure TTextShape.SetDefaultFont;
  859. begin
  860. FontName := DefaultFontName;
  861. FontEmHeight := DefaultFontEmHeight;
  862. FontBidiMode:= fbmAuto;
  863. FontStyle := [];
  864. end;
  865. function TTextShape.GetCornerPositition: single;
  866. begin
  867. result := 1;
  868. end;
  869. procedure TTextShape.DeleteTextBefore(ACount: integer);
  870. var
  871. delCount, selLeft: Integer;
  872. begin
  873. if UserMode <> vsuEditText then exit;
  874. BeginUpdate(TTextShapeTextDiff);
  875. selLeft := Min(FSelStart,FSelEnd);
  876. if selLeft > 0 then
  877. begin
  878. delCount := GetTextLayout.DeleteTextBefore(selLeft, ACount);
  879. FText := GetTextLayout.TextUTF8;
  880. dec(selLeft,delCount);
  881. end;
  882. inc(selLeft, GetTextLayout.IncludeNonSpacingChars(selLeft, 0));
  883. FSelStart := selLeft;
  884. FSelEnd := selLeft;
  885. EndUpdate;
  886. end;
  887. procedure TTextShape.DeleteTextAfter(ACount: integer);
  888. var
  889. selRight: Integer;
  890. tl: TBidiTextLayout;
  891. begin
  892. if UserMode <> vsuEditText then exit;
  893. BeginUpdate(TTextShapeTextDiff);
  894. selRight := Max(FSelStart,FSelEnd);
  895. tl := GetTextLayout;
  896. if selRight+ACount <= tl.CharCount then
  897. begin
  898. tl.DeleteText(selRight, ACount);
  899. FText := tl.TextUTF8;
  900. end;
  901. inc(selRight, GetTextLayout.IncludeNonSpacingChars(selRight, 0));
  902. FSelStart := selRight;
  903. FSelEnd := selRight;
  904. EndUpdate;
  905. end;
  906. function TTextShape.DeleteSelection: boolean;
  907. var
  908. selLeft: Integer;
  909. begin
  910. if FSelStart <> FSelEnd then
  911. begin
  912. BeginUpdate(TTextShapeTextDiff);
  913. selLeft := Min(FSelStart,FSelEnd);
  914. GetTextLayout.DeleteText(selLeft, Abs(FSelEnd-FSelStart));
  915. FText := GetTextLayout.TextUTF8;
  916. inc(selLeft, GetTextLayout.IncludeNonSpacingChars(selLeft, 0));
  917. FSelStart := selLeft;
  918. FSelEnd := selLeft;
  919. EndUpdate;
  920. result := true;
  921. end else
  922. result := false;
  923. end;
  924. procedure TTextShape.SelectAll;
  925. begin
  926. BeginEditingUpdate;
  927. FSelStart:= 0;
  928. FSelEnd:= GetTextLayout.CharCount;
  929. EndEditingUpdate;
  930. end;
  931. function TTextShape.GetAlignBounds(const ALayoutRect: TRect;
  932. const AMatrix: TAffineMatrix): TRectF;
  933. var
  934. ab: TAffineBox;
  935. begin
  936. ab := GetAffineBox(AMatrix, false);
  937. Result:= ab.RectBoundsF;
  938. end;
  939. procedure TTextShape.InsertText(ATextUTF8: string);
  940. var
  941. insertCount: Integer;
  942. begin
  943. if UserMode <> vsuEditText then exit;
  944. BeginUpdate(TTextShapeTextDiff);
  945. DeleteSelection;
  946. insertCount := GetTextLayout.InsertText(ATextUTF8, FSelStart);
  947. FText := GetTextLayout.TextUTF8;
  948. Inc(FSelStart, insertCount);
  949. inc(FSelStart, GetTextLayout.IncludeNonSpacingChars(FSelStart, 0));
  950. FSelEnd := FSelStart;
  951. EndUpdate;
  952. end;
  953. procedure TTextShape.SelectWithMouse(X, Y: single; AExtend: boolean);
  954. var
  955. newPos: Integer;
  956. tl: TBidiTextLayout;
  957. zoom: Single;
  958. untransformed: TAffineMatrix;
  959. begin
  960. tl := GetTextLayout;
  961. zoom := GetTextRenderZoom;
  962. untransformed := GetUntransformedMatrix;
  963. if not IsAffineMatrixInversible(untransformed) then exit;
  964. newPos := tl.GetCharIndexAt(AffineMatrixScale(zoom,zoom)*AffineMatrixInverse(untransformed)*PointF(X,Y));
  965. if newPos<>-1 then
  966. begin
  967. if (newPos <> FSelEnd) or (not AExtend and (FSelStart <> FSelEnd)) or (UserMode <> vsuEditText) then
  968. begin
  969. BeginEditingUpdate;
  970. FSelEnd:= newPos;
  971. if not AExtend or (UserMode <> vsuEditText) then FSelStart:= FSelEnd;
  972. UserMode := vsuEditText;
  973. EndEditingUpdate;
  974. end;
  975. end;
  976. end;
  977. procedure TTextShape.SelectWordWithMouse(X, Y: single);
  978. const letterClasses = [ubcLeftToRight, ubcEuropeanNumber, ubcRightToLeft, ubcArabicLetter, ubcArabicNumber];
  979. var
  980. tl: TBidiTextLayout;
  981. function IsInWord(AIndex: integer): boolean;
  982. var c: TUTF8Char;
  983. begin
  984. c := tl.UTF8Char[AIndex];
  985. result := GetBidiClassUTF8(@c[1]) in letterClasses;
  986. end;
  987. var
  988. newPos, paraIndex, startIndex, endIndex: Integer;
  989. zoom: Single;
  990. untransformed: TAffineMatrix;
  991. inWord: boolean;
  992. begin
  993. if UserMode <> vsuEditText then exit;
  994. tl := GetTextLayout;
  995. zoom := GetTextRenderZoom;
  996. untransformed := GetUntransformedMatrix;
  997. if not IsAffineMatrixInversible(untransformed) then exit;
  998. newPos := tl.GetCharIndexAt(AffineMatrixScale(zoom,zoom)*AffineMatrixInverse(untransformed)*PointF(X,Y), false);
  999. if newPos<>-1 then
  1000. begin
  1001. paraIndex := tl.GetParagraphAt(newPos);
  1002. if (newPos < tl.ParagraphStartIndex[paraIndex]) or
  1003. (newPos >= tl.ParagraphEndIndex[paraIndex]) then exit;
  1004. startIndex := newPos;
  1005. endIndex := newPos+1;
  1006. inWord := IsInWord(newPos);
  1007. while (startIndex > tl.ParagraphStartIndex[paraIndex])
  1008. and (IsInWord(startIndex-1) = inWord) do dec(startIndex);
  1009. while (endIndex < tl.ParagraphEndIndex[paraIndex])
  1010. and (IsInWord(endIndex) = inWord) do inc(endIndex);
  1011. if (FSelStart <> startIndex) or
  1012. (FSelEnd <> endIndex) then
  1013. begin
  1014. BeginEditingUpdate;
  1015. FSelStart := startIndex;
  1016. FSelEnd:= endIndex;
  1017. EndEditingUpdate;
  1018. end;
  1019. end;
  1020. end;
  1021. procedure TTextShape.SelectParagraphWithMouse(X, Y: single);
  1022. var
  1023. newPos, paraIndex: Integer;
  1024. tl: TBidiTextLayout;
  1025. zoom: Single;
  1026. untransformed: TAffineMatrix;
  1027. begin
  1028. if UserMode <> vsuEditText then exit;
  1029. tl := GetTextLayout;
  1030. zoom := GetTextRenderZoom;
  1031. untransformed := GetUntransformedMatrix;
  1032. if not IsAffineMatrixInversible(untransformed) then exit;
  1033. newPos := tl.GetCharIndexAt(AffineMatrixScale(zoom,zoom)*AffineMatrixInverse(untransformed)*PointF(X,Y), false);
  1034. if newPos<>-1 then
  1035. begin
  1036. paraIndex := tl.GetParagraphAt(newPos);
  1037. if (FSelStart <> tl.ParagraphStartIndex[paraIndex]) or
  1038. (FSelEnd <> tl.ParagraphEndIndex[paraIndex]) then
  1039. begin
  1040. BeginEditingUpdate;
  1041. FSelStart := tl.ParagraphStartIndex[paraIndex];
  1042. FSelEnd:= tl.ParagraphEndIndex[paraIndex];
  1043. EndEditingUpdate;
  1044. end;
  1045. end;
  1046. end;
  1047. function TTextShape.HasOutline: boolean;
  1048. begin
  1049. result := not OutlineFill.IsFullyTransparent and (OutlineWidth > 0);
  1050. end;
  1051. procedure TTextShape.InsertUnicodeValue;
  1052. begin
  1053. if FEnteringUnicode then
  1054. begin
  1055. if FUnicodeValue <= $10FFFF then
  1056. InsertText(UnicodeCharToUTF8(FUnicodeValue));
  1057. FEnteringUnicode:= false;
  1058. end;
  1059. end;
  1060. procedure TTextShape.FillChange(ASender: TObject;
  1061. var ADiff: TCustomVectorialFillDiff);
  1062. begin
  1063. if ASender = PenFill then inc(FPenFillIteration);
  1064. inherited FillChange(ASender, ADiff);
  1065. end;
  1066. procedure TTextShape.InvalidateAll;
  1067. begin
  1068. if Assigned(FTextLayout) then FTextLayout.InvalidateLayout;
  1069. FParagraphLayout := nil;
  1070. end;
  1071. function TTextShape.GetVerticalAlignMatrix(tl: TBidiTextLayout): TAffineMatrix;
  1072. var
  1073. th: Single;
  1074. begin
  1075. th := max(tl.TotalTextHeight - tl.ParagraphSpacingBelow * tl.LineHeight, 0);
  1076. if th < tl.AvailableHeight then
  1077. case VerticalAlignment of
  1078. tlBottom: exit(AffineMatrixTranslation(0, tl.AvailableHeight-th));
  1079. tlCenter: exit(AffineMatrixTranslation(0, (tl.AvailableHeight-th)/2));
  1080. end;
  1081. exit(AffineMatrixIdentity);
  1082. end;
  1083. constructor TTextShape.Create(AContainer: TVectorOriginal);
  1084. begin
  1085. inherited Create(AContainer);
  1086. SetDefaultFont;
  1087. FVertAlign:= tlTop;
  1088. FText := '';
  1089. FSelStart := 0;
  1090. FSelEnd := 0;
  1091. FGlobalMatrix := AffineMatrixIdentity;
  1092. FPenPhong:= false;
  1093. FPenFillIteration := 0;
  1094. FAltitudePercent:= DefaultAltitudePercent;
  1095. FLightPosition := PointF(0,0);
  1096. FAliased := false;
  1097. FCurBrokenLineImageId := 0;
  1098. end;
  1099. procedure TTextShape.QuickDefine(constref APoint1, APoint2: TPointF);
  1100. var minSize: single;
  1101. p2: TPointF;
  1102. begin
  1103. minSize := GetFontRenderer.TextSize('Hg').cy/GetTextRenderZoom;
  1104. p2 := APoint2;
  1105. if abs(APoint1.x-p2.x) < minSize then
  1106. begin
  1107. if p2.x < APoint1.x then p2.x := APoint1.x - minSize else
  1108. p2.x := APoint1.x + minSize;
  1109. end;
  1110. if abs(APoint1.y-p2.y) < minSize then
  1111. begin
  1112. if p2.y < APoint1.y then p2.y := APoint1.y - minSize else
  1113. p2.y := APoint1.y + minSize;
  1114. end;
  1115. inherited QuickDefine(APoint1, p2);
  1116. end;
  1117. procedure TTextShape.LoadFromStorage(AStorage: TBGRACustomOriginalStorage);
  1118. var
  1119. font, phongObj: TBGRACustomOriginalStorage;
  1120. tl: TBidiTextLayout;
  1121. paraAlignList: TStringList;
  1122. i: Integer;
  1123. alignment: TAlignment;
  1124. begin
  1125. BeginUpdate;
  1126. inherited LoadFromStorage(AStorage);
  1127. Text := AStorage.RawString['text'];
  1128. font := AStorage.OpenObject('font');
  1129. if Assigned(font) then
  1130. begin
  1131. if font.HasAttribute('name') then
  1132. FontName:= font.RawString['name']
  1133. else
  1134. FontName:= AStorage.RawString['name']; //compatibility
  1135. if fontName = '' then fontName := DefaultFontName;
  1136. if font.HasAttribute('em-height') then
  1137. FontEmHeight:= font.FloatDef['em-height', DefaultFontEmHeight]
  1138. else
  1139. FontEmHeight:= AStorage.FloatDef['em-height', DefaultFontEmHeight]; //compatibility
  1140. if Font.HasAttribute('bidi') then
  1141. FontBidiMode:= StrToFontBidiMode(font.RawString['bidi'])
  1142. else
  1143. FontBidiMode:= StrToFontBidiMode(AStorage.RawString['bidi']); //compatibility
  1144. if font.HasAttribute('style') then
  1145. FontStyle:= StrToFontStyle(font.RawString['style'])
  1146. else
  1147. FontStyle:= StrToFontStyle(AStorage.RawString['style']); //compatibility
  1148. font.Free;
  1149. end else
  1150. SetDefaultFont;
  1151. Aliased := AStorage.Bool['aliased'];
  1152. phongObj := AStorage.OpenObject('pen-phong');
  1153. PenPhong := Assigned(phongObj);
  1154. if PenPhong then
  1155. begin
  1156. LightPosition := phongObj.PointF['light-pos'];
  1157. AltitudePercent:= phongObj.FloatDef['altitude-percent', DefaultAltitudePercent];
  1158. phongObj.Free;
  1159. end else
  1160. begin
  1161. LightPosition := PointF(0,0);
  1162. AltitudePercent:= DefaultAltitudePercent;
  1163. end;
  1164. tl := GetTextLayout;
  1165. paraAlignList := TStringList.Create;
  1166. paraAlignList.DelimitedText:= AStorage.RawString['paragraph-align'];
  1167. for i := 0 to min(paraAlignList.Count, tl.ParagraphCount)-1 do
  1168. begin
  1169. case paraAlignList[i] of
  1170. 'center': alignment := taCenter;
  1171. 'right': alignment := taRightJustify;
  1172. else {'left'} alignment := taLeftJustify;
  1173. end;
  1174. tl.ParagraphAlignment[i] := AlignmentToBidiTextAlignment(alignment, tl.ParagraphRightToLeft[i]);
  1175. end;
  1176. paraAlignList.Free;
  1177. case AStorage.RawString['vertical-align'] of
  1178. 'middle': VerticalAlignment:= tlCenter;
  1179. 'bottom': VerticalAlignment:= tlBottom;
  1180. else VerticalAlignment:= tlTop;
  1181. end;
  1182. EndUpdate;
  1183. end;
  1184. procedure TTextShape.SaveToStorage(AStorage: TBGRACustomOriginalStorage);
  1185. var
  1186. font, phongObj: TBGRACustomOriginalStorage;
  1187. tl: TBidiTextLayout;
  1188. paraAlignList: TStringList;
  1189. i: Integer;
  1190. begin
  1191. inherited SaveToStorage(AStorage);
  1192. AStorage.RawString['text'] := Text;
  1193. font := AStorage.OpenObject('font');
  1194. if font = nil then font := AStorage.CreateObject('font');
  1195. font.RawString['name'] := FontName;
  1196. font.Float['em-height'] := FontEmHeight;
  1197. font.RawString['bidi'] := FontBidiModeToStr(FontBidiMode);
  1198. font.RawString['style'] := FontStyleToStr(FontStyle);
  1199. font.Free;
  1200. AStorage.Bool['aliased'] := Aliased;
  1201. if PenPhong then
  1202. begin
  1203. phongObj := AStorage.OpenObject('pen-phong');
  1204. if phongObj=nil then phongObj := AStorage.CreateObject('pen-phong');
  1205. phongObj.PointF['light-pos'] := LightPosition;
  1206. phongObj.Float['altitude-percent'] := AltitudePercent;
  1207. phongObj.Free;
  1208. end else
  1209. AStorage.RemoveObject('pen-phong');
  1210. tl := GetTextLayout;
  1211. paraAlignList := TStringList.Create;
  1212. for i := 0 to tl.ParagraphCount-1 do
  1213. case tl.ParagraphAlignment[i] of
  1214. btaRightJustify: paraAlignList.Add('right');
  1215. btaCenter: paraAlignList.Add('center');
  1216. btaNatural: if tl.ParagraphRightToLeft[i] then paraAlignList.Add('right') else paraAlignList.Add('left');
  1217. btaOpposite: if tl.ParagraphRightToLeft[i] then paraAlignList.Add('left') else paraAlignList.Add('right');
  1218. else {btaLeftJustify}
  1219. paraAlignList.Add('left');
  1220. end;
  1221. AStorage.RawString['paragraph-align'] := paraAlignList.DelimitedText;
  1222. paraAlignList.Free;
  1223. case VerticalAlignment of
  1224. tlTop: AStorage.RemoveAttribute('vertical-align');
  1225. tlCenter: AStorage.RawString['vertical-align'] := 'middle';
  1226. tlBottom: AStorage.RawString['vertical-align'] := 'bottom';
  1227. end;
  1228. end;
  1229. destructor TTextShape.Destroy;
  1230. begin
  1231. FreeAndNil(FTextLayout);
  1232. FreeAndNil(FFontRenderer);
  1233. inherited Destroy;
  1234. end;
  1235. class function TTextShape.Fields: TVectorShapeFields;
  1236. begin
  1237. Result:= [vsfPenFill,vsfOutlineFill,vsfOutlineWidth];
  1238. end;
  1239. class function TTextShape.PreferPixelCentered: boolean;
  1240. begin
  1241. Result:= false;
  1242. end;
  1243. class function TTextShape.DefaultFontName: string;
  1244. begin
  1245. result := {$IFDEF WINDOWS}'Arial'{$ELSE}{$IFDEF DARWIN}'Helvetica'{$ELSE}'Liberation Sans'{$ENDIF}{$ENDIF};
  1246. end;
  1247. class function TTextShape.DefaultFontEmHeight: single;
  1248. begin
  1249. result := 20;
  1250. end;
  1251. class function TTextShape.DefaultAltitudePercent: single;
  1252. begin
  1253. result := 30;
  1254. end;
  1255. class function TTextShape.CreateEmpty: boolean;
  1256. begin
  1257. Result:= true;
  1258. end;
  1259. procedure TTextShape.ConfigureCustomEditor(AEditor: TBGRAOriginalEditor);
  1260. var
  1261. caret: TBidiCaretPos;
  1262. orientation: TPointF;
  1263. m: TAffineMatrix;
  1264. tl: TBidiTextLayout;
  1265. pts: Array Of TPointF;
  1266. i, idxLight: Integer;
  1267. c: TBGRAPixel;
  1268. zoom: Single;
  1269. begin
  1270. inherited ConfigureCustomEditor(AEditor);
  1271. AEditor.AddPolyline(GetAffineBox(AffineMatrixIdentity,true).AsPolygon, true, opsDashWithShadow);
  1272. if AEditor.Focused and (Usermode = vsuEditText) then
  1273. begin
  1274. tl := GetTextLayout;
  1275. caret:= tl.GetCaret(FSelEnd);
  1276. zoom := GetTextRenderZoom;
  1277. m := AffineMatrixTranslation(-0.5,-0.5)*GetUntransformedMatrix*AffineMatrixScale(1/zoom,1/zoom)*GetVerticalAlignMatrix(tl);
  1278. if FSelStart<>FSelEnd then
  1279. begin
  1280. pts := tl.GetTextEnveloppe(FSelStart, FSelEnd, false, true, true);
  1281. for i := 0 to high(pts) do
  1282. pts[i] := m*pts[i];
  1283. c:= clHighlight;
  1284. c.alpha := 96;
  1285. AEditor.AddPolyline(pts, true, opsDash, c);
  1286. end;
  1287. if (tl.AvailableHeight = EmptySingle) or (caret.Top.y < tl.AvailableHeight) then
  1288. begin
  1289. orientation := (caret.Bottom-caret.Top)*(1/10);
  1290. orientation := PointF(-orientation.y,orientation.x);
  1291. if (tl.AvailableHeight <> EmptySingle) and (caret.Bottom.y <> EmptySingle) and (caret.Bottom.y > tl.AvailableHeight) then caret.Bottom.y := tl.AvailableHeight;
  1292. if (tl.AvailableHeight <> EmptySingle) and (caret.PreviousBottom.y <> EmptySingle) and (caret.PreviousBottom.y > tl.AvailableHeight) then caret.PreviousBottom.y := tl.AvailableHeight;
  1293. if not isEmptyPointF(caret.PreviousTop) and (caret.PreviousRightToLeft<>caret.RightToLeft) then
  1294. begin
  1295. if caret.RightToLeft then orientation := -orientation;
  1296. AEditor.AddPolyline([m*caret.Bottom,m*caret.Top,m*(caret.Top+orientation)],false, opsSolid);
  1297. end else
  1298. AEditor.AddPolyline([m*caret.Bottom,m*caret.Top],false, opsSolid);
  1299. end;
  1300. end;
  1301. if PenPhong then
  1302. begin
  1303. idxLight := AEditor.AddPoint(FLightPosition, @OnMoveLightPos, true);
  1304. if AEditor is TVectorOriginalEditor then
  1305. TVectorOriginalEditor(AEditor).AddLabel(idxLight, rsLightPosition, taCenter, tlTop);
  1306. end;
  1307. end;
  1308. procedure TTextShape.Render(ADest: TBGRABitmap; ARenderOffset: TPoint; AMatrix: TAffineMatrix;
  1309. ADraft: boolean);
  1310. function GetTextPhongHeight: integer;
  1311. begin
  1312. result := round(AltitudePercent/100 * FontEmHeight*0.15);
  1313. end;
  1314. function CreateShader(AOfsX,AOfsY: integer): TPhongShading;
  1315. var
  1316. lightPosF: TPointF;
  1317. lightPosZ: Single;
  1318. begin
  1319. result := TPhongShading.Create;
  1320. result.AmbientFactor := 0.6;
  1321. result.NegativeDiffusionFactor := 0.15;
  1322. lightPosF := FGlobalMatrix*LightPosition+PointF(AOfsX,AOfsY);
  1323. lightPosZ := max(AltitudePercent, 1.2*GetTextPhongHeight);
  1324. result.LightPosition3D := Point3D(lightPosF.x,lightPosF.y,lightPosZ);
  1325. end;
  1326. var
  1327. hasPen: boolean;
  1328. zoom, outlineRenderWidth: Single;
  1329. m: TAffineMatrix;
  1330. tl: TBidiTextLayout;
  1331. fr: TBGRACustomFontRenderer;
  1332. pad, paraIndex, fileIdx: Integer;
  1333. sourceRectF,transfRectF,sourceInvRect,destF: TRectF;
  1334. transfRect, tmpRenderRect, brokenLineRectBounds: TRect;
  1335. tmpTransf: TBGRABitmap;
  1336. tmpBroken: TBGRAMemoryStreamBitmap;
  1337. tmpTransfOutline, tmpBrokenMask: TGrayscaleMask;
  1338. storeImage, useBrokenLinesRender, redrawPen, redrawOutline,
  1339. outlineWidthChange, penPhongChange: Boolean;
  1340. storage: TShapeRenderStorage;
  1341. startBrokenIndex, endBrokenIndex, brokenIndex: LongInt;
  1342. tempRenderNewList, tempRenderCurList: TStringList;
  1343. phongObj, tempStorage: TBGRACustomOriginalStorage;
  1344. brokenRenderOfs: TPoint;
  1345. brokenLinePoints: Array of TPointF;
  1346. brokenLineBoundsF: TRectF;
  1347. procedure ComputeBrokenLinesPath(AStartBroken, AEndBroken: integer);
  1348. var
  1349. p: TBGRAPath;
  1350. begin
  1351. p := TBGRAPath.Create;
  1352. tl.PathBrokenLines(p, AStartBroken, AEndBroken);
  1353. brokenLinePoints := p.ToPoints(m);
  1354. brokenLineBoundsF := GetPointBoundsF(brokenLinePoints);
  1355. p.Free;
  1356. end;
  1357. procedure FillPen(ADestination: TCustomUniversalBitmap; AOffset: TPoint; ABrush: TUniversalBrush);
  1358. var
  1359. pts: ArrayOfTPointF;
  1360. i: Integer;
  1361. begin
  1362. if not hasPen then exit;
  1363. pts := PointsF(brokenLinePoints);
  1364. for i := high(pts) downto 0 do
  1365. pts[i].Offset(AOffset.x, AOffset.Y);
  1366. ADestination.FillMode:= fmWinding;
  1367. if (ADraft and PenPhong) or Aliased then
  1368. ADestination.FillPoly(pts, ABrush, false)
  1369. else
  1370. ADestination.FillPolyAntialias(pts, ABrush, false);
  1371. end;
  1372. procedure RenderPen(ADestination: TBGRACustomBitmap; AOffset: TPoint);
  1373. var
  1374. rF: TRectF;
  1375. r: TRect;
  1376. textMask: TGrayscaleMask;
  1377. textFx: TBGRACustomTextEffect;
  1378. scan: TBGRACustomScanner;
  1379. shader: TPhongShading;
  1380. maskOfs: TPoint;
  1381. b: TUniversalBrush;
  1382. begin
  1383. if not hasPen then exit;
  1384. if PenPhong then
  1385. begin
  1386. rF := brokenLineBoundsF;
  1387. rF.Offset(AOffset.X, AOffset.Y);
  1388. rF := TRectF.Intersect(rF, rectF(0,0,ADestination.Width,ADestination.Height));
  1389. r := Rect(floor(rF.Left), floor(rF.Top), ceil(rF.Right), ceil(rF.Bottom));
  1390. r.Inflate(1, 1);
  1391. maskOfs := Point(AOffset.x - r.Left, AOffset.y - r.Top);
  1392. textMask := TGrayscaleMask.Create(r.Width, r.Height, 0);
  1393. textMask.SolidBrush(b, ByteMaskWhite, dmDrawWithTransparency);
  1394. FillPen(textMask, maskOfs, b);
  1395. textFx := TBGRACustomTextEffect.Create(textMask, true, textMask.Width,textMask.Height, Point(0, 0));
  1396. shader:= CreateShader(AOffset.X, AOffset.Y);
  1397. if PenFill.FillType = vftSolid then
  1398. textFx.DrawShaded(ADestination, r.Left, r.Top, shader, GetTextPhongHeight, PenFill.SolidColor)
  1399. else
  1400. begin
  1401. scan := PenFill.CreateScanner(AffineMatrixTranslation(AOffset.X, AOffset.Y)*FGlobalMatrix, ADraft);
  1402. textFx.DrawShaded(ADestination, r.Left, r.Top, shader, GetTextPhongHeight, scan);
  1403. scan.Free;
  1404. end;
  1405. shader.Free;
  1406. textFx.Free;
  1407. end else
  1408. begin
  1409. if PenFill.FillType = vftSolid then
  1410. begin
  1411. ADestination.SolidBrush(b, PenFill.SolidColor, dmDrawWithTransparency);
  1412. FillPen(ADestination, AOffset, b);
  1413. end else
  1414. begin
  1415. scan := PenFill.CreateScanner(AffineMatrixTranslation(AOffset.X, AOffset.Y)*FGlobalMatrix, ADraft);
  1416. ADestination.ScannerBrush(b, scan, dmDrawWithTransparency);
  1417. FillPen(ADestination, AOffset, b);
  1418. scan.Free;
  1419. end;
  1420. end;
  1421. end;
  1422. procedure RenderPenMask(ADestination: TGrayscaleMask; AOffset: TPoint);
  1423. var
  1424. b: TUniversalBrush;
  1425. begin
  1426. ADestination.SolidBrush(b, ByteMaskWhite, dmDrawWithTransparency);
  1427. FillPen(ADestination, AOffset, b);
  1428. end;
  1429. procedure RenderFromMask(ADestination: TBGRABitmap; AX, AY: Integer; AMask: TGrayscaleMask;
  1430. AFillOffset: TPoint; AFill: TVectorialFill);
  1431. var
  1432. scan: TBGRACustomScanner;
  1433. begin
  1434. if AFill.FillType = vftSolid then
  1435. begin
  1436. ADestination.FillMask(AX, AY, AMask, AFill.SolidColor, dmDrawWithTransparency);
  1437. end else
  1438. if AFill.FillType <> vftNone then
  1439. begin
  1440. scan := AFill.CreateScanner(AffineMatrixTranslation(AFillOffset.X, AFillOffset.Y)*FGlobalMatrix, ADraft);
  1441. ADestination.FillMask(AX, AY, AMask, scan, dmDrawWithTransparency);
  1442. scan.Free;
  1443. end;
  1444. end;
  1445. procedure RenderFromMask(ADestination: TGrayscaleMask; AX, AY: Integer; AMask: TGrayscaleMask);
  1446. begin
  1447. ADestination.FillMask(AX, AY, AMask, ByteMaskWhite);
  1448. end;
  1449. procedure FillOutline(ADestination: TCustomUniversalBitmap; AOffset: TPoint; ABrush: TUniversalBrush);
  1450. var
  1451. pts: ArrayOfTPointF;
  1452. i: Integer;
  1453. begin
  1454. if not HasOutline then exit;
  1455. ADestination.Pen.JoinStyle:= pjsRound;
  1456. ADestination.Pen.Style:= psSolid;
  1457. ADestination.FillMode:= fmWinding;
  1458. pts := ADestination.Pen.ComputePolygon(brokenLinePoints, outlineRenderWidth);
  1459. for i := high(pts) downto 0 do
  1460. pts[i].Offset(AOffset.x, AOffset.Y);
  1461. if ADraft or Aliased then
  1462. ADestination.FillPoly(pts, ABrush, false)
  1463. else
  1464. ADestination.FillPolyAntialias(pts, ABrush, false);
  1465. end;
  1466. procedure RenderOutlineMask(ADestination: TGrayscaleMask; AOffset: TPoint);
  1467. var
  1468. b: TUniversalBrush;
  1469. begin
  1470. ADestination.SolidBrush(b, ByteMaskWhite, dmDrawWithTransparency);
  1471. FillOutline(ADestination, AOffset, b);
  1472. end;
  1473. procedure RenderOutline(ADestination: TBGRABitmap; AOffset: TPoint);
  1474. var
  1475. scan: TBGRACustomScanner;
  1476. b: TUniversalBrush;
  1477. begin
  1478. if OutlineFill.FillType = vftSolid then
  1479. begin
  1480. ADestination.SolidBrush(b, OutlineFill.SolidColor, dmDrawWithTransparency);
  1481. FillOutline(ADestination, AOffset, b);
  1482. end else
  1483. if OutlineFill.FillType <> vftNone then
  1484. begin
  1485. scan := OutlineFill.CreateScanner(AffineMatrixTranslation(AOffset.X, AOffset.Y)*FGlobalMatrix, ADraft);
  1486. ADestination.ScannerBrush(b, scan, dmDrawWithTransparency);
  1487. FillOutline(ADestination, AOffset, b);
  1488. scan.Free;
  1489. end;
  1490. end;
  1491. procedure RenderBrokenLines(ADestination: TBGRABitmap; AStartBroken, AEndBroken: integer; AOffset: TPoint);
  1492. begin
  1493. ComputeBrokenLinesPath(AStartBroken, AEndBroken);
  1494. RenderOutline(ADestination, AOffset);
  1495. RenderPen(ADestination, AOffset);
  1496. end;
  1497. procedure StoreRGBAImage(var AImageId: int64; AImage: TBGRAMemoryStreamBitmap; AImageOffset: TPoint);
  1498. var
  1499. renderObj: TBGRACustomOriginalStorage;
  1500. begin
  1501. if AImageId = 0 then
  1502. begin
  1503. inc(FCurBrokenLineImageId);
  1504. AImageId := FCurBrokenLineImageId;
  1505. end;
  1506. renderObj := tempStorage.CreateObject(inttostr(AImageId));
  1507. renderObj.PointF['size'] := PointF(AImage.Width, AImage.Height);
  1508. renderObj.PointF['offset'] := PointF(AImageOffset) - PointF(ARenderOffset);
  1509. renderObj.WriteFile('image.data', AImage.Stream, false, AImage.OwnStream);
  1510. AImage.OwnStream := false;
  1511. renderObj.Free;
  1512. tempRenderNewList.Add(inttostr(AImageId));
  1513. end;
  1514. procedure EncodeSimpleRLE(AData: PByte; ASize: integer; AStream: TStream);
  1515. var
  1516. repCount, val: Byte;
  1517. repeating: boolean;
  1518. begin
  1519. repeating := true;
  1520. while ASize > 0 do
  1521. begin
  1522. val := AData^; inc(AData); repCount := 1; dec(ASize);
  1523. while (ASize > 0) and (AData^ = val) and (repCount < 254) do
  1524. begin
  1525. inc(AData);
  1526. dec(ASize);
  1527. inc(repCount);
  1528. end;
  1529. if (repCount > 2) or (ASize = 0) then
  1530. begin
  1531. if not repeating then AStream.WriteByte(0);
  1532. AStream.WriteByte(repCount);
  1533. AStream.WriteByte(val);
  1534. repeating := false;
  1535. end else
  1536. begin
  1537. while (ASize > 1) and (repCount < 253) and ((AData^ <> (AData-1)^) or ((AData+1)^ <> (AData-1)^)) do
  1538. begin
  1539. inc(AData, 2);
  1540. dec(ASize, 2);
  1541. inc(repCount, 2);
  1542. end;
  1543. if (ASize = 1) and (repCount < 254) then
  1544. begin
  1545. inc(AData);
  1546. dec(ASize);
  1547. inc(repCount);
  1548. end;
  1549. if repeating then AStream.WriteByte(0);
  1550. AStream.WriteByte(repCount);
  1551. AStream.WriteBuffer((AData-repCount)^, repCount);
  1552. repeating := true;
  1553. end;
  1554. end;
  1555. AStream.WriteByte(255);
  1556. end;
  1557. procedure DecodeSimpleRLE(AData: PByte; ASize: integer; AStream: TStream);
  1558. var
  1559. repCount, val: Byte;
  1560. begin
  1561. while ASize > 0 do
  1562. begin
  1563. repCount := AStream.ReadByte;
  1564. if repCount = 255 then break;
  1565. if repCount > 0 then
  1566. begin
  1567. val := AStream.ReadByte;
  1568. if repCount > ASize then repCount := ASize;
  1569. fillchar(AData^, repCount, val);
  1570. inc(AData, repCount);
  1571. dec(ASize, repCount);
  1572. end;
  1573. repCount := AStream.ReadByte;
  1574. if repCount = 255 then break;
  1575. if repCount > 0 then
  1576. begin
  1577. if repCount > ASize then repCount := ASize;
  1578. AStream.ReadBuffer(AData^, repCount);
  1579. inc(AData, repCount);
  1580. dec(ASize, repCount);
  1581. end;
  1582. end;
  1583. while ASize > 0 do
  1584. begin
  1585. AData^ := 0;
  1586. inc(AData);
  1587. dec(ASize);
  1588. end;
  1589. end;
  1590. procedure StoreMask(var AMaskId: int64; AMask: TGrayscaleMask; AMaskOffset: TPoint);
  1591. var
  1592. imgStream: TMemoryStream;
  1593. renderObj: TBGRACustomOriginalStorage;
  1594. begin
  1595. imgStream := TMemoryStream.Create;
  1596. EncodeSimpleRLE(AMask.Data, AMask.NbPixels, imgStream);
  1597. if AMaskId = 0 then
  1598. begin
  1599. inc(FCurBrokenLineImageId);
  1600. AMaskId := FCurBrokenLineImageId;
  1601. end;
  1602. renderObj := tempStorage.CreateObject(inttostr(AMaskId));
  1603. renderObj.PointF['size'] := PointF(AMask.Width, AMask.Height);
  1604. renderObj.PointF['offset'] := PointF(AMaskOffset) - PointF(ARenderOffset);
  1605. renderObj.WriteFile('mask.data', imgStream, false, true);
  1606. renderObj.Free;
  1607. tempRenderNewList.Add(inttostr(AMaskId));
  1608. end;
  1609. function LoadStoredMask(AMaskId: integer; out AMask: TGrayscaleMask; out AOffset: TPoint): boolean;
  1610. var
  1611. imgStream: TStream;
  1612. renderObj: TBGRACustomOriginalStorage;
  1613. size: TPoint;
  1614. begin
  1615. AMask := nil;
  1616. AOffset := Point(0,0);
  1617. result := false;
  1618. renderObj := tempStorage.OpenObject(inttostr(AMaskId));
  1619. if Assigned(renderObj) then
  1620. begin
  1621. size := renderObj.PointF['size'].Round;
  1622. AOffset := (renderObj.PointF['offset'] + PointF(ARenderOffset)).Round;
  1623. imgStream := renderObj.GetFileStream('mask.data');
  1624. if not Assigned(imgStream) or (imgStream.Size = 0) then
  1625. begin
  1626. renderObj.Free;
  1627. exit;
  1628. end;
  1629. tempRenderNewList.Add(inttostr(AMaskId));
  1630. AMask := TGrayscaleMask.Create;
  1631. AMask.SetSize(size.x, size.y);
  1632. imgStream.Position := 0;
  1633. DecodeSimpleRLE(AMask.Data, AMask.NbPixels, imgStream);
  1634. renderObj.Free;
  1635. result := true;
  1636. end;
  1637. end;
  1638. procedure RenderFromStoredMask(ADestination: TBGRABitmap; AMaskId: integer; AFill: TVectorialFill);
  1639. var
  1640. brokenRenderOfs: TPoint;
  1641. tmpBrokenMask: TGrayscaleMask;
  1642. begin
  1643. if LoadStoredMask(AMaskId, tmpBrokenMask, brokenRenderOfs) then
  1644. begin
  1645. RenderFromMask(ADestination, brokenRenderOfs.X - transfRect.Left,
  1646. brokenRenderOfs.Y - transfRect.Top, tmpBrokenMask,
  1647. Point(- transfRect.Left, - transfRect.Top), AFill);
  1648. tmpBrokenMask.Free;
  1649. end;
  1650. end;
  1651. procedure RenderFromStoredMask(ADestination: TGrayscaleMask; AMaskId: integer);
  1652. var
  1653. brokenRenderOfs: TPoint;
  1654. tmpBrokenMask: TGrayscaleMask;
  1655. begin
  1656. if LoadStoredMask(AMaskId, tmpBrokenMask, brokenRenderOfs) then
  1657. begin
  1658. RenderFromMask(ADestination, brokenRenderOfs.X - transfRect.Left,
  1659. brokenRenderOfs.Y - transfRect.Top, tmpBrokenMask);
  1660. tmpBrokenMask.Free;
  1661. end;
  1662. end;
  1663. procedure RenderFromStoredImage(ADestination: TBGRABitmap; AImageId: integer);
  1664. var
  1665. renderObj: TBGRACustomOriginalStorage;
  1666. brokenRenderOfs, size: TPoint;
  1667. imgStream: TStream;
  1668. tmpBroken: TBGRAMemoryStreamBitmap;
  1669. begin
  1670. renderObj := tempStorage.OpenObject(inttostr(AImageId));
  1671. if Assigned(renderObj) then
  1672. begin
  1673. size := renderObj.PointF['size'].Round;
  1674. brokenRenderOfs := (renderObj.PointF['offset'] + PointF(ARenderOffset)).Round;
  1675. imgStream := renderObj.GetFileStream('image.data');
  1676. if (imgStream = nil) or (imgStream.Size = 0) then
  1677. begin
  1678. renderObj.Free;
  1679. exit;
  1680. end;
  1681. tempRenderNewList.Add(inttostr(AImageId));
  1682. tmpBroken := TBGRAMemoryStreamBitmap.Create(size.x, size.y,
  1683. imgStream as TMemoryStream, 0, false);
  1684. ADestination.PutImage(brokenRenderOfs.X - transfRect.Left,
  1685. brokenRenderOfs.Y - transfRect.Top,
  1686. tmpBroken, dmDrawWithTransparency);
  1687. tmpBroken.Free;
  1688. renderObj.Free;
  1689. end;
  1690. end;
  1691. procedure ApplyClipBox(ADest: TCustomUniversalBitmap);
  1692. var
  1693. maskBox: TAffineBox;
  1694. wholeImage: TAffineBox;
  1695. begin
  1696. maskBox := AffineMatrixTranslation(-transfRect.Left,-transfRect.Top) * m *
  1697. TAffineBox.AffineBox(sourceRectF);
  1698. wholeImage := TAffineBox.AffineBox(PointF(-1,-1), PointF(-1,ADest.Height+1),
  1699. PointF(ADest.Width+1,-1));
  1700. ADest.FillMode := fmAlternate;
  1701. ADest.ErasePolyAntialias( ConcatPointsF([wholeImage.AsPolygon, maskBox.AsPolygon], true),
  1702. 255, false);
  1703. end;
  1704. begin
  1705. RetrieveRenderStorage(AMatrix, transfRect, tmpTransf);
  1706. if Assigned(tmpTransf) then
  1707. begin
  1708. ADest.PutImage(transfRect.Left + ARenderOffset.X, transfRect.Top + ARenderOffset.Y,
  1709. tmpTransf, dmDrawWithTransparency);
  1710. tmpTransf.Free;
  1711. exit;
  1712. end;
  1713. hasPen := not PenFill.IsFullyTransparent;
  1714. if not hasPen and not HasOutline then exit;
  1715. SetGlobalMatrix(AffineMatrixTranslation(ARenderOffset.X,ARenderOffset.Y)*AMatrix);
  1716. zoom := GetTextRenderZoom;
  1717. if zoom = 0 then exit;
  1718. fr := GetFontRenderer;
  1719. if fr.FontEmHeight = 0 then exit;
  1720. pad := fr.FontEmHeight;
  1721. m := FGlobalMatrix* //global transform
  1722. GetUntransformedMatrix* //transform according to shape rectangle
  1723. AffineMatrixScale(1/zoom,1/zoom); //shrink zoomed text if necessary
  1724. tl := GetTextLayout;
  1725. sourceRectF := RectF(-pad,0,tl.AvailableWidth+pad,min(tl.TotalTextHeight,tl.AvailableHeight));
  1726. m *= GetVerticalAlignMatrix(tl);
  1727. if CanHaveRenderStorage then
  1728. begin
  1729. storage := OpenRenderStorage(true);
  1730. tempStorage := storage.temporary;
  1731. end else
  1732. begin
  1733. storage := TShapeRenderStorage.None;
  1734. tempStorage := TemporaryStorage;
  1735. end;
  1736. if Assigned(tempStorage) then
  1737. begin
  1738. tempRenderNewList := TStringList.Create;
  1739. useBrokenLinesRender := not ADraft and (Usermode = vsuEditText);
  1740. if not tempStorage.AffineMatrixEquals('last-matrix', AMatrix) or
  1741. not tempStorage.PointFEquals('origin', Origin) or
  1742. not tempStorage.PointFEquals('x-axis', XAxis) or
  1743. not tempStorage.PointFEquals('y-axis', YAxis) or
  1744. not tempStorage.FloatEquals('em-height', FontEmHeight) or
  1745. not useBrokenLinesRender then
  1746. begin
  1747. //all temp files that are obsolete
  1748. tempRenderCurList := TStringList.Create;
  1749. tempStorage.EnumerateObjects(tempRenderCurList);
  1750. for fileIdx := 0 to tempRenderCurList.Count-1 do
  1751. tempStorage.RemoveObject(tempRenderCurList[fileIdx]);
  1752. tempRenderCurList.Free;
  1753. tempStorage.RemoveAttribute('last-matrix');
  1754. tempStorage.RemoveAttribute('origin');
  1755. tempStorage.RemoveAttribute('x-axis');
  1756. tempStorage.RemoveAttribute('y-axis');
  1757. tempStorage.RemoveAttribute('em-height');
  1758. tempStorage.RemoveAttribute('outline-width');
  1759. tempStorage.RemoveObject('pen-phong');
  1760. end;
  1761. end else
  1762. begin
  1763. tempRenderNewList := nil;
  1764. useBrokenLinesRender := false;
  1765. end;
  1766. storeImage := CanHaveRenderStorage and not ADraft; // and not useBrokenLinesRender;
  1767. if storeImage or useBrokenLinesRender then
  1768. destF := rectF(0,0,ADest.Width,ADest.Height)
  1769. else
  1770. destF := RectF(ADest.ClipRect.Left,ADest.ClipRect.Top,ADest.ClipRect.Right,ADest.ClipRect.Bottom);
  1771. transfRectF := (m*TAffineBox.AffineBox(sourceRectF)).RectBoundsF;
  1772. transfRectF := TRectF.Intersect(transfRectF, destF);
  1773. if not IsAffineMatrixInversible(m) then
  1774. begin
  1775. tempRenderNewList.Free;
  1776. exit;
  1777. end;
  1778. sourceInvRect := (AffineMatrixInverse(m)*TAffineBox.AffineBox(transfRectF)).RectBoundsF;
  1779. sourceInvRect.Top := floor(sourceInvRect.Top);
  1780. sourceInvRect.Bottom := ceil(sourceInvRect.Bottom);
  1781. sourceRectF := TRectF.Intersect(sourceRectF,sourceInvRect);
  1782. if IsEmptyRectF(sourceRectF) then
  1783. begin
  1784. tempRenderNewList.Free;
  1785. exit;
  1786. end;
  1787. sourceRectF.Left := floor(sourceRectF.Left);
  1788. sourceRectF.Top := floor(sourceRectF.Top);
  1789. sourceRectF.Right := floor(sourceRectF.Right);
  1790. sourceRectF.Bottom := sourceRectF.Bottom;
  1791. with transfRectF do
  1792. transfRect := Rect(floor(Left),floor(Top),ceil(Right),ceil(Bottom));
  1793. if PenPhong and Assigned(tempStorage) then
  1794. begin
  1795. phongObj := tempStorage.OpenObject('pen-phong');
  1796. penPhongChange := not Assigned(phongObj) or
  1797. not phongObj.PointFEquals('light-pos', LightPosition) or
  1798. not phongObj.FloatEquals('altitude-percent', AltitudePercent) or
  1799. (phongObj.Int['fill-iteration'] <> FPenFillIteration);
  1800. phongObj.Free;
  1801. end else
  1802. penPhongChange := false;
  1803. if HasOutline then
  1804. begin
  1805. outlineRenderWidth := zoom*OutlineWidth;
  1806. outlineWidthChange := Assigned(tempStorage) and
  1807. (tempStorage.Float['outline-width'] <> OutlineWidth);
  1808. end else
  1809. begin
  1810. outlineRenderWidth := 0;
  1811. outlineWidthChange := false;
  1812. end;
  1813. if useBrokenLinesRender then
  1814. begin
  1815. brokenLineBoundsF := EmptyRectF;
  1816. if HasOutline then
  1817. tmpTransfOutline := TGrayscaleMask.Create(transfRect.Width, transfRect.Height)
  1818. else tmpTransfOutline := nil;
  1819. //render each broken line independently
  1820. setlength(FParagraphLayout, tl.ParagraphCount);
  1821. for paraIndex := 0 to tl.ParagraphCount-1 do
  1822. with FParagraphLayout[paraIndex] do
  1823. begin
  1824. startBrokenIndex := tl.ParagraphStartBrokenLine[paraIndex];
  1825. endBrokenIndex := tl.ParagraphEndBrokenLine[paraIndex];
  1826. setlength(FParagraphLayout[paraIndex].brokenLines, endBrokenIndex - startBrokenIndex);
  1827. for brokenIndex := startBrokenIndex to endBrokenIndex-1 do
  1828. with brokenLines[brokenIndex - startBrokenIndex] do
  1829. begin
  1830. redrawPen := hasPen and
  1831. (
  1832. (PenPhong and (penPhongChange or (penImageId = 0) or
  1833. not tempStorage.ObjectExists(inttostr(penImageId))) ) or
  1834. (not PenPhong and ((penMaskId = 0) or
  1835. not tempStorage.ObjectExists(inttostr(penMaskId))) )
  1836. );
  1837. redrawOutline := HasOutline and ((outlineMaskId = 0) or outlineWidthChange or
  1838. not tempStorage.ObjectExists(inttostr(outlineMaskId)) );
  1839. if redrawPen or redrawOutline then
  1840. ComputeBrokenLinesPath(brokenIndex, brokenIndex+1);
  1841. if redrawOutline then
  1842. begin
  1843. with brokenLineBoundsF do
  1844. brokenLineRectBounds := Rect(floor(Left - outlineRenderWidth/2),
  1845. floor(Top - outlineRenderWidth/2), ceil(Right + outlineRenderWidth/2),
  1846. ceil(Bottom + outlineRenderWidth/2));
  1847. tmpRenderRect := TRect.Intersect(brokenLineRectBounds, transfRect);
  1848. brokenRenderOfs := tmpRenderRect.TopLeft;
  1849. tmpBrokenMask := TGrayscaleMask.Create(tmpRenderRect.Width, tmpRenderRect.Height);
  1850. RenderOutlineMask(tmpBrokenMask, Point(-brokenRenderOfs.X, -brokenRenderOfs.Y));
  1851. RenderFromMask(tmpTransfOutline, brokenRenderOfs.X - transfRect.Left,
  1852. brokenRenderOfs.Y - transfRect.Top, tmpBrokenMask);
  1853. StoreMask(outlineMaskId, tmpBrokenMask, brokenRenderOfs);
  1854. tmpBrokenMask.Free;
  1855. end else
  1856. if HasOutline then
  1857. RenderFromStoredMask(tmpTransfOutline, outlineMaskId);
  1858. if redrawPen then
  1859. begin
  1860. if PenPhong then
  1861. begin
  1862. with brokenLineBoundsF do
  1863. brokenLineRectBounds := Rect(floor(Left), floor(Top), ceil(Right), ceil(Bottom));
  1864. tmpRenderRect := TRect.Intersect(brokenLineRectBounds, transfRect);
  1865. brokenRenderOfs := tmpRenderRect.TopLeft;
  1866. tmpBroken := TBGRAMemoryStreamBitmap.Create(tmpRenderRect.Width, tmpRenderRect.Height);
  1867. RenderPen(tmpBroken, Point(- brokenRenderOfs.X,
  1868. - brokenRenderOfs.Y));
  1869. StoreRGBAImage(penImageId, tmpBroken, brokenRenderOfs);
  1870. tmpBroken.Free;
  1871. end else
  1872. begin
  1873. with brokenLineBoundsF do
  1874. brokenLineRectBounds := Rect(floor(Left), floor(Top), ceil(Right), ceil(Bottom));
  1875. tmpRenderRect := TRect.Intersect(brokenLineRectBounds, transfRect);
  1876. brokenRenderOfs := tmpRenderRect.TopLeft;
  1877. tmpBrokenMask := TGrayscaleMask.Create(tmpRenderRect.Width, tmpRenderRect.Height);
  1878. RenderPenMask(tmpBrokenMask, Point(-brokenRenderOfs.X, -brokenRenderOfs.Y));
  1879. StoreMask(penMaskId, tmpBrokenMask, brokenRenderOfs);
  1880. tmpBrokenMask.Free;
  1881. end;
  1882. end;
  1883. end;
  1884. end;
  1885. tmpTransf := TBGRABitmap.Create(transfRect.Width, transfRect.Height);
  1886. if Assigned(tmpTransfOutline) then
  1887. begin
  1888. ApplyClipBox(tmpTransfOutline);
  1889. RenderFromMask(tmpTransf, 0, 0, tmpTransfOutline,
  1890. Point(-transfRect.Left, -transfRect.Top), OutlineFill);
  1891. tmpTransfOutline.Free;
  1892. end;
  1893. if hasPen then
  1894. for paraIndex := 0 to tl.ParagraphCount-1 do
  1895. with FParagraphLayout[paraIndex] do
  1896. begin
  1897. for brokenIndex := 0 to high(brokenLines) do
  1898. with brokenLines[brokenIndex] do
  1899. begin
  1900. if PenPhong then RenderFromStoredImage(tmpTransf, penImageId)
  1901. else RenderFromStoredMask(tmpTransf, penMaskId, PenFill);
  1902. end;
  1903. end;
  1904. end else
  1905. begin
  1906. tmpTransf := TBGRABitmap.Create(transfRect.Width, transfRect.Height);
  1907. RenderBrokenLines(tmpTransf, 0, tl.BrokenLineCount, Point(-transfRect.Left, -transfRect.Top));
  1908. //make list of temp files to keep
  1909. if Assigned(tempStorage) and Assigned(tempRenderNewList) then
  1910. for paraIndex := 0 to high(FParagraphLayout) do
  1911. with FParagraphLayout[paraIndex] do
  1912. for brokenIndex := 0 to high(brokenLines) do
  1913. with brokenLines[brokenIndex] do
  1914. begin
  1915. if hasPen then
  1916. begin
  1917. if not PenPhong and (penImageId <> 0) then
  1918. tempRenderNewList.Add(inttostr(penImageId));
  1919. if PenPhong and not penPhongChange and (penMaskId <> 0) then
  1920. tempRenderNewList.Add(inttostr(penMaskId));
  1921. end;
  1922. if HasOutline and (outlineMaskId <> 0) and not outlineWidthChange then
  1923. tempRenderNewList.Add(inttostr(outlineMaskId));
  1924. end;
  1925. end;
  1926. if Assigned(tempStorage) then
  1927. begin
  1928. //remove temp files that are obsolete
  1929. tempRenderCurList := TStringList.Create;
  1930. tempStorage.EnumerateObjects(tempRenderCurList);
  1931. for fileIdx := 0 to tempRenderCurList.Count-1 do
  1932. if tempRenderNewList.IndexOf(tempRenderCurList[fileIdx]) = -1 then
  1933. tempStorage.RemoveObject(tempRenderCurList[fileIdx]);
  1934. tempRenderCurList.Free;
  1935. tempStorage.AffineMatrix['last-matrix'] := AMatrix;
  1936. tempStorage.PointF['origin'] := Origin;
  1937. tempStorage.PointF['x-axis'] := XAxis;
  1938. tempStorage.PointF['y-axis'] := YAxis;
  1939. tempStorage.Float['em-height'] := FontEmHeight;
  1940. if HasOutline then
  1941. tempStorage.Float['outline-width'] := OutlineWidth
  1942. else tempStorage.RemoveAttribute('outline-width');
  1943. if PenPhong then
  1944. begin
  1945. phongObj := tempStorage.CreateObject('pen-phong');
  1946. phongObj.PointF['light-pos'] := LightPosition;
  1947. phongObj.Float['altitude-percent'] := AltitudePercent;
  1948. phongObj.Int['fill-iteration'] := FPenFillIteration;
  1949. phongObj.Free;
  1950. end else
  1951. tempStorage.RemoveObject('pen-phong');
  1952. end;
  1953. storage.Close;
  1954. tempRenderNewList.Free;
  1955. ApplyClipBox(tmpTransf);
  1956. ADest.PutImage(transfRect.Left, transfRect.Top, tmpTransf, dmDrawWithTransparency);
  1957. transfRect.Offset(-ARenderOffset.X,-ARenderOffset.Y);
  1958. if storeImage then UpdateRenderStorage(transfRect, tmpTransf)
  1959. else UpdateRenderStorage(transfRect);
  1960. tmpTransf.Free;
  1961. end;
  1962. function TTextShape.GetRenderBounds(ADestRect: TRect; AMatrix: TAffineMatrix;
  1963. AOptions: TRenderBoundsOptions): TRectF;
  1964. var
  1965. ab: TAffineBox;
  1966. u: TPointF;
  1967. lenU, margin: Single;
  1968. begin
  1969. if (GetPenVisible(rboAssumePenFill in AOptions) or HasOutline) and
  1970. (Text <> '') then
  1971. begin
  1972. ab := GetAffineBox(AMatrix, false);
  1973. //add margin for text that would be out of bound (for example italic j)
  1974. u := ab.TopRight-ab.TopLeft;
  1975. lenU := VectLen(u);
  1976. if lenU<>0 then u *= (1/lenU);
  1977. margin := FontEmHeight;
  1978. u *= margin;
  1979. ab.TopLeft -= u;
  1980. ab.TopRight += u;
  1981. ab.BottomLeft -= u;
  1982. result := ab.RectBoundsF;
  1983. end
  1984. else
  1985. result:= EmptyRectF;
  1986. end;
  1987. function TTextShape.PointInShape(APoint: TPointF): boolean;
  1988. begin
  1989. result := GetAffineBox(AffineMatrixIdentity,true).Contains(APoint);
  1990. end;
  1991. function TTextShape.PointInShape(APoint: TPointF; ARadius: single): boolean;
  1992. begin
  1993. result := false;
  1994. end;
  1995. function TTextShape.PointInPen(APoint: TPointF): boolean;
  1996. var
  1997. tl: TBidiTextLayout;
  1998. pt: TPointF;
  1999. i: Integer;
  2000. untransformed: TAffineMatrix;
  2001. begin
  2002. if not GetAffineBox(AffineMatrixIdentity,true).Contains(APoint) then
  2003. exit(false);
  2004. SetGlobalMatrix(AffineMatrixIdentity);
  2005. tl := GetTextLayout;
  2006. untransformed := GetUntransformedMatrix;
  2007. if not IsAffineMatrixInversible(untransformed) then exit(false);
  2008. pt := AffineMatrixInverse(untransformed)*APoint;
  2009. for i := 0 to tl.PartCount-1 do
  2010. if tl.PartAffineBox[i].Contains(pt) then exit(true);
  2011. result := false;
  2012. end;
  2013. function TTextShape.GetIsSlow(const AMatrix: TAffineMatrix): boolean;
  2014. begin
  2015. Result:= true;
  2016. end;
  2017. function TTextShape.GetGenericCost: integer;
  2018. begin
  2019. Result:= 10;
  2020. end;
  2021. procedure TTextShape.MouseMove(Shift: TShiftState; X, Y: single;
  2022. var ACursor: TOriginalEditorCursor; var AHandled: boolean);
  2023. begin
  2024. if FMouseSelecting then
  2025. begin
  2026. SelectWithMouse(X,Y, true);
  2027. ACursor := oecText;
  2028. AHandled:= true;
  2029. end else
  2030. begin
  2031. inherited MouseMove(Shift, X, Y, ACursor, AHandled);
  2032. if (ACursor = oecDefault) and PointInShape(PointF(X,Y)) then ACursor := oecText;
  2033. end;
  2034. end;
  2035. procedure TTextShape.MouseDown(RightButton: boolean; ClickCount: integer; Shift: TShiftState; X,
  2036. Y: single; var ACursor: TOriginalEditorCursor; var AHandled: boolean);
  2037. begin
  2038. inherited MouseDown(RightButton, ClickCount, Shift, X, Y, ACursor, AHandled);
  2039. if not AHandled and not RightButton and PointInShape(PointF(X,Y)) then
  2040. begin
  2041. case ClickCount mod 3 of
  2042. 1: begin
  2043. FMouseSelecting:= true;
  2044. SelectWithMouse(X,Y, ssShift in Shift);
  2045. end;
  2046. 2: SelectWordWithMouse(X, Y);
  2047. 0 {3}: SelectParagraphWithMouse(X, Y)
  2048. end;
  2049. AHandled:= true;
  2050. end;
  2051. if (ACursor = oecDefault) and PointInShape(PointF(X,Y)) then ACursor := oecText;
  2052. end;
  2053. procedure TTextShape.MouseUp(RightButton: boolean; Shift: TShiftState; X,
  2054. Y: single; var ACursor: TOriginalEditorCursor; var AHandled: boolean);
  2055. begin
  2056. if FMouseSelecting and not RightButton then
  2057. begin
  2058. FMouseSelecting:= false;
  2059. ACursor := oecText;
  2060. AHandled:= true;
  2061. end else
  2062. begin
  2063. inherited MouseUp(RightButton, Shift, X, Y, ACursor, AHandled);
  2064. if (ACursor = oecDefault) and PointInShape(PointF(X,Y)) then ACursor := oecText;
  2065. end;
  2066. end;
  2067. procedure TTextShape.KeyDown(Shift: TShiftState; Key: TSpecialKey;
  2068. var AHandled: boolean);
  2069. var
  2070. idxPara, newPos: Integer;
  2071. tl: TBidiTextLayout;
  2072. begin
  2073. if (FTextLayout = nil) or (Usermode <> vsuEditText) then exit;
  2074. if Key = skDelete then
  2075. begin
  2076. if FSelStart <> FSelEnd then DeleteSelection
  2077. else DeleteTextAfter(1);
  2078. AHandled:= true;
  2079. end else
  2080. if (Key in [skLeft,skRight]) and not (ssAlt in Shift) then
  2081. begin
  2082. tl := GetTextLayout;
  2083. if (Key = skLeft) xor tl.ParagraphRightToLeft[tl.GetParagraphAt(FSelEnd)] then
  2084. begin
  2085. BeginEditingUpdate;
  2086. if FSelEnd > 0 then
  2087. Dec(FSelEnd, tl.IncludeNonSpacingCharsBefore(FSelEnd,1) );
  2088. if not (ssShift in Shift) then FSelStart := FSelEnd;
  2089. EndEditingUpdate;
  2090. end else
  2091. begin
  2092. BeginEditingUpdate;
  2093. if FSelEnd < tl.CharCount then
  2094. Inc(FSelEnd, tl.IncludeNonSpacingChars(FSelEnd,1) );
  2095. if not (ssShift in Shift) then FSelStart := FSelEnd;
  2096. EndEditingUpdate;
  2097. end;
  2098. AHandled := true;
  2099. end else
  2100. if (Key in [skUp,skDown]) and not (ssAlt in Shift) then
  2101. begin
  2102. tl := GetTextLayout;
  2103. if Key = skUp then
  2104. newPos := tl.FindTextAbove(FSelEnd)
  2105. else
  2106. newPos := tl.FindTextBelow(FSelEnd);
  2107. if (newPos <> -1) or (not (ssShift in Shift) and (FSelStart <> FSelEnd)) then
  2108. begin
  2109. BeginEditingUpdate;
  2110. FSelEnd := newPos;
  2111. if not (ssShift in Shift) then FSelStart := FSelEnd;
  2112. EndEditingUpdate;
  2113. end;
  2114. AHandled:= true;
  2115. end else
  2116. if Key = skHome then
  2117. begin
  2118. tl := GetTextLayout;
  2119. BeginEditingUpdate;
  2120. if ssCtrl in Shift then
  2121. FSelEnd := 0
  2122. else
  2123. begin
  2124. idxPara := tl.GetParagraphAt(FSelEnd);
  2125. FSelEnd := tl.ParagraphStartIndex[idxPara];
  2126. end;
  2127. if not (ssShift in Shift) then FSelStart := FSelEnd;
  2128. EndEditingUpdate;
  2129. AHandled := true;
  2130. end else
  2131. if Key = skEnd then
  2132. begin
  2133. tl := GetTextLayout;
  2134. BeginEditingUpdate;
  2135. if ssCtrl in Shift then
  2136. FSelEnd := tl.CharCount
  2137. else
  2138. begin
  2139. idxPara := tl.GetParagraphAt(FSelEnd);
  2140. FSelEnd := tl.ParagraphEndIndexBeforeParagraphSeparator[idxPara];
  2141. end;
  2142. if not (ssShift in Shift) then FSelStart := FSelEnd;
  2143. EndEditingUpdate;
  2144. AHandled := true;
  2145. end else
  2146. if (Key = skReturn) and ([ssCtrl,ssShift] <= Shift) and FEnteringUnicode then
  2147. begin
  2148. InsertUnicodeValue;
  2149. AHandled:= true;
  2150. end else
  2151. if Key = skReturn then
  2152. begin
  2153. if ssShift in Shift then
  2154. InsertText(UnicodeCharToUTF8(UNICODE_LINE_SEPARATOR))
  2155. else
  2156. InsertText(#10);
  2157. AHandled := true;
  2158. end else
  2159. if Key = skTab then
  2160. begin
  2161. InsertText(#9);
  2162. AHandled := true;
  2163. end else
  2164. if (Key = skU) and ([ssCtrl,ssShift] <= Shift) then
  2165. begin
  2166. if FEnteringUnicode then InsertUnicodeValue;
  2167. FEnteringUnicode:= true;
  2168. FUnicodeValue:= 0;
  2169. FUnicodeDigitCount:= 0;
  2170. AHandled := true;
  2171. end else
  2172. if (Key in[sk0..sk9,skNum0..skNum9,skA..skF]) and ([ssCtrl,ssShift] <= Shift) and FEnteringUnicode then
  2173. begin
  2174. if FUnicodeDigitCount >= 8 then FEnteringUnicode:= false else
  2175. begin
  2176. FUnicodeValue := (FUnicodeValue shl 4);
  2177. case Key of
  2178. sk0..sk9: inc(FUnicodeValue, ord(Key)-ord(sk0));
  2179. skNum0..skNum9: inc(FUnicodeValue, ord(Key)-ord(sk0));
  2180. skA..skF: inc(FUnicodeValue, ord(Key)-ord(skA)+10);
  2181. end;
  2182. end;
  2183. end else
  2184. if (Key = skC) and (ssCtrl in Shift) then
  2185. begin
  2186. if CopySelection then AHandled:= true;
  2187. end else
  2188. if (Key = skX) and (ssCtrl in Shift) then
  2189. begin
  2190. if CutSelection then AHandled:= true;
  2191. end else
  2192. if (Key = skV) and (ssCtrl in Shift) then
  2193. begin
  2194. if PasteSelection then AHandled := true;
  2195. end else
  2196. if (Key = skA) and (ssCtrl in Shift) then
  2197. begin
  2198. SelectAll;
  2199. AHandled := true;
  2200. end;
  2201. end;
  2202. procedure TTextShape.KeyPress(UTF8Key: string; var AHandled: boolean);
  2203. begin
  2204. if (Usermode = vsuEditText) and (UTF8Key = #8) then
  2205. begin
  2206. if FSelEnd <> FSelStart then DeleteSelection
  2207. else DeleteTextBefore(1);
  2208. AHandled := true;
  2209. end else
  2210. if UTF8Key >= ' ' then
  2211. begin
  2212. if Usermode <> vsuEditText then
  2213. begin
  2214. if Text = '' then
  2215. begin
  2216. Usermode := vsuEditText;
  2217. InsertText(UTF8Key);
  2218. AHandled := true;
  2219. end;
  2220. end else
  2221. begin
  2222. InsertText(UTF8Key);
  2223. AHandled := true;
  2224. end;
  2225. end;
  2226. end;
  2227. procedure TTextShape.KeyUp(Shift: TShiftState; Key: TSpecialKey;
  2228. var AHandled: boolean);
  2229. begin
  2230. if (Key in[skCtrl,skShift]) and FEnteringUnicode then
  2231. begin
  2232. InsertUnicodeValue;
  2233. AHandled := true;
  2234. end;
  2235. end;
  2236. procedure TTextShape.SetFontNameAndStyle(AFontName: string;
  2237. AFontStyle: TFontStyles);
  2238. begin
  2239. if (AFontName <> FFontName) or (AFontStyle <> FFontStyle) then
  2240. begin
  2241. BeginUpdate(TTextShapeFontDiff);
  2242. FFontName := AFontName;
  2243. FFontStyle:= AFontStyle;
  2244. EndUpdate;
  2245. end;
  2246. end;
  2247. function TTextShape.CopySelection: boolean;
  2248. var
  2249. stream: TStringStream;
  2250. begin
  2251. if HasSelection then
  2252. begin
  2253. stream := nil;
  2254. try
  2255. Clipboard.Clear;
  2256. stream := TStringStream.Create(GetTextLayout.CopyText(min(FSelStart,FSelEnd),abs(FSelEnd-FSelStart)));
  2257. Clipboard.SetFormat(PredefinedClipboardFormat(pcfText), stream);
  2258. finally
  2259. stream.Free;
  2260. end;
  2261. result := true;
  2262. end
  2263. else result := false;
  2264. end;
  2265. function TTextShape.CutSelection: boolean;
  2266. begin
  2267. result := CopySelection;
  2268. if result then DeleteSelection;
  2269. end;
  2270. function TTextShape.PasteSelection: boolean;
  2271. var
  2272. txt: String;
  2273. begin
  2274. if CanPasteSelection then
  2275. begin
  2276. txt := Clipboard.AsText;
  2277. txt := StringReplace(txt, #13#10, #10, [rfReplaceAll]);
  2278. txt := StringReplace(txt, #10#13, #10, [rfReplaceAll]);
  2279. txt := StringReplace(txt, #13, #10, [rfReplaceAll]);
  2280. txt := StringReplace(txt, UnicodeCharToUTF8(UNICODE_PARAGRAPH_SEPARATOR), #10, [rfReplaceAll]);
  2281. txt := StringReplace(txt, UnicodeCharToUTF8(UNICODE_NEXT_LINE), #10, [rfReplaceAll]);
  2282. InsertText(txt);
  2283. result := true;
  2284. end else
  2285. result := false;
  2286. end;
  2287. procedure TTextShape.Transform(const AMatrix: TAffineMatrix);
  2288. var
  2289. zoom: Single;
  2290. begin
  2291. BeginUpdate;
  2292. AddDiffHandler(TTextShapeFontDiff);
  2293. AddDiffHandler(TTextShapePhongDiff);
  2294. zoom := (VectLen(AMatrix[1,1],AMatrix[2,1])+VectLen(AMatrix[1,2],AMatrix[2,2]))/2;
  2295. FontEmHeight:= zoom*FontEmHeight;
  2296. LightPosition := AMatrix*LightPosition;
  2297. inherited Transform(AMatrix);
  2298. EndUpdate;
  2299. end;
  2300. class function TTextShape.StorageClassName: RawByteString;
  2301. begin
  2302. result := 'text';
  2303. end;
  2304. class function TTextShape.Usermodes: TVectorShapeUsermodes;
  2305. begin
  2306. Result:=inherited Usermodes + [vsuEditText];
  2307. end;
  2308. function TTextShape.AppendToSVG(AContent: TSVGContent; ADefs: TSVGDefine): TSVGElement;
  2309. var
  2310. topLeft, u, v: TPointF;
  2311. w, h, zoom: Single;
  2312. t: TSVGText;
  2313. tl: TBidiTextLayout;
  2314. i: Integer;
  2315. span: TSVGTSpan;
  2316. fm: TFontPixelMetric;
  2317. rF: TRectF;
  2318. penFillId, outlineFillId: String;
  2319. a: ArrayOfTFloatWithCSSUnit;
  2320. begin
  2321. topLeft := Origin - (XAxis - Origin) - (YAxis - Origin);
  2322. w := Width*2; h := Height*2;
  2323. result := AContent.AppendText(topLeft, '');
  2324. if (XAxis.y <> 0) or (YAxis.x <> 0) then
  2325. begin
  2326. u := XAxis - Origin;
  2327. if w > 0 then u *= (2/w);
  2328. v := YAxis - Origin;
  2329. if h > 0 then v *= (2/h);
  2330. result.matrix[cuPixel] := AffineMatrixTranslation(topLeft.X, topLeft.Y) *
  2331. AffineMatrix(u, v, PointF(0, 0)) *
  2332. AffineMatrixTranslation(-topLeft.X, -topLeft.Y);
  2333. end;
  2334. if PenVisible then
  2335. begin
  2336. if IsAffineMatrixInversible(result.Matrix[cuPixel]) then
  2337. penFillId := AppendVectorialFillToSVGDefs(PenFill,
  2338. AffineMatrixInverse(result.Matrix[cuPixel]), ADefs, 'fill')
  2339. else penFillId := '';
  2340. if penFillId <> '' then
  2341. result.fill := 'url(#' + penFillId + ')'
  2342. else result.fillColor := PenColor;
  2343. end
  2344. else result.fillNone;
  2345. if OutlineVisible then
  2346. begin
  2347. if IsAffineMatrixInversible(result.Matrix[cuPixel]) then
  2348. outlineFillId := AppendVectorialFillToSVGDefs(OutlineFill,
  2349. AffineMatrixInverse(result.Matrix[cuPixel]), ADefs, 'stroke')
  2350. else outlineFillId:= '';
  2351. if outlineFillId <> '' then
  2352. result.stroke := 'url(#' + outlineFillId + ')'
  2353. else result.strokeColor := OutlineFill.AverageColor;
  2354. result.strokeWidth := FloatWithCSSUnit(OutlineWidth, cuCustom);
  2355. result.strokeLineJoinLCL:= pjsRound;
  2356. result.paintOrder:= spoStrokeFillMarkers;
  2357. end else
  2358. result.strokeNone;
  2359. t := TSVGText(result);
  2360. t.fontStyleLCL:= FontStyle;
  2361. t.fontSize := FloatWithCSSUnit(FontEmHeight, cuPixel);
  2362. t.fontFamily:= FontName;
  2363. SetGlobalMatrix(AffineMatrixIdentity);
  2364. zoom := GetTextRenderZoom;
  2365. tl := GetTextLayout;
  2366. fm := tl.FontRenderer.GetFontPixelMetric;
  2367. for i := 0 to tl.PartCount-1 do
  2368. begin
  2369. rF := tl.PartRectF[i];
  2370. if rF.IsEmpty then continue;
  2371. rF.OffseT(topLeft.x, topLeft.y);
  2372. span := t.Content.AppendTextSpan(tl.GetTextPart(i, true));
  2373. if tl.PartRightToLeft[i] then
  2374. span.textDirection:= stdRtl
  2375. else span.textDirection:= stdLtr;
  2376. with rF do
  2377. begin
  2378. setlength({%H-}a, 1);
  2379. a[0] := FloatWithCSSUnit(Left/zoom, cuCustom);
  2380. if span.textDirection = stdRtl then a[0].value += Width/zoom;
  2381. span.x := a;
  2382. a[0] := FloatWithCSSUnit((Top + fm.Baseline)/zoom, cuCustom);
  2383. span.y := a;
  2384. span.textLength := FloatWithCSSUnit(Width/zoom, cuCustom);
  2385. end;
  2386. end;
  2387. end;
  2388. initialization
  2389. RegisterVectorShape(TTextShape);
  2390. end.