| 1 | unit BGRATypewriter;
|
|---|
| 2 |
|
|---|
| 3 | {$mode objfpc}{$H+}
|
|---|
| 4 |
|
|---|
| 5 | interface
|
|---|
| 6 |
|
|---|
| 7 | uses
|
|---|
| 8 | Classes, SysUtils, AvgLvlTree, BGRABitmapTypes, BGRACanvas2D, BGRATransform;
|
|---|
| 9 |
|
|---|
| 10 | type
|
|---|
| 11 | TGlyphBoxes = array of record
|
|---|
| 12 | Glyph: string;
|
|---|
| 13 | Box: TAffineBox;
|
|---|
| 14 | end;
|
|---|
| 15 |
|
|---|
| 16 | { TBGRAGlyph }
|
|---|
| 17 |
|
|---|
| 18 | TBGRAGlyph = class
|
|---|
| 19 | protected
|
|---|
| 20 | FIdentifier: string;
|
|---|
| 21 | procedure WriteHeader(AStream: TStream; AName: string; AContentSize: longint);
|
|---|
| 22 | class procedure ReadHeader(AStream: TStream; out AName: string; out AContentSize: longint);
|
|---|
| 23 | function ContentSize: integer; virtual;
|
|---|
| 24 | function HeaderName: string; virtual;
|
|---|
| 25 | procedure WriteContent(AStream: TStream); virtual;
|
|---|
| 26 | procedure ReadContent(AStream: TStream); virtual;
|
|---|
| 27 | public
|
|---|
| 28 | Width,Height: single;
|
|---|
| 29 | constructor Create(AIdentifier: string); virtual;
|
|---|
| 30 | constructor Create(AStream: TStream); virtual;
|
|---|
| 31 | procedure Path({%H-}ADest: IBGRAPath; {%H-}AMatrix: TAffineMatrix); virtual;
|
|---|
| 32 | property Identifier: string read FIdentifier;
|
|---|
| 33 | procedure SaveToStream(AStream: TStream);
|
|---|
| 34 | class function LoadFromStream(AStream: TStream): TBGRAGlyph;
|
|---|
| 35 | end;
|
|---|
| 36 |
|
|---|
| 37 | TGlyphPointCurveMode= TEasyBezierCurveMode;
|
|---|
| 38 |
|
|---|
| 39 | const
|
|---|
| 40 | cmAuto = TEasyBezierCurveMode.cmAuto;
|
|---|
| 41 | cmCurve = TEasyBezierCurveMode.cmCurve;
|
|---|
| 42 | cmAngle = TEasyBezierCurveMode.cmAngle;
|
|---|
| 43 |
|
|---|
| 44 | type
|
|---|
| 45 | { TBGRAPolygonalGlyph }
|
|---|
| 46 |
|
|---|
| 47 | TBGRAPolygonalGlyph = class(TBGRAGlyph)
|
|---|
| 48 | private
|
|---|
| 49 | function GetClosed: boolean;
|
|---|
| 50 | function GetMinimumDotProduct: single;
|
|---|
| 51 | function GetPoint(AIndex: integer): TPointF;
|
|---|
| 52 | function GetPointCount: integer;
|
|---|
| 53 | procedure SetClosed(AValue: boolean);
|
|---|
| 54 | procedure SetMinimumDotProduct(AValue: single);
|
|---|
| 55 | procedure SetPoint(AIndex: integer; AValue: TPointF);
|
|---|
| 56 | procedure SetQuadraticCurves(AValue: boolean);
|
|---|
| 57 | protected
|
|---|
| 58 | FQuadraticCurves: boolean;
|
|---|
| 59 | FEasyBezier: TEasyBezierCurve;
|
|---|
| 60 | function ContentSize: integer; override;
|
|---|
| 61 | function HeaderName: string; override;
|
|---|
| 62 | procedure WriteContent(AStream: TStream); override;
|
|---|
| 63 | procedure ReadContent(AStream: TStream); override;
|
|---|
| 64 | function PointTransformMatrix(APoint: PPointF; AData: pointer): TPointF;
|
|---|
| 65 | procedure Init;
|
|---|
| 66 | public
|
|---|
| 67 | Offset: TPointF;
|
|---|
| 68 | constructor Create(AIdentifier: string); override;
|
|---|
| 69 | constructor Create(AStream: TStream); override;
|
|---|
| 70 | constructor Create(AStream: TStream; AQuadratic: boolean);
|
|---|
| 71 | procedure SetPoints(const APoints: array of TPointF); overload;
|
|---|
| 72 | procedure SetPoints(const APoints: array of TPointF; const ACurveMode: array of TGlyphPointCurveMode); overload;
|
|---|
| 73 | procedure Path(ADest: IBGRAPath; AMatrix: TAffineMatrix); override;
|
|---|
| 74 | property QuadraticCurves: boolean read FQuadraticCurves write SetQuadraticCurves;
|
|---|
| 75 | property Closed: boolean read GetClosed write SetClosed;
|
|---|
| 76 | property MinimumDotProduct: single read GetMinimumDotProduct write SetMinimumDotProduct;
|
|---|
| 77 | property Point[AIndex: integer]: TPointF read GetPoint write SetPoint;
|
|---|
| 78 | property PointCount: integer read GetPointCount;
|
|---|
| 79 | end;
|
|---|
| 80 |
|
|---|
| 81 | TBGRACustomTypeWriterHeader = record
|
|---|
| 82 | HeaderName: String;
|
|---|
| 83 | NbGlyphs: integer;
|
|---|
| 84 | end;
|
|---|
| 85 |
|
|---|
| 86 | { TBGRACustomTypeWriter }
|
|---|
| 87 |
|
|---|
| 88 | TBGRACustomTypeWriter = class
|
|---|
| 89 | private
|
|---|
| 90 | FGlyphs: TAvgLvlTree;
|
|---|
| 91 | protected
|
|---|
| 92 | TypeWriterMatrix: TAffineMatrix;
|
|---|
| 93 | function CompareGlyph({%H-}Tree: TAvgLvlTree; Data1, Data2: Pointer): integer;
|
|---|
| 94 | function FindGlyph(AIdentifier: string): TAvgLvlTreeNode;
|
|---|
| 95 | function GetGlyph(AIdentifier: string): TBGRAGlyph; virtual;
|
|---|
| 96 | procedure SetGlyph(AIdentifier: string; AValue: TBGRAGlyph);
|
|---|
| 97 | procedure TextPath(ADest: TBGRACanvas2D; ATextUTF8: string; X,Y: Single; AAlign: TBGRATypeWriterAlignment; ADrawEachChar: boolean);
|
|---|
| 98 | procedure GlyphPath(ADest: TBGRACanvas2D; AIdentifier: string; X,Y: Single; AAlign: TBGRATypeWriterAlignment = twaTopLeft);
|
|---|
| 99 | procedure DrawLastPath(ADest: TBGRACanvas2D);
|
|---|
| 100 | procedure ClearGlyphs;
|
|---|
| 101 | procedure RemoveGlyph(AIdentifier: string);
|
|---|
| 102 | procedure AddGlyph(AGlyph: TBGRAGlyph);
|
|---|
| 103 | function GetGlyphMatrix(AGlyph: TBGRAGlyph; X,Y: Single; AAlign: TBGRATypeWriterAlignment): TAffineMatrix;
|
|---|
| 104 | function GetTextMatrix(ATextUTF8: string; X,Y: Single; AAlign: TBGRATypeWriterAlignment): TAffineMatrix;
|
|---|
| 105 | property Glyph[AIdentifier: string]: TBGRAGlyph read GetGlyph write SetGlyph;
|
|---|
| 106 | function CustomHeaderSize: integer; virtual;
|
|---|
| 107 | procedure WriteCustomHeader(AStream: TStream); virtual;
|
|---|
| 108 | function ReadCustomTypeWriterHeader(AStream: TStream): TBGRACustomTypeWriterHeader;
|
|---|
| 109 | procedure ReadAdditionalHeader({%H-}AStream: TStream); virtual;
|
|---|
| 110 | function HeaderName: string; virtual;
|
|---|
| 111 | public
|
|---|
| 112 | OutlineMode: TBGRATypeWriterOutlineMode;
|
|---|
| 113 | DrawGlyphsSimultaneously : boolean;
|
|---|
| 114 | constructor Create;
|
|---|
| 115 | procedure SaveGlyphsToFile(AFilenameUTF8: string);
|
|---|
| 116 | procedure SaveGlyphsToStream(AStream: TStream);
|
|---|
| 117 | procedure LoadGlyphsFromFile(AFilenameUTF8: string);
|
|---|
| 118 | procedure LoadGlyphsFromStream(AStream: TStream);
|
|---|
| 119 | procedure DrawGlyph(ADest: TBGRACanvas2D; AIdentifier: string; X,Y: Single; AAlign: TBGRATypeWriterAlignment = twaTopLeft);
|
|---|
| 120 | procedure DrawText(ADest: TBGRACanvas2D; ATextUTF8: string; X,Y: Single; AAlign: TBGRATypeWriterAlignment = twaTopLeft); virtual;
|
|---|
| 121 | procedure CopyTextPathTo(ADest: IBGRAPath; ATextUTF8: string; X,Y: Single; AAlign: TBGRATypeWriterAlignment = twaTopLeft); virtual;
|
|---|
| 122 | function GetGlyphBox(AIdentifier: string; X,Y: Single; AAlign: TBGRATypeWriterAlignment = twaTopLeft): TAffineBox;
|
|---|
| 123 | function GetTextBox(ATextUTF8: string; X,Y: Single; AAlign: TBGRATypeWriterAlignment = twaTopLeft): TAffineBox;
|
|---|
| 124 | function GetTextGlyphBoxes(ATextUTF8: string; X,Y: Single; AAlign: TBGRATypeWriterAlignment = twaTopLeft): TGlyphBoxes;
|
|---|
| 125 | procedure NeedGlyphRange(AUnicodeFrom, AUnicodeTo: Cardinal);
|
|---|
| 126 | procedure NeedGlyphAnsiRange;
|
|---|
| 127 | destructor Destroy; override;
|
|---|
| 128 | end;
|
|---|
| 129 |
|
|---|
| 130 | function ComputeEasyBezier(APoints: array of TPointF; AClosed: boolean; AMinimumDotProduct: single = 0.707): ArrayOfTPointF; overload;
|
|---|
| 131 | function ComputeEasyBezier(APoints: array of TPointF; ACurveMode: array of TGlyphPointCurveMode; AClosed: boolean; AMinimumDotProduct: single = 0.707): ArrayOfTPointF; overload;
|
|---|
| 132 |
|
|---|
| 133 | implementation
|
|---|
| 134 |
|
|---|
| 135 | uses BGRAUTF8;
|
|---|
| 136 |
|
|---|
| 137 | procedure LEWritePointF(Stream: TStream; AValue: TPointF);
|
|---|
| 138 | begin
|
|---|
| 139 | LEWriteSingle(Stream,AValue.x);
|
|---|
| 140 | LEWriteSingle(Stream,AValue.y);
|
|---|
| 141 | end;
|
|---|
| 142 |
|
|---|
| 143 | function LEReadPointF(Stream: TStream): TPointF;
|
|---|
| 144 | begin
|
|---|
| 145 | result.x := LEReadSingle(Stream);
|
|---|
| 146 | result.y := LEReadSingle(Stream);
|
|---|
| 147 | end;
|
|---|
| 148 |
|
|---|
| 149 | function ComputeEasyBezier(APoints: array of TPointF; AClosed: boolean; AMinimumDotProduct: single = 0.707): ArrayOfTPointF;
|
|---|
| 150 | var
|
|---|
| 151 | glyph: TBGRAPolygonalGlyph;
|
|---|
| 152 | canvas2D: TBGRACanvas2D;
|
|---|
| 153 | i: integer;
|
|---|
| 154 | begin
|
|---|
| 155 | if length(APoints) <= 2 then
|
|---|
| 156 | begin
|
|---|
| 157 | setlength(result, length(APoints));
|
|---|
| 158 | for i := 0 to high(result) do
|
|---|
| 159 | result[i] := APoints[i];
|
|---|
| 160 | exit;
|
|---|
| 161 | end;
|
|---|
| 162 | glyph := TBGRAPolygonalGlyph.Create('');
|
|---|
| 163 | glyph.QuadraticCurves := true;
|
|---|
| 164 | glyph.Closed:= AClosed;
|
|---|
| 165 | glyph.MinimumDotProduct := AMinimumDotProduct;
|
|---|
| 166 | glyph.SetPoints(APoints);
|
|---|
| 167 | canvas2D := TBGRACanvas2D.Create(nil);
|
|---|
| 168 | canvas2D.pixelCenteredCoordinates := true;
|
|---|
| 169 | glyph.Path(canvas2D,AffineMatrixIdentity);
|
|---|
| 170 | glyph.Free;
|
|---|
| 171 | result := canvas2D.currentPath;
|
|---|
| 172 | canvas2D.free;
|
|---|
| 173 | end;
|
|---|
| 174 |
|
|---|
| 175 | function ComputeEasyBezier(APoints: array of TPointF;
|
|---|
| 176 | ACurveMode: array of TGlyphPointCurveMode; AClosed: boolean;
|
|---|
| 177 | AMinimumDotProduct: single): ArrayOfTPointF;
|
|---|
| 178 | var
|
|---|
| 179 | glyph: TBGRAPolygonalGlyph;
|
|---|
| 180 | canvas2D: TBGRACanvas2D;
|
|---|
| 181 | i: integer;
|
|---|
| 182 | begin
|
|---|
| 183 | if length(APoints) <= 2 then
|
|---|
| 184 | begin
|
|---|
| 185 | setlength(result, length(APoints));
|
|---|
| 186 | for i := 0 to high(result) do
|
|---|
| 187 | result[i] := APoints[i];
|
|---|
| 188 | exit;
|
|---|
| 189 | end;
|
|---|
| 190 | glyph := TBGRAPolygonalGlyph.Create('');
|
|---|
| 191 | glyph.QuadraticCurves := true;
|
|---|
| 192 | glyph.Closed:= AClosed;
|
|---|
| 193 | glyph.MinimumDotProduct := AMinimumDotProduct;
|
|---|
| 194 | glyph.SetPoints(APoints, ACurveMode);
|
|---|
| 195 | canvas2D := TBGRACanvas2D.Create(nil);
|
|---|
| 196 | canvas2D.pixelCenteredCoordinates := true;
|
|---|
| 197 | glyph.Path(canvas2D,AffineMatrixIdentity);
|
|---|
| 198 | glyph.Free;
|
|---|
| 199 | result := canvas2D.currentPath;
|
|---|
| 200 | canvas2D.free;
|
|---|
| 201 | end;
|
|---|
| 202 |
|
|---|
| 203 | { TBGRAPolygonalGlyph }
|
|---|
| 204 |
|
|---|
| 205 | function TBGRAPolygonalGlyph.GetClosed: boolean;
|
|---|
| 206 | begin
|
|---|
| 207 | result := FEasyBezier.Closed;
|
|---|
| 208 | end;
|
|---|
| 209 |
|
|---|
| 210 | function TBGRAPolygonalGlyph.GetMinimumDotProduct: single;
|
|---|
| 211 | begin
|
|---|
| 212 | result := FEasyBezier.MinimumDotProduct;
|
|---|
| 213 | end;
|
|---|
| 214 |
|
|---|
| 215 | function TBGRAPolygonalGlyph.GetPoint(AIndex: integer): TPointF;
|
|---|
| 216 | begin
|
|---|
| 217 | result := FEasyBezier.Point[AIndex];
|
|---|
| 218 | end;
|
|---|
| 219 |
|
|---|
| 220 | function TBGRAPolygonalGlyph.GetPointCount: integer;
|
|---|
| 221 | begin
|
|---|
| 222 | result := FEasyBezier.PointCount;
|
|---|
| 223 | end;
|
|---|
| 224 |
|
|---|
| 225 | procedure TBGRAPolygonalGlyph.SetClosed(AValue: boolean);
|
|---|
| 226 | begin
|
|---|
| 227 | FEasyBezier.Closed := AValue;
|
|---|
| 228 | end;
|
|---|
| 229 |
|
|---|
| 230 | procedure TBGRAPolygonalGlyph.SetMinimumDotProduct(AValue: single);
|
|---|
| 231 | begin
|
|---|
| 232 | FEasyBezier.MinimumDotProduct := AValue;
|
|---|
| 233 | end;
|
|---|
| 234 |
|
|---|
| 235 | procedure TBGRAPolygonalGlyph.SetPoint(AIndex: integer; AValue: TPointF);
|
|---|
| 236 | begin
|
|---|
| 237 | FEasyBezier.Point[AIndex] := AValue;
|
|---|
| 238 | end;
|
|---|
| 239 |
|
|---|
| 240 | procedure TBGRAPolygonalGlyph.SetQuadraticCurves(AValue: boolean);
|
|---|
| 241 | begin
|
|---|
| 242 | if FQuadraticCurves=AValue then Exit;
|
|---|
| 243 | FQuadraticCurves:=AValue;
|
|---|
| 244 | end;
|
|---|
| 245 |
|
|---|
| 246 | function TBGRAPolygonalGlyph.ContentSize: integer;
|
|---|
| 247 | begin
|
|---|
| 248 | Result:= (inherited ContentSize) + sizeof(single)*2 + 4 + sizeof(single)*2*PointCount;
|
|---|
| 249 | end;
|
|---|
| 250 |
|
|---|
| 251 | function TBGRAPolygonalGlyph.HeaderName: string;
|
|---|
| 252 | begin
|
|---|
| 253 | if FQuadraticCurves then
|
|---|
| 254 | Result:='TBGRAEasyBezierGlyph'
|
|---|
| 255 | else
|
|---|
| 256 | Result:='TBGRAPolygonalGlyph'
|
|---|
| 257 | end;
|
|---|
| 258 |
|
|---|
| 259 | procedure TBGRAPolygonalGlyph.WriteContent(AStream: TStream);
|
|---|
| 260 | var i: integer;
|
|---|
| 261 | begin
|
|---|
| 262 | inherited WriteContent(AStream);
|
|---|
| 263 | LEWritePointF(AStream, Offset);
|
|---|
| 264 | LEWriteLongint(AStream,PointCount);
|
|---|
| 265 | for i := 0 to PointCount-1 do
|
|---|
| 266 | LEWritePointF(AStream, FEasyBezier.Point[i]);
|
|---|
| 267 | if FQuadraticCurves then
|
|---|
| 268 | for i := 0 to PointCount-1 do
|
|---|
| 269 | LEWriteLongint(AStream, ord(FEasyBezier.CurveMode[i]));
|
|---|
| 270 | end;
|
|---|
| 271 |
|
|---|
| 272 | procedure TBGRAPolygonalGlyph.ReadContent(AStream: TStream);
|
|---|
| 273 | var i: integer;
|
|---|
| 274 | tempPts: array of TPointF;
|
|---|
| 275 | flags: LongInt;
|
|---|
| 276 | begin
|
|---|
| 277 | inherited ReadContent(AStream);
|
|---|
| 278 | Offset := LEReadPointF(AStream);
|
|---|
| 279 | SetLength(tempPts, LEReadLongint(AStream));
|
|---|
| 280 | for i := 0 to high(tempPts) do
|
|---|
| 281 | tempPts[i] := LEReadPointF(AStream);
|
|---|
| 282 | SetPoints(tempPts);
|
|---|
| 283 | if FQuadraticCurves then
|
|---|
| 284 | begin
|
|---|
| 285 | for i := 0 to high(tempPts) do
|
|---|
| 286 | begin
|
|---|
| 287 | flags := LEReadLongint(AStream);
|
|---|
| 288 | FEasyBezier.CurveMode[i] := TEasyBezierCurveMode(flags and 255);
|
|---|
| 289 | end;
|
|---|
| 290 | end;
|
|---|
| 291 | end;
|
|---|
| 292 |
|
|---|
| 293 | function TBGRAPolygonalGlyph.PointTransformMatrix(APoint: PPointF;
|
|---|
| 294 | AData: pointer): TPointF;
|
|---|
| 295 | begin
|
|---|
| 296 | result := TAffineMatrix(AData^) * APoint^;
|
|---|
| 297 | end;
|
|---|
| 298 |
|
|---|
| 299 | procedure TBGRAPolygonalGlyph.Init;
|
|---|
| 300 | begin
|
|---|
| 301 | FEasyBezier.Init;
|
|---|
| 302 | Closed := True;
|
|---|
| 303 | Offset := PointF(0,0);
|
|---|
| 304 | FQuadraticCurves:= False;
|
|---|
| 305 | end;
|
|---|
| 306 |
|
|---|
| 307 | constructor TBGRAPolygonalGlyph.Create(AIdentifier: string);
|
|---|
| 308 | begin
|
|---|
| 309 | Init;
|
|---|
| 310 | inherited Create(AIdentifier);
|
|---|
| 311 | end;
|
|---|
| 312 |
|
|---|
| 313 | constructor TBGRAPolygonalGlyph.Create(AStream: TStream);
|
|---|
| 314 | begin
|
|---|
| 315 | Init;
|
|---|
| 316 | inherited Create(AStream);
|
|---|
| 317 | end;
|
|---|
| 318 |
|
|---|
| 319 | constructor TBGRAPolygonalGlyph.Create(AStream: TStream; AQuadratic: boolean);
|
|---|
| 320 | begin
|
|---|
| 321 | Init;
|
|---|
| 322 | FQuadraticCurves:= AQuadratic;
|
|---|
| 323 | inherited Create(AStream);
|
|---|
| 324 | end;
|
|---|
| 325 |
|
|---|
| 326 | procedure TBGRAPolygonalGlyph.SetPoints(const APoints: array of TPointF);
|
|---|
| 327 | begin
|
|---|
| 328 | FEasyBezier.SetPoints(APoints, cmAuto);
|
|---|
| 329 | end;
|
|---|
| 330 |
|
|---|
| 331 | procedure TBGRAPolygonalGlyph.SetPoints(const APoints: array of TPointF;
|
|---|
| 332 | const ACurveMode: array of TGlyphPointCurveMode);
|
|---|
| 333 | begin
|
|---|
| 334 | if length(APoints) <> length(ACurveMode) then
|
|---|
| 335 | raise exception.Create('Dimension mismatch');
|
|---|
| 336 | FEasyBezier.SetPoints(APoints, ACurveMode);
|
|---|
| 337 | end;
|
|---|
| 338 |
|
|---|
| 339 | procedure TBGRAPolygonalGlyph.Path(ADest: IBGRAPath; AMatrix: TAffineMatrix);
|
|---|
| 340 | var i: integer;
|
|---|
| 341 | nextMove: boolean;
|
|---|
| 342 | begin
|
|---|
| 343 | AMatrix := AMatrix*AffineMatrixTranslation(Offset.X,Offset.Y);
|
|---|
| 344 | if not FQuadraticCurves then
|
|---|
| 345 | begin
|
|---|
| 346 | nextMove := true;
|
|---|
| 347 | for i := 0 to PointCount-1 do
|
|---|
| 348 | if isEmptyPointF(Point[i]) then
|
|---|
| 349 | begin
|
|---|
| 350 | if not nextMove and Closed then ADest.closePath;
|
|---|
| 351 | nextMove := true;
|
|---|
| 352 | end else
|
|---|
| 353 | begin
|
|---|
| 354 | if nextMove then
|
|---|
| 355 | begin
|
|---|
| 356 | ADest.moveTo(AMatrix*Point[i]);
|
|---|
| 357 | nextMove := false;
|
|---|
| 358 | end else
|
|---|
| 359 | ADest.lineTo(AMatrix*Point[i]);
|
|---|
| 360 | end;
|
|---|
| 361 | if not nextmove and Closed then ADest.closePath;
|
|---|
| 362 | end else
|
|---|
| 363 | FEasyBezier.CopyToPath(ADest, @PointTransformMatrix, @AMatrix);
|
|---|
| 364 | end;
|
|---|
| 365 |
|
|---|
| 366 | { TBGRAGlyph }
|
|---|
| 367 |
|
|---|
| 368 | procedure TBGRAGlyph.WriteHeader(AStream: TStream; AName: string;
|
|---|
| 369 | AContentSize: longint);
|
|---|
| 370 | begin
|
|---|
| 371 | LEWriteByte(AStream, length(AName));
|
|---|
| 372 | AStream.Write(AName[1],length(AName));
|
|---|
| 373 | LEWriteLongint(AStream, AContentSize);
|
|---|
| 374 | end;
|
|---|
| 375 |
|
|---|
| 376 | class procedure TBGRAGlyph.ReadHeader(AStream: TStream; out AName: string; out
|
|---|
| 377 | AContentSize: longint);
|
|---|
| 378 | var NameLength: integer;
|
|---|
| 379 | begin
|
|---|
| 380 | NameLength := LEReadByte(AStream);
|
|---|
| 381 | setlength(AName,NameLength);
|
|---|
| 382 | AStream.Read(AName[1],length(AName));
|
|---|
| 383 | AContentSize := LEReadLongint(AStream);
|
|---|
| 384 | end;
|
|---|
| 385 |
|
|---|
| 386 | function TBGRAGlyph.ContentSize: integer;
|
|---|
| 387 | begin
|
|---|
| 388 | result := 4+length(FIdentifier)+sizeof(single)*2;
|
|---|
| 389 | end;
|
|---|
| 390 |
|
|---|
| 391 | function TBGRAGlyph.HeaderName: string;
|
|---|
| 392 | begin
|
|---|
| 393 | result := 'TBGRAGlyph';
|
|---|
| 394 | end;
|
|---|
| 395 |
|
|---|
| 396 | procedure TBGRAGlyph.WriteContent(AStream: TStream);
|
|---|
| 397 | begin
|
|---|
| 398 | LEWriteLongint(AStream,length(FIdentifier));
|
|---|
| 399 | AStream.Write(FIdentifier[1],length(FIdentifier));
|
|---|
| 400 | LEWriteSingle(AStream,Width);
|
|---|
| 401 | LEWriteSingle(AStream,Height);
|
|---|
| 402 | end;
|
|---|
| 403 |
|
|---|
| 404 | procedure TBGRAGlyph.ReadContent(AStream: TStream);
|
|---|
| 405 | var lIdentifierLength: integer;
|
|---|
| 406 | begin
|
|---|
| 407 | lIdentifierLength:= LEReadLongint(AStream);
|
|---|
| 408 | setlength(FIdentifier, lIdentifierLength);
|
|---|
| 409 | AStream.Read(FIdentifier[1],length(FIdentifier));
|
|---|
| 410 | Width := LEReadSingle(AStream);
|
|---|
| 411 | Height := LEReadSingle(AStream);
|
|---|
| 412 | end;
|
|---|
| 413 |
|
|---|
| 414 | constructor TBGRAGlyph.Create(AIdentifier: string);
|
|---|
| 415 | begin
|
|---|
| 416 | FIdentifier:= AIdentifier;
|
|---|
| 417 | end;
|
|---|
| 418 |
|
|---|
| 419 | constructor TBGRAGlyph.Create(AStream: TStream);
|
|---|
| 420 | begin
|
|---|
| 421 | ReadContent(AStream);
|
|---|
| 422 | end;
|
|---|
| 423 |
|
|---|
| 424 | procedure TBGRAGlyph.Path(ADest: IBGRAPath; AMatrix: TAffineMatrix);
|
|---|
| 425 | begin
|
|---|
| 426 | //nothing
|
|---|
| 427 | end;
|
|---|
| 428 |
|
|---|
| 429 | procedure TBGRAGlyph.SaveToStream(AStream: TStream);
|
|---|
| 430 | begin
|
|---|
| 431 | WriteHeader(AStream, HeaderName, ContentSize);
|
|---|
| 432 | WriteContent(AStream);
|
|---|
| 433 | end;
|
|---|
| 434 |
|
|---|
| 435 | class function TBGRAGlyph.LoadFromStream(AStream: TStream) : TBGRAGlyph;
|
|---|
| 436 | var lName: string;
|
|---|
| 437 | lContentSize: integer;
|
|---|
| 438 | EndPosition: Int64;
|
|---|
| 439 | begin
|
|---|
| 440 | ReadHeader(AStream,lName,lContentSize);
|
|---|
| 441 | EndPosition := AStream.Position + lContentSize;
|
|---|
| 442 | if lName = 'TBGRAPolygonalGlyph' then
|
|---|
| 443 | result := TBGRAPolygonalGlyph.Create(AStream)
|
|---|
| 444 | else if lName = 'TBGRAEasyBezierGlyph' then
|
|---|
| 445 | result := TBGRAPolygonalGlyph.Create(AStream, true)
|
|---|
| 446 | else if lName = 'TBGRAGlyph' then
|
|---|
| 447 | result := TBGRAGlyph.Create(AStream)
|
|---|
| 448 | else
|
|---|
| 449 | raise exception.Create('Unknown glyph type (' + lName + ')');
|
|---|
| 450 | AStream.Position:= EndPosition;
|
|---|
| 451 | end;
|
|---|
| 452 |
|
|---|
| 453 | { TBGRACustomTypeWriter }
|
|---|
| 454 |
|
|---|
| 455 | function TBGRACustomTypeWriter.GetGlyph(AIdentifier: string): TBGRAGlyph;
|
|---|
| 456 | var Node: TAvgLvlTreeNode;
|
|---|
| 457 | begin
|
|---|
| 458 | Node := FindGlyph(AIdentifier);
|
|---|
| 459 | if Node = nil then
|
|---|
| 460 | result := nil
|
|---|
| 461 | else
|
|---|
| 462 | result := TBGRAGlyph(Node.Data);
|
|---|
| 463 | end;
|
|---|
| 464 |
|
|---|
| 465 | procedure TBGRACustomTypeWriter.SetGlyph(AIdentifier: string; AValue: TBGRAGlyph);
|
|---|
| 466 | var Node: TAvgLvlTreeNode;
|
|---|
| 467 | begin
|
|---|
| 468 | if AValue.Identifier <> AIdentifier then
|
|---|
| 469 | raise exception.Create('Identifier mismatch');
|
|---|
| 470 | Node := FindGlyph(AIdentifier);
|
|---|
| 471 | if Node <> nil then
|
|---|
| 472 | begin
|
|---|
| 473 | if pointer(AValue) <> Node.Data then
|
|---|
| 474 | TBGRAGlyph(Node.Data).Free;
|
|---|
| 475 | Node.Data := AValue;
|
|---|
| 476 | end else
|
|---|
| 477 | FGlyphs.Add(pointer(AValue));
|
|---|
| 478 | end;
|
|---|
| 479 |
|
|---|
| 480 | function TBGRACustomTypeWriter.CompareGlyph(Tree: TAvgLvlTree; Data1, Data2: Pointer): integer;
|
|---|
| 481 | begin
|
|---|
| 482 | result := CompareStr(TBGRAGlyph(Data1).Identifier,TBGRAGlyph(Data2).Identifier);
|
|---|
| 483 | end;
|
|---|
| 484 |
|
|---|
| 485 | function TBGRACustomTypeWriter.FindGlyph(AIdentifier: string): TAvgLvlTreeNode;
|
|---|
| 486 | var Comp: integer;
|
|---|
| 487 | Node: TAvgLvlTreeNode;
|
|---|
| 488 | begin
|
|---|
| 489 | Node:=FGlyphs.Root;
|
|---|
| 490 | while (Node<>nil) do begin
|
|---|
| 491 | Comp:=CompareStr(AIdentifier,TBGRAGlyph(Node.Data).Identifier);
|
|---|
| 492 | if Comp=0 then break;
|
|---|
| 493 | if Comp<0 then begin
|
|---|
| 494 | Node:=Node.Left
|
|---|
| 495 | end else begin
|
|---|
| 496 | Node:=Node.Right
|
|---|
| 497 | end;
|
|---|
| 498 | end;
|
|---|
| 499 | result := Node;
|
|---|
| 500 | end;
|
|---|
| 501 |
|
|---|
| 502 | constructor TBGRACustomTypeWriter.Create;
|
|---|
| 503 | begin
|
|---|
| 504 | FGlyphs := TAvgLvlTree.CreateObjectCompare(@CompareGlyph);
|
|---|
| 505 | TypeWriterMatrix := AffineMatrixIdentity;
|
|---|
| 506 | OutlineMode:= twoFill;
|
|---|
| 507 | DrawGlyphsSimultaneously := false;
|
|---|
| 508 | end;
|
|---|
| 509 |
|
|---|
| 510 | procedure TBGRACustomTypeWriter.DrawGlyph(ADest: TBGRACanvas2D;
|
|---|
| 511 | AIdentifier: string; X, Y: Single; AAlign: TBGRATypeWriterAlignment);
|
|---|
| 512 | begin
|
|---|
| 513 | GlyphPath(ADest, AIdentifier, X,Y, AAlign);
|
|---|
| 514 | DrawLastPath(ADest);
|
|---|
| 515 | end;
|
|---|
| 516 |
|
|---|
| 517 | procedure TBGRACustomTypeWriter.DrawText(ADest: TBGRACanvas2D; ATextUTF8: string;
|
|---|
| 518 | X, Y: Single; AAlign: TBGRATypeWriterAlignment);
|
|---|
| 519 | begin
|
|---|
| 520 | TextPath(ADest, ATextUTF8, X,Y, AAlign, (OutlineMode <> twoPath) and not DrawGlyphsSimultaneously);
|
|---|
| 521 | end;
|
|---|
| 522 |
|
|---|
| 523 | procedure TBGRACustomTypeWriter.CopyTextPathTo(ADest: IBGRAPath; ATextUTF8: string; X,Y: Single; AAlign: TBGRATypeWriterAlignment = twaTopLeft);
|
|---|
| 524 | var
|
|---|
| 525 | pstr: pchar;
|
|---|
| 526 | left,charlen: integer;
|
|---|
| 527 | nextchar: string;
|
|---|
| 528 | g: TBGRAGlyph;
|
|---|
| 529 | m,m2: TAffineMatrix;
|
|---|
| 530 | begin
|
|---|
| 531 | if ATextUTF8 = '' then exit;
|
|---|
| 532 | m := GetTextMatrix(ATextUTF8, X,Y,AAlign);
|
|---|
| 533 | m2 := m;
|
|---|
| 534 |
|
|---|
| 535 | pstr := @ATextUTF8[1];
|
|---|
| 536 | left := length(ATextUTF8);
|
|---|
| 537 | while left > 0 do
|
|---|
| 538 | begin
|
|---|
| 539 | charlen := UTF8CharacterLength(pstr);
|
|---|
| 540 | setlength(nextchar, charlen);
|
|---|
| 541 | move(pstr^, nextchar[1], charlen);
|
|---|
| 542 | inc(pstr,charlen);
|
|---|
| 543 | dec(left,charlen);
|
|---|
| 544 |
|
|---|
| 545 | g := GetGlyph(nextchar);
|
|---|
| 546 | if g <> nil then
|
|---|
| 547 | begin
|
|---|
| 548 | if AAlign in [twaLeft,twaMiddle,twaRight] then
|
|---|
| 549 | m2 := m*AffineMatrixTranslation(0,-g.Height/2) else
|
|---|
| 550 | if AAlign in [twaBottomLeft,twaBottom,twaBottomRight] then
|
|---|
| 551 | m2 := m*AffineMatrixTranslation(0,-g.Height)
|
|---|
| 552 | else
|
|---|
| 553 | m2 := m;
|
|---|
| 554 | g.Path(ADest, m2);
|
|---|
| 555 | m := m*AffineMatrixTranslation(g.Width,0);
|
|---|
| 556 | end;
|
|---|
| 557 | end;
|
|---|
| 558 | end;
|
|---|
| 559 |
|
|---|
| 560 | function TBGRACustomTypeWriter.GetGlyphBox(AIdentifier: string; X, Y: Single;
|
|---|
| 561 | AAlign: TBGRATypeWriterAlignment): TAffineBox;
|
|---|
| 562 | var g: TBGRAGlyph;
|
|---|
| 563 | m: TAffineMatrix;
|
|---|
| 564 | begin
|
|---|
| 565 | g := GetGlyph(AIdentifier);
|
|---|
| 566 | if g = nil then result := TAffineBox.EmptyBox else
|
|---|
| 567 | begin
|
|---|
| 568 | m := GetGlyphMatrix(g,X,Y,AAlign);
|
|---|
| 569 | result := TAffineBox.AffineBox(m*PointF(0,0),m*PointF(g.Width,0),m*PointF(0,g.Height));
|
|---|
| 570 | end;
|
|---|
| 571 | end;
|
|---|
| 572 |
|
|---|
| 573 | function TBGRACustomTypeWriter.GetTextBox(ATextUTF8: string; X, Y: Single;
|
|---|
| 574 | AAlign: TBGRATypeWriterAlignment): TAffineBox;
|
|---|
| 575 | var
|
|---|
| 576 | m: TAffineMatrix;
|
|---|
| 577 | totalWidth,minY,maxY,gMinY,gMaxY: single;
|
|---|
| 578 |
|
|---|
| 579 | pstr: pchar;
|
|---|
| 580 | left,charlen: integer;
|
|---|
| 581 | nextchar: string;
|
|---|
| 582 | g: TBGRAGlyph;
|
|---|
| 583 |
|
|---|
| 584 | begin
|
|---|
| 585 | if ATextUTF8 = '' then result := TAffineBox.EmptyBox else
|
|---|
| 586 | begin
|
|---|
| 587 | m := GetTextMatrix(ATextUTF8,X,Y,AAlign);
|
|---|
| 588 | minY := 0;
|
|---|
| 589 | maxY := 0;
|
|---|
| 590 | totalWidth := 0;
|
|---|
| 591 |
|
|---|
| 592 | pstr := @ATextUTF8[1];
|
|---|
| 593 | left := length(ATextUTF8);
|
|---|
| 594 | while left > 0 do
|
|---|
| 595 | begin
|
|---|
| 596 | charlen := UTF8CharacterLength(pstr);
|
|---|
| 597 | setlength(nextchar, charlen);
|
|---|
| 598 | move(pstr^, nextchar[1], charlen);
|
|---|
| 599 | inc(pstr,charlen);
|
|---|
| 600 | dec(left,charlen);
|
|---|
| 601 |
|
|---|
| 602 | g := GetGlyph(nextchar);
|
|---|
| 603 | if g <> nil then
|
|---|
| 604 | begin
|
|---|
| 605 | totalWidth += g.Width;
|
|---|
| 606 |
|
|---|
| 607 | if AAlign in [twaLeft,twaMiddle,twaRight] then
|
|---|
| 608 | begin
|
|---|
| 609 | gMinY := -g.Height/2;
|
|---|
| 610 | gMaxY := g.Height/2;
|
|---|
| 611 | end else
|
|---|
| 612 | if AAlign in [twaBottomLeft,twaBottom,twaBottomRight] then
|
|---|
| 613 | begin
|
|---|
| 614 | gMinY := -g.Height;
|
|---|
| 615 | gMaxY := 0;
|
|---|
| 616 | end
|
|---|
| 617 | else
|
|---|
| 618 | begin
|
|---|
| 619 | gMinY := 0;
|
|---|
| 620 | gMaxY := g.Height;
|
|---|
| 621 | end;
|
|---|
| 622 | if gMinY < minY then minY := gMinY;
|
|---|
| 623 | if gMaxY > maxY then maxY := gMaxY;
|
|---|
| 624 | end;
|
|---|
| 625 | end;
|
|---|
| 626 |
|
|---|
| 627 | result := TAffineBox.AffineBox(m*PointF(0,minY),m*PointF(totalWidth,minY),m*PointF(0,maxY));
|
|---|
| 628 | end;
|
|---|
| 629 | end;
|
|---|
| 630 |
|
|---|
| 631 | function TBGRACustomTypeWriter.GetTextGlyphBoxes(ATextUTF8: string; X, Y: Single;
|
|---|
| 632 | AAlign: TBGRATypeWriterAlignment): TGlyphBoxes;
|
|---|
| 633 | var
|
|---|
| 634 | m: TAffineMatrix;
|
|---|
| 635 | gMinY,gMaxY: single;
|
|---|
| 636 |
|
|---|
| 637 | pstr: pchar;
|
|---|
| 638 | left,charlen: integer;
|
|---|
| 639 | nextchar: string;
|
|---|
| 640 | g: TBGRAGlyph;
|
|---|
| 641 | numChar: integer;
|
|---|
| 642 |
|
|---|
| 643 | begin
|
|---|
| 644 | if ATextUTF8 = '' then result := nil else
|
|---|
| 645 | begin
|
|---|
| 646 | setlength(result, UTF8Length(ATextUTF8));
|
|---|
| 647 |
|
|---|
| 648 | m := GetTextMatrix(ATextUTF8,X,Y,AAlign);
|
|---|
| 649 |
|
|---|
| 650 | pstr := @ATextUTF8[1];
|
|---|
| 651 | left := length(ATextUTF8);
|
|---|
| 652 | numChar := 0;
|
|---|
| 653 | while left > 0 do
|
|---|
| 654 | begin
|
|---|
| 655 | charlen := UTF8CharacterLength(pstr);
|
|---|
| 656 | setlength(nextchar, charlen);
|
|---|
| 657 | move(pstr^, nextchar[1], charlen);
|
|---|
| 658 | inc(pstr,charlen);
|
|---|
| 659 | dec(left,charlen);
|
|---|
| 660 |
|
|---|
| 661 | result[numChar].Glyph := nextchar;
|
|---|
| 662 | g := GetGlyph(nextchar);
|
|---|
| 663 | if g <> nil then
|
|---|
| 664 | begin
|
|---|
| 665 | if AAlign in [twaLeft,twaMiddle,twaRight] then
|
|---|
| 666 | begin
|
|---|
| 667 | gMinY := -g.Height/2;
|
|---|
| 668 | gMaxY := g.Height/2;
|
|---|
| 669 | end else
|
|---|
| 670 | if AAlign in [twaBottomLeft,twaBottom,twaBottomRight] then
|
|---|
| 671 | begin
|
|---|
| 672 | gMinY := -g.Height;
|
|---|
| 673 | gMaxY := 0;
|
|---|
| 674 | end
|
|---|
| 675 | else
|
|---|
| 676 | begin
|
|---|
| 677 | gMinY := 0;
|
|---|
| 678 | gMaxY := g.Height;
|
|---|
| 679 | end;
|
|---|
| 680 | result[numChar].Box := TAffineBox.AffineBox(m*PointF(0,gMinY),m*PointF(g.Width,gMinY),m*PointF(0,gMaxY));
|
|---|
| 681 | m := m*AffineMatrixTranslation(g.Width,0);
|
|---|
| 682 | end else
|
|---|
| 683 | result[numChar].Box := TAffineBox.EmptyBox;
|
|---|
| 684 |
|
|---|
| 685 | inc(numChar);
|
|---|
| 686 | end;
|
|---|
| 687 | end;
|
|---|
| 688 | end;
|
|---|
| 689 |
|
|---|
| 690 | procedure TBGRACustomTypeWriter.NeedGlyphRange(AUnicodeFrom, AUnicodeTo: Cardinal);
|
|---|
| 691 | var c: cardinal;
|
|---|
| 692 | begin
|
|---|
| 693 | for c := AUnicodeFrom to AUnicodeTo do
|
|---|
| 694 | GetGlyph(UnicodeCharToUTF8(c));
|
|---|
| 695 | end;
|
|---|
| 696 |
|
|---|
| 697 | procedure TBGRACustomTypeWriter.NeedGlyphAnsiRange;
|
|---|
| 698 | var i: integer;
|
|---|
| 699 | begin
|
|---|
| 700 | for i := 0 to 255 do
|
|---|
| 701 | GetGlyph(AnsiToUtf8(chr(i)));
|
|---|
| 702 | end;
|
|---|
| 703 |
|
|---|
| 704 | procedure TBGRACustomTypeWriter.TextPath(ADest: TBGRACanvas2D; ATextUTF8: string; X,
|
|---|
| 705 | Y: Single; AAlign: TBGRATypeWriterAlignment; ADrawEachChar: boolean);
|
|---|
| 706 | var
|
|---|
| 707 | pstr: pchar;
|
|---|
| 708 | left,charlen: integer;
|
|---|
| 709 | nextchar: string;
|
|---|
| 710 | g: TBGRAGlyph;
|
|---|
| 711 | m,m2: TAffineMatrix;
|
|---|
| 712 | begin
|
|---|
| 713 | if not ADrawEachChar then ADest.beginPath;
|
|---|
| 714 | if ATextUTF8 = '' then exit;
|
|---|
| 715 | m := GetTextMatrix(ATextUTF8, X,Y,AAlign);
|
|---|
| 716 | m2 := m;
|
|---|
| 717 |
|
|---|
| 718 | pstr := @ATextUTF8[1];
|
|---|
| 719 | left := length(ATextUTF8);
|
|---|
| 720 | while left > 0 do
|
|---|
| 721 | begin
|
|---|
| 722 | charlen := UTF8CharacterLength(pstr);
|
|---|
| 723 | setlength(nextchar, charlen);
|
|---|
| 724 | move(pstr^, nextchar[1], charlen);
|
|---|
| 725 | inc(pstr,charlen);
|
|---|
| 726 | dec(left,charlen);
|
|---|
| 727 |
|
|---|
| 728 | g := GetGlyph(nextchar);
|
|---|
| 729 | if g <> nil then
|
|---|
| 730 | begin
|
|---|
| 731 | if AAlign in [twaLeft,twaMiddle,twaRight] then
|
|---|
| 732 | m2 := m*AffineMatrixTranslation(0,-g.Height/2) else
|
|---|
| 733 | if AAlign in [twaBottomLeft,twaBottom,twaBottomRight] then
|
|---|
| 734 | m2 := m*AffineMatrixTranslation(0,-g.Height)
|
|---|
| 735 | else
|
|---|
| 736 | m2 := m;
|
|---|
| 737 | if ADrawEachChar then ADest.beginPath;
|
|---|
| 738 | g.Path(ADest, m2);
|
|---|
| 739 | if ADrawEachChar then DrawLastPath(ADest);
|
|---|
| 740 | m := m*AffineMatrixTranslation(g.Width,0);
|
|---|
| 741 | end;
|
|---|
| 742 | end;
|
|---|
| 743 | end;
|
|---|
| 744 |
|
|---|
| 745 | procedure TBGRACustomTypeWriter.GlyphPath(ADest: TBGRACanvas2D; AIdentifier: string;
|
|---|
| 746 | X, Y: Single; AAlign: TBGRATypeWriterAlignment);
|
|---|
| 747 | var g: TBGRAGlyph;
|
|---|
| 748 | begin
|
|---|
| 749 | ADest.beginPath;
|
|---|
| 750 | g := GetGlyph(AIdentifier);
|
|---|
| 751 | if g = nil then exit;
|
|---|
| 752 | g.Path(ADest, GetGlyphMatrix(g,X,Y,AAlign));
|
|---|
| 753 | end;
|
|---|
| 754 |
|
|---|
| 755 | procedure TBGRACustomTypeWriter.DrawLastPath(ADest: TBGRACanvas2D);
|
|---|
| 756 | begin
|
|---|
| 757 | case OutlineMode of
|
|---|
| 758 | twoPath: ;
|
|---|
| 759 | twoFill: ADest.fill;
|
|---|
| 760 | twoStroke: ADest.stroke;
|
|---|
| 761 | twoFillOverStroke: ADest.fillOverStroke;
|
|---|
| 762 | twoStrokeOverFill: ADest.strokeOverFill;
|
|---|
| 763 | twoFillThenStroke: begin ADest.fill; ADest.stroke; end;
|
|---|
| 764 | twoStrokeThenFill: begin ADest.stroke; ADest.fill; end;
|
|---|
| 765 | end;
|
|---|
| 766 | end;
|
|---|
| 767 |
|
|---|
| 768 | procedure TBGRACustomTypeWriter.ClearGlyphs;
|
|---|
| 769 | begin
|
|---|
| 770 | FGlyphs.FreeAndClear;
|
|---|
| 771 | end;
|
|---|
| 772 |
|
|---|
| 773 | procedure TBGRACustomTypeWriter.RemoveGlyph(AIdentifier: string);
|
|---|
| 774 | var Node: TAvgLvlTreeNode;
|
|---|
| 775 | begin
|
|---|
| 776 | Node := FindGlyph(AIdentifier);
|
|---|
| 777 | if Node <> nil then FGlyphs.Delete(Node);
|
|---|
| 778 | end;
|
|---|
| 779 |
|
|---|
| 780 | procedure TBGRACustomTypeWriter.AddGlyph(AGlyph: TBGRAGlyph);
|
|---|
| 781 | begin
|
|---|
| 782 | Glyph[AGlyph.Identifier] := AGlyph;
|
|---|
| 783 | end;
|
|---|
| 784 |
|
|---|
| 785 | procedure TBGRACustomTypeWriter.SaveGlyphsToStream(AStream: TStream);
|
|---|
| 786 | var Enumerator: TAvgLvlTreeNodeEnumerator;
|
|---|
| 787 | begin
|
|---|
| 788 | LEWriteLongint(AStream,CustomHeaderSize);
|
|---|
| 789 | WriteCustomHeader(AStream);
|
|---|
| 790 |
|
|---|
| 791 | Enumerator := FGlyphs.GetEnumerator;
|
|---|
| 792 | while Enumerator.MoveNext do
|
|---|
| 793 | TBGRAGlyph(Enumerator.Current.Data).SaveToStream(AStream);
|
|---|
| 794 | Enumerator.Free;
|
|---|
| 795 | end;
|
|---|
| 796 |
|
|---|
| 797 | procedure TBGRACustomTypeWriter.LoadGlyphsFromFile(AFilenameUTF8: string);
|
|---|
| 798 | var Stream: TFileStreamUTF8;
|
|---|
| 799 | begin
|
|---|
| 800 | Stream := nil;
|
|---|
| 801 | try
|
|---|
| 802 | Stream := TFileStreamUTF8.Create(AFilenameUTF8, fmOpenRead);
|
|---|
| 803 | LoadGlyphsFromStream(Stream);
|
|---|
| 804 | finally
|
|---|
| 805 | Stream.Free;
|
|---|
| 806 | end;
|
|---|
| 807 | end;
|
|---|
| 808 |
|
|---|
| 809 | procedure TBGRACustomTypeWriter.LoadGlyphsFromStream(AStream: TStream);
|
|---|
| 810 | var Header: TBGRACustomTypeWriterHeader;
|
|---|
| 811 | i: integer;
|
|---|
| 812 | g: TBGRAGlyph;
|
|---|
| 813 | HeaderSize: integer;
|
|---|
| 814 | GlyphStartPosition: Int64;
|
|---|
| 815 | begin
|
|---|
| 816 | HeaderSize := LEReadLongint(AStream);
|
|---|
| 817 | GlyphStartPosition:= AStream.Position+HeaderSize;
|
|---|
| 818 | Header := ReadCustomTypeWriterHeader(AStream);
|
|---|
| 819 | if header.HeaderName <> HeaderName then
|
|---|
| 820 | raise exception.Create('Invalid file format ("'+header.HeaderName+'" should be "'+HeaderName+'")');
|
|---|
| 821 | ReadAdditionalHeader(AStream);
|
|---|
| 822 | AStream.Position:= GlyphStartPosition;
|
|---|
| 823 | for i := 0 to Header.NbGlyphs-1 do
|
|---|
| 824 | begin
|
|---|
| 825 | g := TBGRAGlyph.LoadFromStream(AStream);
|
|---|
| 826 | AddGlyph(g);
|
|---|
| 827 | end;
|
|---|
| 828 | end;
|
|---|
| 829 |
|
|---|
| 830 | procedure TBGRACustomTypeWriter.SaveGlyphsToFile(AFilenameUTF8: string);
|
|---|
| 831 | var Stream: TFileStreamUTF8;
|
|---|
| 832 | begin
|
|---|
| 833 | Stream := nil;
|
|---|
| 834 | try
|
|---|
| 835 | Stream := TFileStreamUTF8.Create(AFilenameUTF8, fmCreate or fmOpenWrite);
|
|---|
| 836 | SaveGlyphsToStream(Stream);
|
|---|
| 837 | finally
|
|---|
| 838 | Stream.Free;
|
|---|
| 839 | end;
|
|---|
| 840 | end;
|
|---|
| 841 |
|
|---|
| 842 | function TBGRACustomTypeWriter.GetGlyphMatrix(AGlyph: TBGRAGlyph; X, Y: Single;
|
|---|
| 843 | AAlign: TBGRATypeWriterAlignment): TAffineMatrix;
|
|---|
| 844 | var tGlyph: TPointF;
|
|---|
| 845 | begin
|
|---|
| 846 | if AGlyph = nil then
|
|---|
| 847 | begin
|
|---|
| 848 | result := AffineMatrixIdentity;
|
|---|
| 849 | exit;
|
|---|
| 850 | end;
|
|---|
| 851 | tGlyph := PointF(0,0);
|
|---|
| 852 | if AAlign in [twaTop,twaMiddle,twaBottom] then tGlyph.X -= AGlyph.Width/2;
|
|---|
| 853 | if AAlign in [twaTopRight,twaRight,twaBottomRight] then tGlyph.X -= AGlyph.Width;
|
|---|
| 854 | if AAlign in [twaLeft,twaMiddle,twaRight] then tGlyph.Y -= AGlyph.Height/2;
|
|---|
| 855 | if AAlign in [twaBottomLeft,twaBottom,twaBottomRight] then tGlyph.Y -= AGlyph.Height;
|
|---|
| 856 | result := AffineMatrixTranslation(X,Y)*TypeWriterMatrix*AffineMatrixTranslation(tGlyph.X,tGlyph.Y);
|
|---|
| 857 | end;
|
|---|
| 858 |
|
|---|
| 859 | function TBGRACustomTypeWriter.GetTextMatrix(ATextUTF8: string; X, Y: Single;
|
|---|
| 860 | AAlign: TBGRATypeWriterAlignment): TAffineMatrix;
|
|---|
| 861 | var
|
|---|
| 862 | tGlyph: TPointF;
|
|---|
| 863 | totalWidth: single;
|
|---|
| 864 | pstr: pchar;
|
|---|
| 865 | left,charlen: integer;
|
|---|
| 866 | nextchar: string;
|
|---|
| 867 | g: TBGRAGlyph;
|
|---|
| 868 | begin
|
|---|
| 869 | tGlyph := PointF(0,0);
|
|---|
| 870 | if not (AAlign in [twaLeft,twaTopLeft,twaBottomLeft]) then
|
|---|
| 871 | begin
|
|---|
| 872 | totalWidth := 0;
|
|---|
| 873 | pstr := @ATextUTF8[1];
|
|---|
| 874 | left := length(ATextUTF8);
|
|---|
| 875 | while left > 0 do
|
|---|
| 876 | begin
|
|---|
| 877 | charlen := UTF8CharacterLength(pstr);
|
|---|
| 878 | setlength(nextchar, charlen);
|
|---|
| 879 | move(pstr^, nextchar[1], charlen);
|
|---|
| 880 | inc(pstr,charlen);
|
|---|
| 881 | dec(left,charlen);
|
|---|
| 882 |
|
|---|
| 883 | g := GetGlyph(nextchar);
|
|---|
| 884 | if g <> nil then totalWidth += g.Width;
|
|---|
| 885 | end;
|
|---|
| 886 |
|
|---|
| 887 | if AAlign in[twaTop,twaMiddle,twaBottom] then tGlyph.X -= totalWidth/2 else
|
|---|
| 888 | if AAlign in[twaTopRight, twaRight, twaBottomRight] then tGlyph.X -= totalWidth;
|
|---|
| 889 | end;
|
|---|
| 890 | result := AffineMatrixTranslation(X,Y)*TypeWriterMatrix*AffineMatrixTranslation(tGlyph.X,tGlyph.Y);
|
|---|
| 891 | end;
|
|---|
| 892 |
|
|---|
| 893 | function TBGRACustomTypeWriter.CustomHeaderSize: integer;
|
|---|
| 894 | begin
|
|---|
| 895 | result := 1+length(HeaderName)+4;
|
|---|
| 896 | end;
|
|---|
| 897 |
|
|---|
| 898 | procedure TBGRACustomTypeWriter.WriteCustomHeader(AStream: TStream);
|
|---|
| 899 | var lHeaderName: string;
|
|---|
| 900 | begin
|
|---|
| 901 | lHeaderName:= HeaderName;
|
|---|
| 902 | LEWriteByte(AStream,length(lHeaderName));
|
|---|
| 903 | AStream.Write(lHeaderName[1],length(lHeaderName));
|
|---|
| 904 | LEWriteLongint(AStream,FGlyphs.Count);
|
|---|
| 905 | end;
|
|---|
| 906 |
|
|---|
| 907 | function TBGRACustomTypeWriter.ReadCustomTypeWriterHeader(AStream: TStream
|
|---|
| 908 | ): TBGRACustomTypeWriterHeader;
|
|---|
| 909 | begin
|
|---|
| 910 | setlength(result.HeaderName, LEReadByte(AStream));
|
|---|
| 911 | AStream.Read(result.HeaderName[1],length(result.HeaderName));
|
|---|
| 912 | result.NbGlyphs:= LEReadLongint(AStream);
|
|---|
| 913 | end;
|
|---|
| 914 |
|
|---|
| 915 | procedure TBGRACustomTypeWriter.ReadAdditionalHeader(AStream: TStream);
|
|---|
| 916 | begin
|
|---|
| 917 | //nothing
|
|---|
| 918 | end;
|
|---|
| 919 |
|
|---|
| 920 | function TBGRACustomTypeWriter.HeaderName: string;
|
|---|
| 921 | begin
|
|---|
| 922 | result := 'TBGRACustomTypeWriter';
|
|---|
| 923 | end;
|
|---|
| 924 |
|
|---|
| 925 | destructor TBGRACustomTypeWriter.Destroy;
|
|---|
| 926 | begin
|
|---|
| 927 | FGlyphs.FreeAndClear;
|
|---|
| 928 | FGlyphs.Free;
|
|---|
| 929 | inherited Destroy;
|
|---|
| 930 | end;
|
|---|
| 931 |
|
|---|
| 932 | end.
|
|---|
| 933 |
|
|---|