lcvectortextshapes.pas 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821
  1. unit LCVectorTextShapes;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. Classes, SysUtils, LCVectorRectShapes, BGRATextBidi, BGRABitmapTypes, LCVectorOriginal, BGRAGraphics,
  6. BGRABitmap, BGRALayerOriginal, BGRACanvas2D;
  7. const
  8. AlwaysVectorialText = true;
  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. FLightPosition: TPointF;
  75. FFontBidiMode: TFontBidiMode;
  76. FFontEmHeight: single;
  77. FFontName: string;
  78. FFontStyle: TFontStyles;
  79. FText: string;
  80. FSelStart,FSelEnd: integer;
  81. FVertAlign: TTextLayout;
  82. FEnteringUnicode: boolean;
  83. FUnicodeValue: cardinal;
  84. FUnicodeDigitCount: integer;
  85. FMouseSelecting: boolean;
  86. function GetBidiParagraphAlignment: TBidiTextAlignment;
  87. function GetCanPasteSelection: boolean;
  88. function GetHasSelection: boolean;
  89. function GetParagraphAlignment: TAlignment;
  90. procedure OnMoveLightPos({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF;
  91. {%H-}AShift: TShiftState);
  92. procedure SetAliased(AValue: boolean);
  93. procedure SetAltitudePercent(AValue: single);
  94. procedure SetPenPhong(AValue: boolean);
  95. procedure SetFontBidiMode(AValue: TFontBidiMode);
  96. procedure SetFontEmHeight(AValue: single);
  97. procedure SetFontName(AValue: string);
  98. procedure SetFontStyle(AValue: TFontStyles);
  99. procedure SetBidiParagraphAlignment(AValue: TBidiTextAlignment);
  100. procedure SetLightPosition(AValue: TPointF);
  101. procedure SetParagraphAlignment(AValue: TAlignment);
  102. procedure SetText(AValue: string);
  103. procedure SetVertAlign(AValue: TTextLayout);
  104. protected
  105. FTextLayout: TBidiTextLayout;
  106. FFontRenderer: TBGRACustomFontRenderer;
  107. FGlobalMatrix: TAffineMatrix;
  108. procedure DoOnChange(ABoundsBefore: TRectF; ADiff: TVectorShapeDiff); override;
  109. procedure SetGlobalMatrix(AMatrix: TAffineMatrix);
  110. function ShowArrows: boolean; override;
  111. function GetTextLayout: TBidiTextLayout;
  112. function GetFontRenderer: TBGRACustomFontRenderer;
  113. function UseVectorialTextRenderer: boolean;
  114. function UpdateFontRenderer: boolean;
  115. function GetTextRenderZoom: single;
  116. function GetUntransformedMatrix: TAffineMatrix; //matrix before render transform
  117. function IsTextMirrored(ABox: TAffineBox): boolean;
  118. procedure SetDefaultFont;
  119. function GetCornerPositition: single; override;
  120. procedure DeleteTextBefore(ACount: integer);
  121. procedure DeleteTextAfter(ACount: integer);
  122. procedure InsertText(ATextUTF8: string);
  123. procedure SelectWithMouse(X,Y: single; AExtend: boolean);
  124. function HasOutline: boolean;
  125. procedure InsertUnicodeValue;
  126. public
  127. constructor Create(AContainer: TVectorOriginal); override;
  128. procedure QuickDefine(constref APoint1,APoint2: TPointF); override;
  129. procedure LoadFromStorage(AStorage: TBGRACustomOriginalStorage); override;
  130. procedure SaveToStorage(AStorage: TBGRACustomOriginalStorage); override;
  131. destructor Destroy; override;
  132. class function Fields: TVectorShapeFields; override;
  133. class function PreferPixelCentered: boolean; override;
  134. class function DefaultFontName: string;
  135. class function DefaultFontEmHeight: single;
  136. class function DefaultAltitudePercent: single;
  137. class function CreateEmpty: boolean; override;
  138. class function StorageClassName: RawByteString; override;
  139. class function Usermodes: TVectorShapeUsermodes; override;
  140. procedure ConfigureCustomEditor(AEditor: TBGRAOriginalEditor); override;
  141. procedure Render(ADest: TBGRABitmap; ARenderOffset: TPoint; AMatrix: TAffineMatrix; ADraft: boolean); overload; override;
  142. function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
  143. function PointInShape(APoint: TPointF): boolean; overload; override;
  144. function PointInShape({%H-}APoint: TPointF; {%H-}ARadius: single): boolean; overload; override;
  145. function PointInPen(APoint: TPointF): boolean; overload; override;
  146. function GetIsSlow(const {%H-}AMatrix: TAffineMatrix): boolean; override;
  147. function GetGenericCost: integer; override;
  148. procedure MouseMove({%H-}Shift: TShiftState; {%H-}X, {%H-}Y: single; var {%H-}ACursor: TOriginalEditorCursor; var {%H-}AHandled: boolean); override;
  149. procedure MouseDown({%H-}RightButton: boolean; {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: single; var {%H-}ACursor: TOriginalEditorCursor; var {%H-}AHandled: boolean); override;
  150. procedure MouseUp({%H-}RightButton: boolean; {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: single; var {%H-}ACursor: TOriginalEditorCursor; var {%H-}AHandled: boolean); override;
  151. procedure KeyDown({%H-}Shift: TShiftState; {%H-}Key: TSpecialKey; var {%H-}AHandled: boolean); override;
  152. procedure KeyPress({%H-}UTF8Key: string; var {%H-}AHandled: boolean); override;
  153. procedure KeyUp({%H-}Shift: TShiftState; {%H-}Key: TSpecialKey; var {%H-}AHandled: boolean); override;
  154. procedure SetFontNameAndStyle(AFontName: string; AFontStyle: TFontStyles);
  155. function CopySelection: boolean;
  156. function CutSelection: boolean;
  157. function PasteSelection: boolean;
  158. function DeleteSelection: boolean;
  159. function GetAlignBounds(const {%H-}ALayoutRect: TRect; const AMatrix: TAffineMatrix): TRectF; override;
  160. procedure Transform(const AMatrix: TAffineMatrix); override;
  161. function AllowShearTransform: boolean; override;
  162. property HasSelection: boolean read GetHasSelection;
  163. property CanPasteSelection: boolean read GetCanPasteSelection;
  164. property Text: string read FText write SetText;
  165. property FontName: string read FFontName write SetFontName;
  166. property FontStyle: TFontStyles read FFontStyle write SetFontStyle;
  167. property FontEmHeight: single read FFontEmHeight write SetFontEmHeight;
  168. property FontBidiMode: TFontBidiMode read FFontBidiMode write SetFontBidiMode;
  169. property BidiParagraphAlignment: TBidiTextAlignment read GetBidiParagraphAlignment write SetBidiParagraphAlignment;
  170. property ParagraphAlignment: TAlignment read GetParagraphAlignment write SetParagraphAlignment;
  171. property VerticalAlignment: TTextLayout read FVertAlign write SetVertAlign;
  172. property PenPhong: boolean read FPenPhong write SetPenPhong;
  173. property LightPosition: TPointF read FLightPosition write SetLightPosition;
  174. property AltitudePercent: single read FAltitudePercent write SetAltitudePercent;
  175. property Aliased: boolean read FAliased write SetAliased;
  176. end;
  177. function FontStyleToStr(AStyle: TFontStyles): string;
  178. function StrToFontStyle(AText: string): TFontStyles;
  179. function FontBidiModeToStr(AMode: TFontBidiMode): string;
  180. function StrToFontBidiMode(AText: string): TFontBidiMode;
  181. implementation
  182. uses BGRATransform, BGRAText, BGRAVectorize, LCVectorialFill, math,
  183. BGRAUTF8, BGRAUnicode, Graphics, Clipbrd, LCLType, LCLIntf,
  184. BGRAGradients, BGRACustomTextFX, LCResourceString, BGRAFillInfo;
  185. function FontStyleToStr(AStyle: TFontStyles): string;
  186. begin
  187. result := '';
  188. if fsBold in AStyle then result += 'b';
  189. if fsItalic in AStyle then result += 'i';
  190. if fsStrikeOut in AStyle then result += 's';
  191. if fsUnderline in AStyle then result += 'u';
  192. end;
  193. function StrToFontStyle(AText: string): TFontStyles;
  194. var
  195. i: Integer;
  196. begin
  197. result := [];
  198. for i := 1 to length(AText) do
  199. case AText[i] of
  200. 'b': Include(result, fsBold);
  201. 'i': Include(result, fsItalic);
  202. 's': Include(result, fsStrikeOut);
  203. 'u': Include(result, fsUnderline);
  204. end;
  205. end;
  206. function FontBidiModeToStr(AMode: TFontBidiMode): string;
  207. begin
  208. case AMode of
  209. fbmLeftToRight: result := 'ltr';
  210. fbmRightToLeft: result := 'rtl';
  211. else {fbmAuto} result := 'auto';
  212. end;
  213. end;
  214. function StrToFontBidiMode(AText: string): TFontBidiMode;
  215. begin
  216. if CompareText(AText,'ltr')=0 then result := fbmLeftToRight else
  217. if CompareText(AText,'rtl')=0 then result := fbmRightToLeft
  218. else result := fbmAuto;
  219. end;
  220. { TTextShapeTextDiff }
  221. constructor TTextShapeTextDiff.Create(AStartShape: TVectorShape);
  222. var
  223. tl: TBidiTextLayout;
  224. i: Integer;
  225. begin
  226. with (AStartShape as TTextShape) do
  227. begin
  228. FTextBefore:= FText;
  229. FVertAlignBefore:= FVertAlign;
  230. tl := GetTextLayout;
  231. FSelStartBefore := FSelStart;
  232. FSelEndBefore:= FSelEnd;
  233. setlength(FParaAlignBefore, tl.ParagraphCount);
  234. for i := 0 to high(FParaAlignBefore) do
  235. FParaAlignBefore[i] := tl.ParagraphAlignment[i];
  236. end;
  237. end;
  238. procedure TTextShapeTextDiff.ComputeDiff(AEndShape: TVectorShape);
  239. var
  240. tl: TBidiTextLayout;
  241. i: Integer;
  242. begin
  243. with (AEndShape as TTextShape) do
  244. begin
  245. FTextAfter:= FText;
  246. FVertAlignAfter:= FVertAlign;
  247. FSelStartAfter := FSelStart;
  248. FSelEndAfter:= FSelEnd;
  249. tl := GetTextLayout;
  250. setlength(FParaAlignAfter, tl.ParagraphCount);
  251. for i := 0 to high(FParaAlignAfter) do
  252. FParaAlignAfter[i] := tl.ParagraphAlignment[i];
  253. end;
  254. end;
  255. procedure TTextShapeTextDiff.Apply(AStartShape: TVectorShape);
  256. var
  257. tl: TBidiTextLayout;
  258. i: Integer;
  259. begin
  260. with (AStartShape as TTextShape) do
  261. begin
  262. BeginUpdate;
  263. FreeAndNil(FTextLayout);
  264. FText := FTextAfter;
  265. FVertAlign := FVertAlignAfter;
  266. FSelStart := FSelStartAfter;
  267. FSelEnd := FSelEndAfter;
  268. tl := GetTextLayout;
  269. for i := 0 to min(length(FParaAlignAfter),tl.ParagraphCount)-1 do
  270. tl.ParagraphAlignment[i] := FParaAlignAfter[i];
  271. EndUpdate;
  272. end;
  273. end;
  274. procedure TTextShapeTextDiff.Unapply(AEndShape: TVectorShape);
  275. var
  276. tl: TBidiTextLayout;
  277. i: Integer;
  278. begin
  279. with (AEndShape as TTextShape) do
  280. begin
  281. BeginUpdate;
  282. FreeAndNil(FTextLayout);
  283. FText := FTextBefore;
  284. FVertAlign := FVertAlignBefore;
  285. FSelStart := FSelStartBefore;
  286. FSelEnd := FSelEndBefore;
  287. tl := GetTextLayout;
  288. for i := 0 to min(length(FParaAlignBefore),tl.ParagraphCount)-1 do
  289. tl.ParagraphAlignment[i] := FParaAlignBefore[i];
  290. EndUpdate;
  291. end;
  292. end;
  293. procedure TTextShapeTextDiff.Append(ADiff: TVectorShapeDiff);
  294. var
  295. next: TTextShapeTextDiff;
  296. i: Integer;
  297. begin
  298. next := ADiff as TTextShapeTextDiff;
  299. FTextAfter := next.FTextAfter;
  300. FVertAlignAfter := next.FVertAlignAfter;
  301. FSelStartAfter := next.FSelStartAfter;
  302. FSelEndAfter := next.FSelEndAfter;
  303. setlength(FParaAlignAfter, length(next.FParaAlignAfter));
  304. for i := 0 to high(FParaAlignAfter) do
  305. FParaAlignAfter[i] := next.FParaAlignAfter[i];
  306. end;
  307. function TTextShapeTextDiff.IsIdentity: boolean;
  308. var
  309. i: Integer;
  310. begin
  311. result := (FTextBefore = FTextAfter) and
  312. (FSelStartBefore = FSelStartAfter) and
  313. (FSelEndBefore = FSelEndAfter) and
  314. (FVertAlignBefore = FVertAlignAfter) and
  315. (length(FParaAlignBefore) = length(FParaAlignAfter));
  316. if result then
  317. begin
  318. for i := 0 to high(FParaAlignBefore) do
  319. if FParaAlignBefore[i] <> FParaAlignAfter[i] then
  320. begin
  321. result := false;
  322. break;
  323. end;
  324. end;
  325. end;
  326. { TTextShapePhongDiff }
  327. constructor TTextShapePhongDiff.Create(AStartShape: TVectorShape);
  328. begin
  329. with (AStartShape as TTextShape) do
  330. begin
  331. FAltitudePercentBefore := FAltitudePercent;
  332. FPenPhongBefore := FPenPhong;
  333. FLightPositionBefore := FLightPosition;
  334. end;
  335. end;
  336. procedure TTextShapePhongDiff.ComputeDiff(AEndShape: TVectorShape);
  337. begin
  338. with (AEndShape as TTextShape) do
  339. begin
  340. FAltitudePercentAfter := FAltitudePercent;
  341. FPenPhongAfter := FPenPhong;
  342. FLightPositionAfter := FLightPosition;
  343. end;
  344. end;
  345. procedure TTextShapePhongDiff.Apply(AStartShape: TVectorShape);
  346. begin
  347. with (AStartShape as TTextShape) do
  348. begin
  349. BeginUpdate;
  350. FAltitudePercent := FAltitudePercentAfter;
  351. FPenPhong := FPenPhongAfter;
  352. FLightPosition := FLightPositionAfter;
  353. EndUpdate;
  354. end;
  355. end;
  356. procedure TTextShapePhongDiff.Unapply(AEndShape: TVectorShape);
  357. begin
  358. with (AEndShape as TTextShape) do
  359. begin
  360. BeginUpdate;
  361. FAltitudePercent := FAltitudePercentBefore;
  362. FPenPhong := FPenPhongBefore;
  363. FLightPosition := FLightPositionBefore;
  364. EndUpdate;
  365. end;
  366. end;
  367. procedure TTextShapePhongDiff.Append(ADiff: TVectorShapeDiff);
  368. var
  369. next: TTextShapePhongDiff;
  370. begin
  371. next := ADiff as TTextShapePhongDiff;
  372. FAltitudePercentAfter:= next.FAltitudePercentAfter;
  373. FPenPhongAfter:= next.FPenPhongAfter;
  374. FLightPositionAfter:= next.FLightPositionAfter;
  375. end;
  376. function TTextShapePhongDiff.IsIdentity: boolean;
  377. begin
  378. result := (FAltitudePercentBefore = FAltitudePercentAfter) and
  379. (FPenPhongBefore = FPenPhongAfter) and
  380. (FLightPositionBefore = FLightPositionAfter);
  381. end;
  382. { TTextShapeFontDiff }
  383. constructor TTextShapeFontDiff.Create(AStartShape: TVectorShape);
  384. begin
  385. with (AStartShape as TTextShape) do
  386. begin
  387. FFontBidiModeBefore:= FFontBidiMode;
  388. FFontEmHeightBefore:= FFontEmHeight;
  389. FFontNameBefore:= FFontName;
  390. FFontStyleBefore:= FFontStyle;
  391. FAliasedBefore := FAliased;
  392. end;
  393. end;
  394. procedure TTextShapeFontDiff.ComputeDiff(AEndShape: TVectorShape);
  395. begin
  396. with (AEndShape as TTextShape) do
  397. begin
  398. FFontBidiModeAfter:= FFontBidiMode;
  399. FFontEmHeightAfter:= FFontEmHeight;
  400. FFontNameAfter:= FFontName;
  401. FFontStyleAfter:= FFontStyle;
  402. FAliasedAfter := FAliased;
  403. end;
  404. end;
  405. procedure TTextShapeFontDiff.Apply(AStartShape: TVectorShape);
  406. begin
  407. with (AStartShape as TTextShape) do
  408. begin
  409. BeginUpdate;
  410. FFontBidiMode := FFontBidiModeAfter;
  411. FFontEmHeight := FFontEmHeightAfter;
  412. FFontName := FFontNameAfter;
  413. FFontStyle := FFontStyleAfter;
  414. FAliased := FAliasedAfter;
  415. if Assigned(FTextLayout) then FTextLayout.InvalidateLayout;
  416. EndUpdate;
  417. end;
  418. end;
  419. procedure TTextShapeFontDiff.Unapply(AEndShape: TVectorShape);
  420. begin
  421. with (AEndShape as TTextShape) do
  422. begin
  423. BeginUpdate;
  424. FFontBidiMode := FFontBidiModeBefore;
  425. FFontEmHeight := FFontEmHeightBefore;
  426. FFontName := FFontNameBefore;
  427. FFontStyle := FFontStyleBefore;
  428. FAliased := FAliasedBefore;
  429. if Assigned(FTextLayout) then FTextLayout.InvalidateLayout;
  430. EndUpdate;
  431. end;
  432. end;
  433. procedure TTextShapeFontDiff.Append(ADiff: TVectorShapeDiff);
  434. var
  435. next: TTextShapeFontDiff;
  436. begin
  437. next := ADiff as TTextShapeFontDiff;
  438. FFontBidiModeAfter := next.FFontBidiModeAfter;
  439. FFontEmHeightAfter := next.FFontEmHeightAfter;
  440. FFontNameAfter := next.FFontNameAfter;
  441. FFontStyleAfter := next.FFontStyleAfter;
  442. FAliasedAfter := next.FAliasedAfter;
  443. end;
  444. function TTextShapeFontDiff.IsIdentity: boolean;
  445. begin
  446. result := (FFontBidiModeBefore = FFontBidiModeAfter) and
  447. (FFontEmHeightBefore = FFontEmHeightAfter) and
  448. (FFontNameBefore = FFontNameAfter) and
  449. (FFontStyleBefore = FFontStyleAfter) and
  450. (FAliasedBefore = FAliasedAfter);
  451. end;
  452. { TTextShape }
  453. procedure TTextShape.SetText(AValue: string);
  454. begin
  455. if FText=AValue then Exit;
  456. BeginUpdate(TTextShapeTextDiff);
  457. FText:=AValue;
  458. FSelStart:=0;
  459. FSelEnd :=0;
  460. FreeAndNil(FTextLayout);
  461. EndUpdate;
  462. end;
  463. procedure TTextShape.SetFontBidiMode(AValue: TFontBidiMode);
  464. begin
  465. if FFontBidiMode=AValue then Exit;
  466. BeginUpdate(TTextShapeFontDiff);
  467. FFontBidiMode:=AValue;
  468. EndUpdate;
  469. end;
  470. function TTextShape.GetBidiParagraphAlignment: TBidiTextAlignment;
  471. var
  472. tl: TBidiTextLayout;
  473. paraIndex: Integer;
  474. begin
  475. tl := GetTextLayout;
  476. paraIndex := tl.GetParagraphAt(FSelEnd);
  477. result := tl.ParagraphAlignment[paraIndex];
  478. end;
  479. function TTextShape.GetCanPasteSelection: boolean;
  480. begin
  481. result := Clipboard.HasFormat(PredefinedClipboardFormat(pcfText));
  482. end;
  483. function TTextShape.GetHasSelection: boolean;
  484. begin
  485. result := FSelEnd <> FSelStart;
  486. end;
  487. function TTextShape.GetParagraphAlignment: TAlignment;
  488. var
  489. tl: TBidiTextLayout;
  490. paraIndex: Integer;
  491. rtl: Boolean;
  492. begin
  493. tl := GetTextLayout;
  494. paraIndex := tl.GetParagraphAt(FSelEnd);
  495. rtl := tl.ParagraphRightToLeft[paraIndex];
  496. case tl.ParagraphAlignment[paraIndex] of
  497. btaCenter: result := taCenter;
  498. btaRightJustify: result := taRightJustify;
  499. btaNatural: if rtl then result := taRightJustify else result := taLeftJustify;
  500. btaOpposite: if rtl then result := taLeftJustify else result := taRightJustify;
  501. else {btaLeftJustify}
  502. result := taLeftJustify;
  503. end;
  504. end;
  505. procedure TTextShape.OnMoveLightPos(ASender: TObject; APrevCoord,
  506. ANewCoord: TPointF; AShift: TShiftState);
  507. begin
  508. LightPosition := ANewCoord;
  509. end;
  510. procedure TTextShape.SetAliased(AValue: boolean);
  511. begin
  512. if FAliased=AValue then Exit;
  513. BeginUpdate(TTextShapeFontDiff);
  514. FAliased:=AValue;
  515. EndUpdate;
  516. end;
  517. procedure TTextShape.SetAltitudePercent(AValue: single);
  518. begin
  519. if AValue < 0 then AValue := 0;
  520. if AValue > 100 then AValue := 100;
  521. if FAltitudePercent=AValue then Exit;
  522. BeginUpdate(TTextShapePhongDiff);
  523. FAltitudePercent:=AValue;
  524. EndUpdate;
  525. end;
  526. procedure TTextShape.SetPenPhong(AValue: boolean);
  527. begin
  528. if FPenPhong=AValue then Exit;
  529. BeginUpdate(TTextShapePhongDiff);
  530. FPenPhong:=AValue;
  531. EndUpdate;
  532. end;
  533. procedure TTextShape.SetFontEmHeight(AValue: single);
  534. begin
  535. if FFontEmHeight=AValue then Exit;
  536. BeginUpdate(TTextShapeFontDiff);
  537. FFontEmHeight:=AValue;
  538. if Assigned(FTextLayout) then FTextLayout.InvalidateLayout;
  539. EndUpdate;
  540. end;
  541. procedure TTextShape.SetFontName(AValue: string);
  542. begin
  543. if FFontName=AValue then Exit;
  544. BeginUpdate(TTextShapeFontDiff);
  545. FFontName:=AValue;
  546. if Assigned(FTextLayout) then FTextLayout.InvalidateLayout;
  547. EndUpdate;
  548. end;
  549. procedure TTextShape.SetFontStyle(AValue: TFontStyles);
  550. begin
  551. if FFontStyle=AValue then Exit;
  552. BeginUpdate(TTextShapeFontDiff);
  553. FFontStyle:=AValue;
  554. if Assigned(FTextLayout) then FTextLayout.InvalidateLayout;
  555. EndUpdate;
  556. end;
  557. procedure TTextShape.SetBidiParagraphAlignment(AValue: TBidiTextAlignment);
  558. var
  559. tl: TBidiTextLayout;
  560. paraIndex, paraIndex2, i: Integer;
  561. needUpdate: boolean;
  562. begin
  563. tl := GetTextLayout;
  564. if Usermode <> vsuEditText then
  565. begin
  566. if tl.ParagraphCount = 0 then exit;
  567. paraIndex := 0;
  568. paraIndex2:= tl.ParagraphCount-1;
  569. end else
  570. begin
  571. paraIndex := tl.GetParagraphAt(FSelStart);
  572. paraIndex2 := tl.GetParagraphAt(FSelEnd);
  573. end;
  574. needUpdate := false;
  575. for i := min(paraIndex,paraIndex2) to max(paraIndex,paraIndex2) do
  576. if tl.ParagraphAlignment[i] <> AValue then
  577. begin
  578. if not needUpdate then
  579. begin
  580. BeginUpdate(TTextShapeTextDiff);
  581. needUpdate := true;
  582. end;
  583. tl.ParagraphAlignment[i] := AValue;
  584. end;
  585. if needUpdate then EndUpdate;
  586. end;
  587. procedure TTextShape.SetLightPosition(AValue: TPointF);
  588. begin
  589. if FLightPosition=AValue then Exit;
  590. BeginUpdate(TTextShapePhongDiff);
  591. FLightPosition:=AValue;
  592. EndUpdate;
  593. end;
  594. procedure TTextShape.SetParagraphAlignment(AValue: TAlignment);
  595. var
  596. tl: TBidiTextLayout;
  597. paraIndex, paraIndex2, i: Integer;
  598. bidiAlign: TBidiTextAlignment;
  599. rtl, needUpdate: Boolean;
  600. begin
  601. tl := GetTextLayout;
  602. if UserMode <> vsuEditText then
  603. begin
  604. if tl.ParagraphCount = 0 then exit;
  605. paraIndex := 0;
  606. paraIndex2:= tl.ParagraphCount-1;
  607. end else
  608. begin
  609. paraIndex := tl.GetParagraphAt(FSelStart);
  610. paraIndex2 := tl.GetParagraphAt(FSelEnd);
  611. end;
  612. needUpdate := false;
  613. for i := min(paraIndex,paraIndex2) to max(paraIndex,paraIndex2) do
  614. begin
  615. rtl := tl.ParagraphRightToLeft[i];
  616. case AValue of
  617. taCenter: bidiAlign:= btaCenter;
  618. taRightJustify: if rtl then bidiAlign := btaNatural else bidiAlign := btaOpposite;
  619. else {taLeftJustify}
  620. if rtl then bidiAlign := btaOpposite else bidiAlign := btaNatural;
  621. end;
  622. if tl.ParagraphAlignment[i] <> bidiAlign then
  623. begin
  624. if not needUpdate then
  625. begin
  626. BeginUpdate(TTextShapeTextDiff);
  627. needUpdate := true;
  628. end;
  629. tl.ParagraphAlignment[i] := bidiAlign;
  630. end;
  631. end;
  632. if needUpdate then EndUpdate;
  633. end;
  634. procedure TTextShape.SetVertAlign(AValue: TTextLayout);
  635. begin
  636. if FVertAlign=AValue then Exit;
  637. BeginUpdate(TTextShapeTextDiff);
  638. FVertAlign:=AValue;
  639. EndUpdate;
  640. end;
  641. procedure TTextShape.DoOnChange(ABoundsBefore: TRectF; ADiff: TVectorShapeDiff);
  642. var freeRenderer: boolean;
  643. begin
  644. if Assigned(FFontRenderer) then
  645. begin
  646. freeRenderer := false;
  647. if UseVectorialTextRenderer then
  648. begin
  649. if not (FFontRenderer is TBGRAVectorizedFontRenderer) then
  650. freeRenderer:= true;
  651. end else
  652. begin
  653. if not (FFontRenderer is TLCLFontRenderer) then
  654. freeRenderer:= true;
  655. end;
  656. if freeRenderer then
  657. begin
  658. FreeAndNil(FFontRenderer);
  659. if Assigned(FTextLayout) then
  660. FTextLayout.FontRenderer := GetFontRenderer;
  661. end;
  662. end;
  663. inherited DoOnChange(ABoundsBefore, ADiff);
  664. end;
  665. procedure TTextShape.SetGlobalMatrix(AMatrix: TAffineMatrix);
  666. begin
  667. if AMatrix = FGlobalMatrix then exit;
  668. FGlobalMatrix := AMatrix;
  669. end;
  670. function TTextShape.AllowShearTransform: boolean;
  671. begin
  672. Result:= true;
  673. end;
  674. function TTextShape.ShowArrows: boolean;
  675. begin
  676. Result:= false;
  677. end;
  678. function TTextShape.GetTextLayout: TBidiTextLayout;
  679. var
  680. box: TAffineBox;
  681. begin
  682. if FTextLayout = nil then
  683. FTextLayout := TBidiTextLayout.Create(GetFontRenderer, FText)
  684. else
  685. if UpdateFontRenderer then FTextLayout.InvalidateLayout;
  686. box := GetAffineBox(FGlobalMatrix,false);
  687. FTextLayout.FontBidiMode:= FontBidiMode;
  688. FTextLayout.TopLeft := PointF(0,0);
  689. FTextLayout.AvailableWidth:= box.Width;
  690. FTextLayout.AvailableHeight:= box.Height;
  691. FTextLayout.ParagraphSpacingBelow:= 0.5;
  692. result:= FTextLayout;
  693. end;
  694. function TTextShape.GetFontRenderer: TBGRACustomFontRenderer;
  695. begin
  696. UpdateFontRenderer;
  697. result := FFontRenderer;
  698. end;
  699. function TTextShape.UseVectorialTextRenderer: boolean;
  700. begin
  701. result := AlwaysVectorialText or HasOutline;
  702. end;
  703. function TTextShape.UpdateFontRenderer: boolean;
  704. var
  705. newEmHeight: single;
  706. begin
  707. if FFontRenderer = nil then
  708. begin
  709. if UseVectorialTextRenderer then
  710. begin
  711. FFontRenderer := TBGRAVectorizedFontRenderer.Create;
  712. TBGRAVectorizedFontRenderer(FFontRenderer).QuadraticCurves := true;
  713. TBGRAVectorizedFontRenderer(FFontRenderer).MinFontResolution := 300;
  714. TBGRAVectorizedFontRenderer(FFontRenderer).MaxFontResolution := 300;
  715. end
  716. else
  717. begin
  718. FFontRenderer := TLCLFontRenderer.Create;
  719. TLCLFontRenderer(FFontRenderer).OverrideUnderlineDecoration:= true;
  720. end;
  721. end;
  722. newEmHeight := FontEmHeight*GetTextRenderZoom;
  723. if (newEmHeight <> FFontRenderer.FontEmHeight) or
  724. (FFontRenderer.FontName <> FontName) or
  725. (FFontRenderer.FontStyle <> FontStyle) or
  726. (FFontRenderer.FontQuality <> fqFineAntialiasing) then
  727. begin
  728. FFontRenderer.FontEmHeightF := newEmHeight;
  729. FFontRenderer.FontName:= FontName;
  730. FFontRenderer.FontStyle:= FontStyle;
  731. FFontRenderer.FontQuality:= fqFineAntialiasing;
  732. exit(true);
  733. end
  734. else exit(false);
  735. end;
  736. function TTextShape.GetTextRenderZoom: single;
  737. begin
  738. //font to be rendered at a sufficient size to avoid stretching
  739. result := max(VectLen(FGlobalMatrix[1,1],FGlobalMatrix[2,1]),
  740. VectLen(FGlobalMatrix[1,2],FGlobalMatrix[2,2]));
  741. end;
  742. function TTextShape.GetUntransformedMatrix: TAffineMatrix;
  743. var
  744. ab: TAffineBox;
  745. u, v: TPointF;
  746. lenU, lenV: Single;
  747. begin
  748. ab := GetAffineBox(AffineMatrixIdentity, false);
  749. u := ab.TopRight-ab.TopLeft;
  750. lenU := VectLen(u);
  751. if lenU<>0 then u *= (1/lenU);
  752. v := ab.BottomLeft-ab.TopLeft;
  753. lenV := VectLen(v);
  754. if lenV<>0 then v *= (1/lenV);
  755. result := AffineMatrix(u,v,ab.TopLeft);
  756. end;
  757. function TTextShape.IsTextMirrored(ABox: TAffineBox): boolean;
  758. var
  759. u,v: TPointF;
  760. begin
  761. u := ABox.TopRight-ABox.TopLeft;
  762. v := ABox.BottomLeft-ABox.TopLeft;
  763. result := u.x*v.y - u.y*v.x < 0;
  764. end;
  765. procedure TTextShape.SetDefaultFont;
  766. begin
  767. FontName := DefaultFontName;
  768. FontEmHeight := DefaultFontEmHeight;
  769. FontBidiMode:= fbmAuto;
  770. FontStyle := [];
  771. end;
  772. function TTextShape.GetCornerPositition: single;
  773. begin
  774. result := 1;
  775. end;
  776. procedure TTextShape.DeleteTextBefore(ACount: integer);
  777. var
  778. delCount, selLeft: Integer;
  779. begin
  780. if UserMode <> vsuEditText then exit;
  781. BeginUpdate(TTextShapeTextDiff);
  782. selLeft := Min(FSelStart,FSelEnd);
  783. if selLeft > 0 then
  784. begin
  785. delCount := GetTextLayout.DeleteTextBefore(selLeft, ACount);
  786. FText := GetTextLayout.TextUTF8;
  787. dec(selLeft,delCount);
  788. end;
  789. FSelStart := selLeft;
  790. FSelEnd := selLeft;
  791. EndUpdate;
  792. end;
  793. procedure TTextShape.DeleteTextAfter(ACount: integer);
  794. var
  795. selRight: Integer;
  796. tl: TBidiTextLayout;
  797. begin
  798. if UserMode <> vsuEditText then exit;
  799. BeginUpdate(TTextShapeTextDiff);
  800. selRight := Max(FSelStart,FSelEnd);
  801. tl := GetTextLayout;
  802. if selRight+ACount <= tl.CharCount then
  803. begin
  804. tl.DeleteText(selRight, ACount);
  805. FText := tl.TextUTF8;
  806. end;
  807. FSelStart := selRight;
  808. FSelEnd := selRight;
  809. EndUpdate;
  810. end;
  811. function TTextShape.DeleteSelection: boolean;
  812. var
  813. selLeft: Integer;
  814. begin
  815. if FSelStart <> FSelEnd then
  816. begin
  817. BeginUpdate(TTextShapeTextDiff);
  818. selLeft := Min(FSelStart,FSelEnd);
  819. GetTextLayout.DeleteText(selLeft, Abs(FSelEnd-FSelStart));
  820. FText := GetTextLayout.TextUTF8;
  821. FSelStart := selLeft;
  822. FSelEnd := selLeft;
  823. EndUpdate;
  824. result := true;
  825. end else
  826. result := false;
  827. end;
  828. function TTextShape.GetAlignBounds(const ALayoutRect: TRect;
  829. const AMatrix: TAffineMatrix): TRectF;
  830. var
  831. ab: TAffineBox;
  832. begin
  833. ab := GetAffineBox(AMatrix, false);
  834. Result:= ab.RectBoundsF;
  835. end;
  836. procedure TTextShape.InsertText(ATextUTF8: string);
  837. var
  838. insertCount: Integer;
  839. begin
  840. if UserMode <> vsuEditText then exit;
  841. BeginUpdate(TTextShapeTextDiff);
  842. DeleteSelection;
  843. insertCount := GetTextLayout.InsertText(ATextUTF8, FSelStart);
  844. FText := GetTextLayout.TextUTF8;
  845. Inc(FSelStart, insertCount);
  846. FSelEnd := FSelStart;
  847. EndUpdate;
  848. end;
  849. procedure TTextShape.SelectWithMouse(X, Y: single; AExtend: boolean);
  850. var
  851. newPos: Integer;
  852. tl: TBidiTextLayout;
  853. zoom: Single;
  854. begin
  855. tl := GetTextLayout;
  856. zoom := GetTextRenderZoom;
  857. newPos := tl.GetCharIndexAt(AffineMatrixScale(zoom,zoom)*AffineMatrixInverse(GetUntransformedMatrix)*PointF(X,Y));
  858. if newPos<>-1 then
  859. begin
  860. if (newPos <> FSelEnd) or (not AExtend and (FSelStart <> FSelEnd)) or (UserMode <> vsuEditText) then
  861. begin
  862. BeginEditingUpdate;
  863. FSelEnd:= newPos;
  864. if not AExtend or (UserMode <> vsuEditText) then FSelStart:= FSelEnd;
  865. UserMode := vsuEditText;
  866. EndEditingUpdate;
  867. end;
  868. end;
  869. end;
  870. function TTextShape.HasOutline: boolean;
  871. begin
  872. result := not OutlineFill.IsFullyTransparent and (OutlineWidth > 0);
  873. end;
  874. procedure TTextShape.InsertUnicodeValue;
  875. begin
  876. if FEnteringUnicode then
  877. begin
  878. InsertText(UnicodeCharToUTF8(FUnicodeValue));
  879. FEnteringUnicode:= false;
  880. end;
  881. end;
  882. constructor TTextShape.Create(AContainer: TVectorOriginal);
  883. begin
  884. inherited Create(AContainer);
  885. SetDefaultFont;
  886. FVertAlign:= tlTop;
  887. FText := '';
  888. FSelStart := 0;
  889. FSelEnd := 0;
  890. FGlobalMatrix := AffineMatrixIdentity;
  891. FPenPhong:= false;
  892. FAltitudePercent:= DefaultAltitudePercent;
  893. FLightPosition := PointF(0,0);
  894. FAliased := false;
  895. end;
  896. procedure TTextShape.QuickDefine(constref APoint1, APoint2: TPointF);
  897. var minSize: single;
  898. p2: TPointF;
  899. begin
  900. minSize := GetFontRenderer.TextSize('Hg').cy/GetTextRenderZoom;
  901. p2 := APoint2;
  902. if abs(APoint1.x-p2.x) < minSize then
  903. begin
  904. if p2.x < APoint1.x then p2.x := APoint1.x - minSize else
  905. p2.x := APoint1.x + minSize;
  906. end;
  907. if abs(APoint1.y-p2.y) < minSize then
  908. begin
  909. if p2.y < APoint1.y then p2.y := APoint1.y - minSize else
  910. p2.y := APoint1.y + minSize;
  911. end;
  912. inherited QuickDefine(APoint1, p2);
  913. end;
  914. procedure TTextShape.LoadFromStorage(AStorage: TBGRACustomOriginalStorage);
  915. var
  916. font, phongObj: TBGRACustomOriginalStorage;
  917. tl: TBidiTextLayout;
  918. paraAlignList: TStringList;
  919. i: Integer;
  920. alignment: TAlignment;
  921. begin
  922. BeginUpdate;
  923. inherited LoadFromStorage(AStorage);
  924. Text := AStorage.RawString['text'];
  925. font := AStorage.OpenObject('font');
  926. if Assigned(font) then
  927. begin
  928. if font.HasAttribute('name') then
  929. FontName:= font.RawString['name']
  930. else
  931. FontName:= AStorage.RawString['name']; //compatibility
  932. if fontName = '' then fontName := DefaultFontName;
  933. if font.HasAttribute('em-height') then
  934. FontEmHeight:= font.FloatDef['em-height', DefaultFontEmHeight]
  935. else
  936. FontEmHeight:= AStorage.FloatDef['em-height', DefaultFontEmHeight]; //compatibility
  937. if Font.HasAttribute('bidi') then
  938. FontBidiMode:= StrToFontBidiMode(font.RawString['bidi'])
  939. else
  940. FontBidiMode:= StrToFontBidiMode(AStorage.RawString['bidi']); //compatibility
  941. if font.HasAttribute('style') then
  942. FontStyle:= StrToFontStyle(font.RawString['style'])
  943. else
  944. FontStyle:= StrToFontStyle(AStorage.RawString['style']); //compatibility
  945. font.Free;
  946. end else
  947. SetDefaultFont;
  948. Aliased := AStorage.Bool['aliased'];
  949. phongObj := AStorage.OpenObject('pen-phong');
  950. PenPhong := Assigned(phongObj);
  951. if PenPhong then
  952. begin
  953. LightPosition := phongObj.PointF['light-pos'];
  954. AltitudePercent:= phongObj.FloatDef['altitude-percent', DefaultAltitudePercent];
  955. phongObj.Free;
  956. end else
  957. begin
  958. LightPosition := PointF(0,0);
  959. AltitudePercent:= DefaultAltitudePercent;
  960. end;
  961. tl := GetTextLayout;
  962. paraAlignList := TStringList.Create;
  963. paraAlignList.DelimitedText:= AStorage.RawString['paragraph-align'];
  964. for i := 0 to min(paraAlignList.Count, tl.ParagraphCount)-1 do
  965. begin
  966. case paraAlignList[i] of
  967. 'center': alignment := taCenter;
  968. 'right': alignment := taRightJustify;
  969. else {'left'} alignment := taLeftJustify;
  970. end;
  971. tl.ParagraphAlignment[i] := AlignmentToBidiTextAlignment(alignment, tl.ParagraphRightToLeft[i]);
  972. end;
  973. paraAlignList.Free;
  974. EndUpdate;
  975. end;
  976. procedure TTextShape.SaveToStorage(AStorage: TBGRACustomOriginalStorage);
  977. var
  978. font, phongObj: TBGRACustomOriginalStorage;
  979. tl: TBidiTextLayout;
  980. paraAlignList: TStringList;
  981. i: Integer;
  982. begin
  983. inherited SaveToStorage(AStorage);
  984. AStorage.RawString['text'] := Text;
  985. font := AStorage.OpenObject('font');
  986. if font = nil then font := AStorage.CreateObject('font');
  987. font.RawString['name'] := FontName;
  988. font.Float['em-height'] := FontEmHeight;
  989. font.RawString['bidi'] := FontBidiModeToStr(FontBidiMode);
  990. font.RawString['style'] := FontStyleToStr(FontStyle);
  991. font.Free;
  992. AStorage.Bool['aliased'] := Aliased;
  993. if PenPhong then
  994. begin
  995. phongObj := AStorage.OpenObject('pen-phong');
  996. if phongObj=nil then phongObj := AStorage.CreateObject('pen-phong');
  997. phongObj.PointF['light-pos'] := LightPosition;
  998. phongObj.Float['altitude-percent'] := AltitudePercent;
  999. phongObj.Free;
  1000. end else
  1001. AStorage.RemoveObject('pen-phong');
  1002. tl := GetTextLayout;
  1003. paraAlignList := TStringList.Create;
  1004. for i := 0 to tl.ParagraphCount-1 do
  1005. case tl.ParagraphAlignment[i] of
  1006. btaRightJustify: paraAlignList.Add('right');
  1007. btaCenter: paraAlignList.Add('center');
  1008. btaNatural: if tl.ParagraphRightToLeft[i] then paraAlignList.Add('right') else paraAlignList.Add('left');
  1009. btaOpposite: if tl.ParagraphRightToLeft[i] then paraAlignList.Add('left') else paraAlignList.Add('right');
  1010. else {btaLeftJustify}
  1011. paraAlignList.Add('left');
  1012. end;
  1013. AStorage.RawString['paragraph-align'] := paraAlignList.DelimitedText;
  1014. paraAlignList.Free;
  1015. end;
  1016. destructor TTextShape.Destroy;
  1017. begin
  1018. FreeAndNil(FTextLayout);
  1019. FreeAndNil(FFontRenderer);
  1020. inherited Destroy;
  1021. end;
  1022. class function TTextShape.Fields: TVectorShapeFields;
  1023. begin
  1024. Result:= [vsfPenFill,vsfOutlineFill,vsfOutlineWidth];
  1025. end;
  1026. class function TTextShape.PreferPixelCentered: boolean;
  1027. begin
  1028. Result:= false;
  1029. end;
  1030. class function TTextShape.DefaultFontName: string;
  1031. begin
  1032. result := {$IFDEF WINDOWS}'Arial'{$ELSE}{$IFDEF DARWIN}'Helvetica'{$ELSE}'Liberation Sans'{$ENDIF}{$ENDIF};
  1033. end;
  1034. class function TTextShape.DefaultFontEmHeight: single;
  1035. begin
  1036. result := 20;
  1037. end;
  1038. class function TTextShape.DefaultAltitudePercent: single;
  1039. begin
  1040. result := 30;
  1041. end;
  1042. class function TTextShape.CreateEmpty: boolean;
  1043. begin
  1044. Result:= true;
  1045. end;
  1046. procedure TTextShape.ConfigureCustomEditor(AEditor: TBGRAOriginalEditor);
  1047. var
  1048. caret: TBidiCaretPos;
  1049. orientation: TPointF;
  1050. m: TAffineMatrix;
  1051. tl: TBidiTextLayout;
  1052. pts: Array Of TPointF;
  1053. i, idxLight: Integer;
  1054. c: TBGRAPixel;
  1055. zoom: Single;
  1056. begin
  1057. inherited ConfigureCustomEditor(AEditor);
  1058. AEditor.AddPolyline(GetAffineBox(AffineMatrixIdentity,true).AsPolygon, true, opsDashWithShadow);
  1059. if AEditor.Focused and (Usermode = vsuEditText) then
  1060. begin
  1061. tl := GetTextLayout;
  1062. caret:= tl.GetCaret(FSelEnd);
  1063. zoom := GetTextRenderZoom;
  1064. m := AffineMatrixTranslation(-0.5,-0.5)*GetUntransformedMatrix*AffineMatrixScale(1/zoom,1/zoom);
  1065. if FSelStart<>FSelEnd then
  1066. begin
  1067. pts := tl.GetTextEnveloppe(FSelStart, FSelEnd, false, true, true);
  1068. for i := 0 to high(pts) do
  1069. pts[i] := m*pts[i];
  1070. c:= clHighlight;
  1071. c.alpha := 96;
  1072. AEditor.AddPolyline(pts, true, opsDash, c);
  1073. end;
  1074. if (tl.AvailableHeight = EmptySingle) or (caret.Top.y < tl.AvailableHeight) then
  1075. begin
  1076. orientation := (caret.Bottom-caret.Top)*(1/10);
  1077. orientation := PointF(-orientation.y,orientation.x);
  1078. if (tl.AvailableHeight <> EmptySingle) and (caret.Bottom.y <> EmptySingle) and (caret.Bottom.y > tl.AvailableHeight) then caret.Bottom.y := tl.AvailableHeight;
  1079. if (tl.AvailableHeight <> EmptySingle) and (caret.PreviousBottom.y <> EmptySingle) and (caret.PreviousBottom.y > tl.AvailableHeight) then caret.PreviousBottom.y := tl.AvailableHeight;
  1080. if not isEmptyPointF(caret.PreviousTop) and (caret.PreviousRightToLeft<>caret.RightToLeft) then
  1081. begin
  1082. if caret.RightToLeft then orientation := -orientation;
  1083. AEditor.AddPolyline([m*caret.Bottom,m*caret.Top,m*(caret.Top+orientation)],false, opsSolid);
  1084. end else
  1085. AEditor.AddPolyline([m*caret.Bottom,m*caret.Top],false, opsSolid);
  1086. end;
  1087. end;
  1088. if PenPhong then
  1089. begin
  1090. idxLight := AEditor.AddPoint(FLightPosition, @OnMoveLightPos, true);
  1091. if AEditor is TVectorOriginalEditor then
  1092. TVectorOriginalEditor(AEditor).AddLabel(idxLight, rsLightPosition, taCenter, tlTop);
  1093. end;
  1094. end;
  1095. procedure TTextShape.Render(ADest: TBGRABitmap; ARenderOffset: TPoint; AMatrix: TAffineMatrix;
  1096. ADraft: boolean);
  1097. function GetTextPhongHeight: integer;
  1098. begin
  1099. result := round(AltitudePercent/100 * FontEmHeight*0.15);
  1100. end;
  1101. function CreateShader(AOfsX,AOfsY: integer): TPhongShading;
  1102. var
  1103. lightPosF: TPointF;
  1104. lightPosZ: Single;
  1105. begin
  1106. result := TPhongShading.Create;
  1107. result.AmbientFactor := 0.6;
  1108. result.NegativeDiffusionFactor := 0.15;
  1109. lightPosF := FGlobalMatrix*LightPosition+PointF(AOfsX,AOfsY);
  1110. lightPosZ := max(AltitudePercent, 1.2*GetTextPhongHeight);
  1111. result.LightPosition3D := Point3D(lightPosF.x,lightPosF.y,lightPosZ);
  1112. end;
  1113. var
  1114. zoom: Single;
  1115. m: TAffineMatrix;
  1116. tl: TBidiTextLayout;
  1117. fr: TBGRACustomFontRenderer;
  1118. pad: Integer;
  1119. sourceRectF,transfRectF,sourceInvRect,destF: TRectF;
  1120. transfRect: TRect;
  1121. tmpSource, tmpTransf, tmpTransfMask: TBGRABitmap;
  1122. scan: TBGRACustomScanner;
  1123. ctx: TBGRACanvas2D;
  1124. rf: TResampleFilter;
  1125. storeImage: Boolean;
  1126. shader: TPhongShading;
  1127. textFx: TBGRACustomTextEffect;
  1128. begin
  1129. RetrieveRenderStorage(AMatrix, transfRect, tmpTransf);
  1130. if Assigned(tmpTransf) then
  1131. begin
  1132. ADest.PutImage(transfRect.Left+ARenderOffset.X,transfRect.Top+ARenderOffset.Y, tmpTransf,dmDrawWithTransparency);
  1133. tmpTransf.Free;
  1134. exit;
  1135. end;
  1136. if PenFill.IsFullyTransparent and not HasOutline then exit;
  1137. SetGlobalMatrix(AffineMatrixTranslation(ARenderOffset.X,ARenderOffset.Y)*AMatrix);
  1138. zoom := GetTextRenderZoom;
  1139. if zoom = 0 then exit;
  1140. fr := GetFontRenderer;
  1141. if fr.FontEmHeight = 0 then exit;
  1142. pad := fr.FontEmHeight;
  1143. m := FGlobalMatrix* //global transform
  1144. GetUntransformedMatrix* //transform according to shape rectangle
  1145. AffineMatrixScale(1/zoom,1/zoom); //shrink zoomed text if necessary
  1146. tl := GetTextLayout;
  1147. sourceRectF := RectF(-pad,0,tl.AvailableWidth+pad,min(tl.TotalTextHeight,tl.AvailableHeight));
  1148. storeImage := not ADraft and CanHaveRenderStorage;
  1149. if storeImage then
  1150. destF := rectF(0,0,ADest.Width,ADest.Height)
  1151. else
  1152. begin
  1153. destF := RectF(ADest.ClipRect.Left,ADest.ClipRect.Top,ADest.ClipRect.Right,ADest.ClipRect.Bottom);
  1154. if PenPhong then
  1155. begin
  1156. destF.Left -= 1;
  1157. destF.Top -= 1;
  1158. destF.Right += 1;
  1159. destF.Bottom += 1;
  1160. end;
  1161. end;
  1162. transfRectF := (m*TAffineBox.AffineBox(sourceRectF)).RectBoundsF;
  1163. transfRectF := TRectF.Intersect(transfRectF, destF);
  1164. if not IsAffineMatrixInversible(m) then exit;
  1165. sourceInvRect := (AffineMatrixInverse(m)*TAffineBox.AffineBox(transfRectF)).RectBoundsF;
  1166. sourceInvRect.Top := floor(sourceInvRect.Top);
  1167. sourceInvRect.Bottom := ceil(sourceInvRect.Bottom);
  1168. sourceRectF := TRectF.Intersect(sourceRectF,sourceInvRect);
  1169. if IsEmptyRectF(sourceRectF) then exit;
  1170. sourceRectF.Left := floor(sourceRectF.Left);
  1171. sourceRectF.Top := floor(sourceRectF.Top);
  1172. sourceRectF.Right := floor(sourceRectF.Right);
  1173. sourceRectF.Bottom := sourceRectF.Bottom;
  1174. m := m*AffineMatrixTranslation(sourceRectF.Left,sourceRectF.Top);
  1175. if tl.TotalTextHeight < tl.AvailableHeight then
  1176. case VerticalAlignment of
  1177. tlBottom: m *= AffineMatrixTranslation(0, tl.AvailableHeight-tl.TotalTextHeight);
  1178. tlCenter: m *= AffineMatrixTranslation(0, (tl.AvailableHeight-tl.TotalTextHeight)/2);
  1179. end;
  1180. tl.TopLeft := PointF(-sourceRectF.Left,-sourceRectF.Top);
  1181. with transfRectF do
  1182. transfRect := Rect(floor(Left),floor(Top),ceil(Right),ceil(Bottom));
  1183. if UseVectorialTextRenderer then
  1184. begin
  1185. tmpTransf := TBGRABitmap.Create(transfRect.Width,transfRect.Height);
  1186. ctx := tmpTransf.Canvas2D;
  1187. ctx.transform(AffineMatrixTranslation(-transfRect.Left,-transfRect.Top)*m);
  1188. ctx.fillMode := fmWinding;
  1189. ctx.antialiasing:= not ADraft and not Aliased;
  1190. ctx.beginPath;
  1191. tl.PathText(ctx);
  1192. ctx.resetTransform;
  1193. tmpTransfMask := TBGRABitmap.Create(transfRect.Width,transfRect.Height, BGRABlack);
  1194. ctx := tmpTransfMask.Canvas2D;
  1195. ctx.linearBlend:= true;
  1196. ctx.transform(AffineMatrixTranslation(-transfRect.Left,-transfRect.Top)*m);
  1197. if PenPhong and not PenFill.IsFullyTransparent then
  1198. begin
  1199. ctx := tmpTransf.Canvas2D;
  1200. tmpTransf.Fill(BGRABlack);
  1201. ctx.linearBlend:= true;
  1202. ctx.fillStyle(BGRAWhite);
  1203. ctx.fill;
  1204. textFx := TBGRACustomTextEffect.Create(tmpTransf, false, tmpTransf.Width,tmpTransf.Height, Point(0,0));
  1205. tmpTransf.FillTransparent;
  1206. ctx.linearBlend:= false
  1207. end else
  1208. textFx := nil;
  1209. if HasOutline then
  1210. begin
  1211. ctx := tmpTransf.Canvas2D;
  1212. ctx.lineWidth := zoom*OutlineWidth;
  1213. ctx.lineJoinLCL:= pjsRound;
  1214. ctx.lineStyle(psSolid);
  1215. if OutlineFill.FillType = vftSolid then
  1216. begin
  1217. ctx.strokeStyle(OutlineFill.SolidColor);
  1218. ctx.stroke;
  1219. end else
  1220. if OutlineFill.FillType <> vftNone then
  1221. begin
  1222. scan := OutlineFill.CreateScanner(AffineMatrixTranslation(-transfRect.Left,-transfRect.Top)*FGlobalMatrix, ADraft);
  1223. ctx.strokeStyle(scan);
  1224. ctx.stroke;
  1225. ctx.strokeStyle(BGRABlack);
  1226. scan.Free;
  1227. end;
  1228. end;
  1229. if Assigned(textFx) then
  1230. begin
  1231. scan := PenFill.CreateScanner(AffineMatrixTranslation(-transfRect.Left,-transfRect.Top)*FGlobalMatrix, ADraft);
  1232. shader:= CreateShader(-transfRect.Left, -transfRect.Top);
  1233. textFx.DrawShaded(tmpTransf, 0,0, shader, GetTextPhongHeight, scan);
  1234. shader.Free;
  1235. scan.Free;
  1236. textFx.Free;
  1237. end else
  1238. if not PenFill.IsFullyTransparent then
  1239. begin
  1240. ctx := tmpTransf.Canvas2D;
  1241. if PenFill.FillType = vftSolid then
  1242. begin
  1243. ctx.fillStyle(PenFill.SolidColor);
  1244. ctx.fill;
  1245. end else
  1246. if PenFill.FillType <> vftNone then
  1247. begin
  1248. scan := PenFill.CreateScanner(AffineMatrixTranslation(-transfRect.Left,-transfRect.Top)*FGlobalMatrix, ADraft);
  1249. ctx.fillStyle(scan);
  1250. ctx.fill;
  1251. ctx.fillStyle(BGRABlack);
  1252. scan.Free;
  1253. end;
  1254. end;
  1255. ctx := tmpTransfMask.Canvas2D;
  1256. ctx.beginPath;
  1257. ctx.rect(0,0,sourceRectF.Width,sourceRectF.Height);
  1258. ctx.fillStyle(BGRAWhite);
  1259. ctx.fill;
  1260. tmpTransf.ApplyMask(tmpTransfMask);
  1261. tmpTransfMask.Free;
  1262. ADest.PutImage(transfRect.Left, transfRect.Top, tmpTransf, dmDrawWithTransparency);
  1263. end else
  1264. begin
  1265. if ADraft or Aliased then rf := rfBox else rf := rfHalfCosine;
  1266. if storeImage then
  1267. tmpTransf := TBGRABitmap.Create(transfRect.Width,transfRect.Height)
  1268. else
  1269. tmpTransf := nil;
  1270. if not PenPhong and (PenFill.FillType = vftSolid) then
  1271. begin
  1272. tmpSource := TBGRABitmap.Create(round(sourceRectF.Width),ceil(sourceRectF.Height));
  1273. tl.DrawText(tmpSource,PenFill.SolidColor);
  1274. if frac(sourceRectF.Height) > 0 then
  1275. tmpSource.EraseLine(0,floor(sourceRectF.Height),tmpSource.Width,floor(sourceRectF.Height), round((1-frac(sourceRectF.Height))*255), false);
  1276. if assigned(tmpTransf) then
  1277. tmpTransf.PutImageAffine(AffineMatrixTranslation(-transfRect.Left,-transfRect.Top)*m, tmpSource, rf, dmDrawWithTransparency, 255, false)
  1278. else
  1279. ADest.PutImageAffine(m, tmpSource, rf, dmDrawWithTransparency, 255, false);
  1280. tmpSource.Free;
  1281. end
  1282. else
  1283. if PenFill.FillType <> vftNone then
  1284. begin
  1285. tmpSource := TBGRABitmap.Create(round(sourceRectF.Width),ceil(sourceRectF.Height),BGRABlack);
  1286. tmpSource.LinearAntialiasing:= true;
  1287. tl.DrawText(tmpSource,BGRAWhite);
  1288. if frac(sourceRectF.Height) > 0 then
  1289. tmpSource.DrawLine(0,floor(sourceRectF.Height),tmpSource.Width,floor(sourceRectF.Height), BGRA(0,0,0,round((1-frac(sourceRectF.Height))*255)), false);
  1290. tmpTransfMask := TBGRABitmap.Create(transfRect.Width,transfRect.Height,BGRABlack);
  1291. tmpTransfMask.PutImageAffine(AffineMatrixTranslation(-transfRect.Left,-transfRect.Top)*m,
  1292. tmpSource, rf, dmDrawWithTransparency, 255, false);
  1293. tmpSource.Free;
  1294. if Assigned(tmpTransf) then
  1295. begin
  1296. scan := PenFill.CreateScanner(AffineMatrixTranslation(-transfRect.Left,-transfRect.Top)*FGlobalMatrix, ADraft);
  1297. if PenPhong then
  1298. begin
  1299. shader:= CreateShader(-transfRect.Left, -transfRect.Top);
  1300. textFx := TBGRACustomTextEffect.Create(tmpTransfMask, false, tmpTransfMask.Width,tmpTransfMask.Height, Point(0,0));
  1301. textFx.DrawShaded(tmpTransf, 0,0, shader, GetTextPhongHeight, scan);
  1302. textFx.Free;
  1303. shader.Free;
  1304. end else
  1305. tmpTransf.FillMask(0, 0, tmpTransfMask, scan, dmDrawWithTransparency)
  1306. end
  1307. else
  1308. begin
  1309. scan := PenFill.CreateScanner(FGlobalMatrix, ADraft);
  1310. if PenPhong then
  1311. begin
  1312. shader:= CreateShader(0,0);
  1313. textFx := TBGRACustomTextEffect.Create(tmpTransfMask, false, tmpTransfMask.Width,tmpTransfMask.Height, Point(0,0));
  1314. textFx.DrawShaded(ADest, transfRect.Left, transfRect.Top, shader, GetTextPhongHeight, scan);
  1315. textFx.Free;
  1316. shader.Free;
  1317. end else
  1318. ADest.FillMask(transfRect.Left, transfRect.Top, tmpTransfMask, scan, dmDrawWithTransparency);
  1319. end;
  1320. scan.Free;
  1321. tmpTransfMask.Free;
  1322. end;
  1323. if Assigned(tmpTransf) then
  1324. ADest.PutImage(transfRect.Left, transfRect.Top, tmpTransf, dmDrawWithTransparency);
  1325. end;
  1326. transfRect.Offset(-ARenderOffset.X,-ARenderOffset.Y);
  1327. if storeImage then UpdateRenderStorage(transfRect, tmpTransf)
  1328. else UpdateRenderStorage(transfRect);
  1329. tmpTransf.Free;
  1330. end;
  1331. function TTextShape.GetRenderBounds(ADestRect: TRect; AMatrix: TAffineMatrix;
  1332. AOptions: TRenderBoundsOptions): TRectF;
  1333. var
  1334. ab: TAffineBox;
  1335. u: TPointF;
  1336. lenU, margin: Single;
  1337. begin
  1338. if (GetPenVisible(rboAssumePenFill in AOptions) or HasOutline) and
  1339. (Text <> '') then
  1340. begin
  1341. ab := GetAffineBox(AMatrix, false);
  1342. //add margin for text that would be out of bound (for example italic j)
  1343. u := ab.TopRight-ab.TopLeft;
  1344. lenU := VectLen(u);
  1345. if lenU<>0 then u *= (1/lenU);
  1346. margin := FontEmHeight;
  1347. u *= margin;
  1348. ab.TopLeft -= u;
  1349. ab.TopRight += u;
  1350. ab.BottomLeft -= u;
  1351. result := ab.RectBoundsF;
  1352. end
  1353. else
  1354. result:= EmptyRectF;
  1355. end;
  1356. function TTextShape.PointInShape(APoint: TPointF): boolean;
  1357. begin
  1358. result := GetAffineBox(AffineMatrixIdentity,true).Contains(APoint);
  1359. end;
  1360. function TTextShape.PointInShape(APoint: TPointF; ARadius: single): boolean;
  1361. begin
  1362. result := false;
  1363. end;
  1364. function TTextShape.PointInPen(APoint: TPointF): boolean;
  1365. var
  1366. tl: TBidiTextLayout;
  1367. pt: TPointF;
  1368. i: Integer;
  1369. begin
  1370. if not GetAffineBox(AffineMatrixIdentity,true).Contains(APoint) then
  1371. exit(false);
  1372. SetGlobalMatrix(AffineMatrixIdentity);
  1373. tl := GetTextLayout;
  1374. pt := AffineMatrixInverse(GetUntransformedMatrix)*APoint;
  1375. for i := 0 to tl.PartCount-1 do
  1376. if tl.PartAffineBox[i].Contains(pt) then exit(true);
  1377. result := false;
  1378. end;
  1379. function TTextShape.GetIsSlow(const AMatrix: TAffineMatrix): boolean;
  1380. begin
  1381. Result:= true;
  1382. end;
  1383. function TTextShape.GetGenericCost: integer;
  1384. begin
  1385. Result:= 10;
  1386. end;
  1387. procedure TTextShape.MouseMove(Shift: TShiftState; X, Y: single;
  1388. var ACursor: TOriginalEditorCursor; var AHandled: boolean);
  1389. begin
  1390. if FMouseSelecting then
  1391. begin
  1392. SelectWithMouse(X,Y, true);
  1393. ACursor := oecText;
  1394. AHandled:= true;
  1395. end else
  1396. begin
  1397. inherited MouseMove(Shift, X, Y, ACursor, AHandled);
  1398. if (ACursor = oecDefault) and PointInShape(PointF(X,Y)) then ACursor := oecText;
  1399. end;
  1400. end;
  1401. procedure TTextShape.MouseDown(RightButton: boolean; Shift: TShiftState; X,
  1402. Y: single; var ACursor: TOriginalEditorCursor; var AHandled: boolean);
  1403. begin
  1404. inherited MouseDown(RightButton, Shift, X, Y, ACursor, AHandled);
  1405. if not AHandled and not RightButton and PointInShape(PointF(X,Y)) then
  1406. begin
  1407. FMouseSelecting:= true;
  1408. SelectWithMouse(X,Y, ssShift in Shift);
  1409. AHandled:= true;
  1410. end;
  1411. if (ACursor = oecDefault) and PointInShape(PointF(X,Y)) then ACursor := oecText;
  1412. end;
  1413. procedure TTextShape.MouseUp(RightButton: boolean; Shift: TShiftState; X,
  1414. Y: single; var ACursor: TOriginalEditorCursor; var AHandled: boolean);
  1415. begin
  1416. if FMouseSelecting and not RightButton then
  1417. begin
  1418. FMouseSelecting:= false;
  1419. ACursor := oecText;
  1420. AHandled:= true;
  1421. end else
  1422. begin
  1423. inherited MouseUp(RightButton, Shift, X, Y, ACursor, AHandled);
  1424. if (ACursor = oecDefault) and PointInShape(PointF(X,Y)) then ACursor := oecText;
  1425. end;
  1426. end;
  1427. procedure TTextShape.KeyDown(Shift: TShiftState; Key: TSpecialKey;
  1428. var AHandled: boolean);
  1429. var
  1430. idxPara, newPos: Integer;
  1431. tl: TBidiTextLayout;
  1432. begin
  1433. if (FTextLayout = nil) or (Usermode <> vsuEditText) then exit;
  1434. if Key = skDelete then
  1435. begin
  1436. if FSelStart <> FSelEnd then DeleteSelection
  1437. else DeleteTextAfter(1);
  1438. AHandled:= true;
  1439. end else
  1440. if Key in [skLeft,skRight] then
  1441. begin
  1442. tl := GetTextLayout;
  1443. if (Key = skLeft) xor tl.ParagraphRightToLeft[tl.GetParagraphAt(FSelEnd)] then
  1444. begin
  1445. BeginEditingUpdate;
  1446. if FSelEnd > 0 then
  1447. Dec(FSelEnd, tl.IncludeNonSpacingCharsBefore(FSelEnd,1) );
  1448. if not (ssShift in Shift) then FSelStart := FSelEnd;
  1449. EndEditingUpdate;
  1450. end else
  1451. begin
  1452. BeginEditingUpdate;
  1453. if FSelEnd < tl.CharCount then
  1454. Inc(FSelEnd, tl.IncludeNonSpacingChars(FSelEnd,1) );
  1455. if not (ssShift in Shift) then FSelStart := FSelEnd;
  1456. EndEditingUpdate;
  1457. end;
  1458. AHandled := true;
  1459. end else
  1460. if Key in [skUp,skDown] then
  1461. begin
  1462. tl := GetTextLayout;
  1463. if Key = skUp then
  1464. newPos := tl.FindTextAbove(FSelEnd)
  1465. else
  1466. newPos := tl.FindTextBelow(FSelEnd);
  1467. if (newPos <> -1) or (not (ssShift in Shift) and (FSelStart <> FSelEnd)) then
  1468. begin
  1469. BeginEditingUpdate;
  1470. FSelEnd := newPos;
  1471. if not (ssShift in Shift) then FSelStart := FSelEnd;
  1472. EndEditingUpdate;
  1473. end;
  1474. AHandled:= true;
  1475. end else
  1476. if Key = skHome then
  1477. begin
  1478. tl := GetTextLayout;
  1479. BeginEditingUpdate;
  1480. if ssCtrl in Shift then
  1481. FSelEnd := 0
  1482. else
  1483. begin
  1484. idxPara := tl.GetParagraphAt(FSelEnd);
  1485. FSelEnd := tl.ParagraphStartIndex[idxPara];
  1486. end;
  1487. if not (ssShift in Shift) then FSelStart := FSelEnd;
  1488. EndEditingUpdate;
  1489. AHandled := true;
  1490. end else
  1491. if Key = skEnd then
  1492. begin
  1493. tl := GetTextLayout;
  1494. BeginEditingUpdate;
  1495. if ssCtrl in Shift then
  1496. FSelEnd := tl.CharCount
  1497. else
  1498. begin
  1499. idxPara := tl.GetParagraphAt(FSelEnd);
  1500. FSelEnd := tl.ParagraphEndIndexBeforeParagraphSeparator[idxPara];
  1501. end;
  1502. if not (ssShift in Shift) then FSelStart := FSelEnd;
  1503. EndEditingUpdate;
  1504. AHandled := true;
  1505. end else
  1506. if (Key = skReturn) and ([ssCtrl,ssShift] <= Shift) and FEnteringUnicode then
  1507. begin
  1508. InsertUnicodeValue;
  1509. AHandled:= true;
  1510. end else
  1511. if Key = skReturn then
  1512. begin
  1513. if ssShift in Shift then
  1514. InsertText(UnicodeCharToUTF8(UNICODE_LINE_SEPARATOR))
  1515. else
  1516. InsertText(#10);
  1517. AHandled := true;
  1518. end else
  1519. if Key = skTab then
  1520. begin
  1521. InsertText(#9);
  1522. AHandled := true;
  1523. end else
  1524. if (Key = skU) and ([ssCtrl,ssShift] <= Shift) then
  1525. begin
  1526. if FEnteringUnicode then InsertUnicodeValue;
  1527. FEnteringUnicode:= true;
  1528. FUnicodeValue:= 0;
  1529. FUnicodeDigitCount:= 0;
  1530. AHandled := true;
  1531. end else
  1532. if (Key in[sk0..sk9,skNum0..skNum9,skA..skF]) and ([ssCtrl,ssShift] <= Shift) and FEnteringUnicode then
  1533. begin
  1534. if FUnicodeDigitCount >= 8 then FEnteringUnicode:= false else
  1535. begin
  1536. FUnicodeValue := (FUnicodeValue shl 4);
  1537. case Key of
  1538. sk0..sk9: inc(FUnicodeValue, ord(Key)-ord(sk0));
  1539. skNum0..skNum9: inc(FUnicodeValue, ord(Key)-ord(sk0));
  1540. skA..skF: inc(FUnicodeValue, ord(Key)-ord(skA)+10);
  1541. end;
  1542. end;
  1543. end else
  1544. if (Key = skC) and (ssCtrl in Shift) then
  1545. begin
  1546. if CopySelection then AHandled:= true;
  1547. end else
  1548. if (Key = skX) and (ssCtrl in Shift) then
  1549. begin
  1550. if CutSelection then AHandled:= true;
  1551. end else
  1552. if (Key = skV) and (ssCtrl in Shift) then
  1553. begin
  1554. if PasteSelection then AHandled := true;
  1555. end else
  1556. if (Key = skA) and (ssCtrl in Shift) then
  1557. begin
  1558. BeginEditingUpdate;
  1559. FSelStart:= 0;
  1560. FSelEnd:= GetTextLayout.CharCount;
  1561. EndEditingUpdate;
  1562. AHandled := true;
  1563. end;
  1564. end;
  1565. procedure TTextShape.KeyPress(UTF8Key: string; var AHandled: boolean);
  1566. begin
  1567. if (Usermode = vsuEditText) and (UTF8Key = #8) then
  1568. begin
  1569. if FSelEnd <> FSelStart then DeleteSelection
  1570. else DeleteTextBefore(1);
  1571. AHandled := true;
  1572. end else
  1573. if UTF8Key >= ' ' then
  1574. begin
  1575. if Usermode <> vsuEditText then
  1576. begin
  1577. if Text = '' then
  1578. begin
  1579. Usermode := vsuEditText;
  1580. InsertText(UTF8Key);
  1581. end;
  1582. end else
  1583. InsertText(UTF8Key);
  1584. AHandled := true;
  1585. end;
  1586. end;
  1587. procedure TTextShape.KeyUp(Shift: TShiftState; Key: TSpecialKey;
  1588. var AHandled: boolean);
  1589. begin
  1590. if (Key in[skCtrl,skShift]) and FEnteringUnicode then
  1591. begin
  1592. InsertUnicodeValue;
  1593. AHandled := true;
  1594. end;
  1595. end;
  1596. procedure TTextShape.SetFontNameAndStyle(AFontName: string;
  1597. AFontStyle: TFontStyles);
  1598. begin
  1599. if (AFontName <> FFontName) or (AFontStyle <> FFontStyle) then
  1600. begin
  1601. BeginUpdate(TTextShapeFontDiff);
  1602. FFontName := AFontName;
  1603. FFontStyle:= AFontStyle;
  1604. EndUpdate;
  1605. end;
  1606. end;
  1607. function TTextShape.CopySelection: boolean;
  1608. var
  1609. stream: TStringStream;
  1610. begin
  1611. if HasSelection then
  1612. begin
  1613. stream := nil;
  1614. try
  1615. Clipboard.Clear;
  1616. stream := TStringStream.Create(GetTextLayout.CopyText(min(FSelStart,FSelEnd),abs(FSelEnd-FSelStart)));
  1617. Clipboard.SetFormat(PredefinedClipboardFormat(pcfText), stream);
  1618. finally
  1619. stream.Free;
  1620. end;
  1621. result := true;
  1622. end
  1623. else result := false;
  1624. end;
  1625. function TTextShape.CutSelection: boolean;
  1626. begin
  1627. result := CopySelection;
  1628. if result then DeleteSelection;
  1629. end;
  1630. function TTextShape.PasteSelection: boolean;
  1631. var
  1632. txt: String;
  1633. begin
  1634. if CanPasteSelection then
  1635. begin
  1636. txt := Clipboard.AsText;
  1637. txt := StringReplace(txt, #13#10, #10, [rfReplaceAll]);
  1638. txt := StringReplace(txt, #10#13, #10, [rfReplaceAll]);
  1639. txt := StringReplace(txt, #13, #10, [rfReplaceAll]);
  1640. txt := StringReplace(txt, UnicodeCharToUTF8(UNICODE_PARAGRAPH_SEPARATOR), #10, [rfReplaceAll]);
  1641. txt := StringReplace(txt, UnicodeCharToUTF8(UNICODE_NEXT_LINE), #10, [rfReplaceAll]);
  1642. InsertText(txt);
  1643. result := true;
  1644. end else
  1645. result := false;
  1646. end;
  1647. procedure TTextShape.Transform(const AMatrix: TAffineMatrix);
  1648. var
  1649. zoom: Single;
  1650. begin
  1651. BeginUpdate;
  1652. AddDiffHandler(TTextShapeFontDiff);
  1653. AddDiffHandler(TTextShapePhongDiff);
  1654. zoom := (VectLen(AMatrix[1,1],AMatrix[2,1])+VectLen(AMatrix[1,2],AMatrix[2,2]))/2;
  1655. FontEmHeight:= zoom*FontEmHeight;
  1656. LightPosition := AMatrix*LightPosition;
  1657. inherited Transform(AMatrix);
  1658. EndUpdate;
  1659. end;
  1660. class function TTextShape.StorageClassName: RawByteString;
  1661. begin
  1662. result := 'text';
  1663. end;
  1664. class function TTextShape.Usermodes: TVectorShapeUsermodes;
  1665. begin
  1666. Result:=inherited Usermodes + [vsuEditText];
  1667. end;
  1668. initialization
  1669. RegisterVectorShape(TTextShape);
  1670. end.