Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

904 wiersze
29 KiB

  1. unit utsFontCreatorGDI;
  2. {$IFDEF FPC}
  3. {$mode objfpc}{$H+}
  4. {$ENDIF}
  5. interface
  6. uses
  7. Classes, SysUtils,
  8. utsFont, utsFontCreator, utsTypes, utsGDI, utsImage, utsContext;
  9. type
  10. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  11. TtsFontGDI = class(TtsFont)
  12. private
  13. fHandle: THandle;
  14. fMat2: TMat2;
  15. function GetGlyphIndex(const aCharCode: WideChar): Integer;
  16. procedure GetCharImageAANone(const aDC: HDC; const aCharCode: WideChar; const aImage: TtsImage; const aFormat: TtsFormat);
  17. procedure GetCharImageAANormal(const aDC: HDC; const aCharCode: WideChar; const aImage: TtsImage; const aFormat: TtsFormat);
  18. protected
  19. {%H-}constructor Create(const aHandle: THandle; const aCreator: TtsFontCreator; const aMetric: TtsFontMetric);
  20. public
  21. procedure GetCharImage(const aCharCode: WideChar; const aCharImage: TtsImage; const aFormat: TtsFormat); override;
  22. function GetGlyphMetrics(const aCharCode: WideChar; out aGlyphOrigin, aGlyphSize: TtsPosition; out aAdvance: Integer): Boolean; override;
  23. destructor Destroy; override;
  24. end;
  25. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  26. TtsFontRegistration = class(TObject)
  27. protected
  28. fIsRegistered: Boolean;
  29. fFontname: String;
  30. procedure UnregisterFont; virtual; abstract;
  31. public
  32. property IsRegistered: Boolean read fIsRegistered;
  33. property Fontname: String read fFontname;
  34. destructor Destroy; override;
  35. end;
  36. TtsFontRegistrationFile = class(TtsFontRegistration)
  37. private
  38. fFilename: String;
  39. protected
  40. procedure UnregisterFont; override;
  41. public
  42. constructor Create(const aFilename: String);
  43. end;
  44. TtsFontRegistrationStream = class(TtsFontRegistration)
  45. private
  46. fHandle: THandle;
  47. protected
  48. procedure UnregisterFont; override;
  49. public
  50. constructor Create(const aStream: TStream);
  51. end;
  52. TtsRegistredFontGDI = class(TtsFontGDI)
  53. private
  54. fRegistration: TtsFontRegistration;
  55. protected
  56. {%H-}constructor Create(const aRegistration: TtsFontRegistration; const aHandle: THandle; const aCreator: TtsFontCreator; const aMetric: TtsFontMetric);
  57. public
  58. destructor Destroy; override;
  59. end;
  60. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  61. TtsFontCreatorGDI = class(TtsFontCreator)
  62. private
  63. function CreateFont(const aFontname: String; const aSize: Integer; const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing; out aMetric: TtsFontMetric): THandle;
  64. public
  65. function GetFontByName(const aFontname: String; const aSize: Integer; const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont; overload;
  66. function GetFontByFile(const aFilename: String; const aSize: Integer; const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont; overload;
  67. function GetFontByStream(const aStream: TStream; const aSize: Integer; const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont; overload;
  68. constructor Create(const aContext: TtsContext);
  69. destructor Destroy; override;
  70. end;
  71. implementation
  72. uses
  73. Math,
  74. utsUtils;
  75. type
  76. TT_OFFSET_TABLE = packed record
  77. uMajorVersion: Word;
  78. uMinorVersion: Word;
  79. uNumOfTables: Word;
  80. uSearchRange: Word;
  81. uEntrySelector: Word;
  82. uRangeShift: Word;
  83. end;
  84. TT_TABLE_DIRECTORY = packed record
  85. TableName: Cardinal; // table name
  86. uCheckSum: Cardinal; // Check sum
  87. uOffset: Cardinal; // Offset from beginning of file
  88. uLength: Cardinal; // length of the table in bytes
  89. end;
  90. TT_NAME_TABLE_HEADER = packed record
  91. uFSelector: Word; //format selector. Always 0
  92. uNRCount: Word; //Name Records count
  93. uStorageOffset: Word; //Offset for strings storage, from start of the table
  94. end;
  95. TT_NAME_RECORD = packed record
  96. uPlatformID: Word;
  97. uEncodingID: Word;
  98. uLanguageID: Word;
  99. uNameID: Word;
  100. uStringLength: Word;
  101. uStringOffset: Word; //from start of storage area
  102. end;
  103. const
  104. NAME_ID_COPYRIGHT = 0;
  105. NAME_ID_FACE_NAME = 1;
  106. NAME_ID_STYLE_NAME = 2;
  107. NAME_ID_FULL_NAME = 4;
  108. PLATFORM_ID_APPLE_UNICODE = 0;
  109. PLATFORM_ID_MACINTOSH = 1;
  110. PLATFORM_ID_MICROSOFT = 3;
  111. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  112. //TTF Utils/////////////////////////////////////////////////////////////////////////////////////////////////////////////
  113. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  114. function SWAPWORD(x: Word): Word;
  115. begin
  116. Result := x and $FF;
  117. Result := Result shl 8;
  118. Result := Result or (x shr 8);
  119. end;
  120. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  121. function SWAPLONG(x: Cardinal): Cardinal;
  122. begin
  123. Result := (x and $FF) shl 24;
  124. x := x shr 8;
  125. Result := Result or ((x and $FF) shl 16);
  126. x := x shr 8;
  127. Result := Result or ((x and $FF) shl 8);
  128. x := x shr 8;
  129. Result := Result or x;
  130. end;
  131. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  132. function GetTTTableData(Stream: TStream; TableName: Cardinal; pBuff: Pointer; var Size: Integer): Boolean;
  133. var
  134. Pos: Int64;
  135. OffsetTable: TT_OFFSET_TABLE;
  136. TableDir: TT_TABLE_DIRECTORY;
  137. Idx: Integer;
  138. begin
  139. Result := False;
  140. Pos := Stream.Position;
  141. // Reading table header
  142. Stream.Read(OffsetTable{%H-}, sizeof(TT_OFFSET_TABLE));
  143. OffsetTable.uNumOfTables := SWAPWORD(OffsetTable.uNumOfTables);
  144. OffsetTable.uMajorVersion := SWAPWORD(OffsetTable.uMajorVersion);
  145. OffsetTable.uMinorVersion := SWAPWORD(OffsetTable.uMinorVersion);
  146. //check is this is a true type font and the version is 1.0
  147. if (OffsetTable.uMajorVersion <> 1) or (OffsetTable.uMinorVersion <> 0) then
  148. Exit;
  149. // seaching table with name
  150. for Idx := 0 to OffsetTable.uNumOfTables -1 do begin
  151. Stream.Read(TableDir{%H-}, sizeof(TT_TABLE_DIRECTORY));
  152. if (TableName = TableDir.TableName) then begin
  153. TableDir.uOffset := SWAPLONG(TableDir.uOffset);
  154. TableDir.uLength := SWAPLONG(TableDir.uLength);
  155. // copying tabledata
  156. if (pBuff <> nil) and (Size >= Integer(TableDir.uLength)) then begin
  157. Stream.Seek(TableDir.uOffset, soBeginning);
  158. Size := Stream.Read(pBuff^, TableDir.uLength);
  159. Result := (Size = Integer(TableDir.uLength));
  160. end else
  161. begin
  162. // restoring streamposition
  163. Stream.Position := Pos;
  164. Size := TableDir.uLength;
  165. Result := True;
  166. end;
  167. break;
  168. end;
  169. end;
  170. end;
  171. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  172. function MakeTTTableName(const ch1, ch2, ch3, ch4: Char): Cardinal;
  173. begin
  174. Result := ord(ch4) shl 24 or ord(ch3) shl 16 or ord(ch2) shl 8 or ord(ch1);
  175. end;
  176. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  177. function GetTTString(pBuffer: Pointer; BufferSize: Integer; NameID, LanguageID: Cardinal; out Text: String): Boolean;
  178. var
  179. pActBuffer: pByte;
  180. ttNTHeader: TT_NAME_TABLE_HEADER;
  181. ttRecord: TT_NAME_RECORD;
  182. Idx: Integer;
  183. Prio: Integer;
  184. procedure ExtractName;
  185. var
  186. pTempBuffer: pByte;
  187. pTemp: pWideChar;
  188. uStringLengthH2: Word;
  189. procedure SwapText(pText: pWideChar; Length: Word);
  190. begin
  191. while Length > 0 do begin
  192. pWord(pText)^ := SWAPWORD(pWord(pText)^);
  193. Inc(pText);
  194. Dec(Length);
  195. end;
  196. end;
  197. begin
  198. Result := True;
  199. ttRecord.uStringLength := SWAPWORD(ttRecord.uStringLength);
  200. ttRecord.uStringOffset := SWAPWORD(ttRecord.uStringOffset);
  201. uStringLengthH2 := ttRecord.uStringLength shr 1;
  202. pTempBuffer := pBuffer;
  203. Inc(pTempBuffer, ttNTHeader.uStorageOffset + ttRecord.uStringOffset);
  204. // Unicode
  205. if ((ttRecord.uPlatformID = PLATFORM_ID_MICROSOFT) and (ttRecord.uEncodingID in [0, 1])) or
  206. ((ttRecord.uPlatformID = PLATFORM_ID_APPLE_UNICODE) and (ttRecord.uEncodingID > 0)) then begin
  207. pTemp := tsStrAlloc(uStringLengthH2);
  208. try
  209. // uStringLengthH2 * 2 because possible buffer overrun
  210. Move(pTempBuffer^, pTemp^, uStringLengthH2 * 2);
  211. SwapText(pTemp, uStringLengthH2);
  212. WideCharLenToStrVar(pTemp, uStringLengthH2, Text);
  213. finally
  214. tsStrDispose(pTemp);
  215. end;
  216. end else
  217. // none unicode
  218. begin
  219. SetLength(Text, ttRecord.uStringLength);
  220. Move(pTempBuffer^, Text[1], ttRecord.uStringLength);
  221. end;
  222. end;
  223. begin
  224. Result := False;
  225. pActBuffer := pBuffer;
  226. Move(pActBuffer^, ttNTHeader{%H-}, sizeof(TT_NAME_TABLE_HEADER));
  227. inc(pActBuffer, sizeof(TT_NAME_TABLE_HEADER));
  228. ttNTHeader.uNRCount := SWAPWORD(ttNTHeader.uNRCount);
  229. ttNTHeader.uStorageOffset := SWAPWORD(ttNTHeader.uStorageOffset);
  230. Prio := -1;
  231. for Idx := 0 to ttNTHeader.uNRCount -1 do begin
  232. Move(pActBuffer^, ttRecord, sizeof(TT_NAME_RECORD));
  233. Inc(pActBuffer, sizeof(TT_NAME_RECORD));
  234. ttRecord.uNameID := SWAPWORD(ttRecord.uNameID);
  235. if ttRecord.uNameID = NameID then begin
  236. ttRecord.uPlatformID := SWAPWORD(ttRecord.uPlatformID);
  237. ttRecord.uEncodingID := SWAPWORD(ttRecord.uEncodingID);
  238. ttRecord.uLanguageID := SWAPWORD(ttRecord.uLanguageID);
  239. // highest priority
  240. if (ttRecord.uPlatformID = PLATFORM_ID_MICROSOFT) then begin
  241. // system language
  242. if (ttRecord.uLanguageID = languageID) then begin
  243. if Prio <= 7 then begin
  244. ExtractName;
  245. Prio := 7;
  246. end;
  247. end else
  248. // english
  249. if (ttRecord.uLanguageID = 1033) then begin
  250. if Prio <= 6 then begin
  251. ExtractName;
  252. Prio := 6;
  253. end;
  254. end else
  255. // all else
  256. if Prio <= 5 then begin
  257. ExtractName;
  258. Prio := 5;
  259. end;
  260. end else
  261. // apple unicode
  262. if (ttRecord.uPlatformID = PLATFORM_ID_APPLE_UNICODE) then begin
  263. ExtractName;
  264. Prio := 4;
  265. end else
  266. // macintosh
  267. if (ttRecord.uPlatformID = PLATFORM_ID_MACINTOSH) then begin
  268. // english
  269. if (ttRecord.uLanguageID = 0) then begin
  270. if Prio <= 3 then begin
  271. ExtractName;
  272. Prio := 3;
  273. end;
  274. end else
  275. // all other
  276. begin
  277. ExtractName;
  278. Prio := 2;
  279. end;
  280. end else
  281. begin
  282. if Prio <= 1 then begin
  283. ExtractName;
  284. Prio := 1;
  285. end;
  286. end;
  287. end;
  288. end;
  289. end;
  290. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  291. function GetTTFontFullNameFromStream(Stream: TStream; LanguageID: Cardinal): String;
  292. var
  293. TableName: Cardinal;
  294. Buffer: Pointer;
  295. BufferSize: Integer;
  296. begin
  297. TableName := MakeTTTableName('n', 'a', 'm', 'e');
  298. BufferSize := 0;
  299. if GetTTTableData(Stream, TableName, nil, BufferSize) then begin
  300. GetMem(Buffer, BufferSize);
  301. try
  302. if GetTTTableData(Stream, TableName, Buffer, BufferSize) then begin
  303. if not GetTTString(Buffer, BufferSize, NAME_ID_FULL_NAME, LanguageID, Result) then
  304. if not GetTTString(Buffer, BufferSize, NAME_ID_FACE_NAME, LanguageID, Result) then
  305. Result := '';
  306. end;
  307. finally
  308. FreeMem(Buffer);
  309. end;
  310. end;
  311. end;
  312. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  313. function GetTTFontFullNameFromFile(const aFilename: String; const aLanguageID: Cardinal): String;
  314. var
  315. fs: TFileStream;
  316. begin
  317. fs := TFileStream.Create(aFilename, fmOpenRead or fmShareDenyWrite);
  318. try
  319. result := GetTTFontFullNameFromStream(fs, aLanguageID);
  320. finally
  321. fs.Free;
  322. end;
  323. end;
  324. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  325. //TtsFontGDI////////////////////////////////////////////////////////////////////////////////////////////////////////////
  326. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  327. function TtsFontGDI.GetGlyphIndex(const aCharCode: WideChar): Integer;
  328. var
  329. DC: HDC;
  330. GCPRes: TGCPResultsW;
  331. begin
  332. result := -1;
  333. DC := CreateCompatibleDC(0);
  334. try
  335. SelectObject(DC, fHandle);
  336. if Assigned(GetCharacterPlacementW) then begin
  337. FillChar(GCPRes{%H-}, SizeOf(GCPRes), #0);
  338. GetMem(GCPRes.lpGlyphs, SizeOf(Cardinal));
  339. try
  340. GCPRes.lStructSize := SizeOf(GCPRes);
  341. GCPRes.lpGlyphs^ := 0;
  342. GCPRes.nGlyphs := 1;
  343. if (GetCharacterPlacementW(DC, @aCharCode, 1, GCP_MAXEXTENT, @GCPRes, 0) <> GDI_ERROR) and
  344. (GCPRes.nGlyphs = 1) and
  345. (GCPRes.lpGlyphs <> nil) then
  346. begin
  347. result := GCPRes.lpGlyphs^;
  348. end;
  349. finally
  350. FreeMem(GCPRes.lpGlyphs);
  351. end;
  352. end;
  353. finally
  354. DeleteDC(DC);
  355. end;
  356. end;
  357. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  358. procedure TtsFontGDI.GetCharImageAANone(const aDC: HDC; const aCharCode: WideChar; const aImage: TtsImage; const aFormat: TtsFormat);
  359. var
  360. gm: TGlyphMetrics;
  361. GlyphIndex, srcW, srcX, w, h, x, y: Integer;
  362. Size, OutlineRes: Cardinal;
  363. Buffer, pSrc, pDst: PByte;
  364. procedure ExpandByte;
  365. var
  366. i, cnt, srcCnt: Integer;
  367. c: TtsColor4f;
  368. begin
  369. srcCnt := min(8, srcX);
  370. cnt := min(8, x);
  371. for i := 1 to cnt do begin
  372. c := tsColor4f(1, 1, 1, 1);
  373. if ((pSrc^ and $80) > 0) then
  374. c.a := 1.0
  375. else
  376. c.a := 0.0;
  377. pSrc^ := (pSrc^ and not $80) shl 1;
  378. tsFormatMap(aFormat, pDst, c);
  379. end;
  380. dec(srcX, srcCnt);
  381. dec(x, cnt);
  382. inc(pSrc);
  383. end;
  384. begin
  385. if (fMat2.eM11.value <> 1) then
  386. raise EtsException.Create('invalid value');
  387. FillChar(gm{%H-}, SizeOf(gm), #0);
  388. GlyphIndex := GetGlyphIndex(aCharCode);
  389. if (GlyphIndex < 0) then
  390. exit;
  391. Size := GetGlyphOutlineA(aDC, GlyphIndex, GGO_BITMAP or GGO_GLYPH_INDEX, @gm, 0, nil, @fMat2);
  392. if (Size = GDI_ERROR) or (Size = 0) then
  393. exit;
  394. GetMem(Buffer, Size);
  395. try
  396. OutlineRes := GetGlyphOutlineA(aDC, GlyphIndex, GGO_BITMAP or GGO_GLYPH_INDEX, @gm, Size, Buffer, @fMat2);
  397. if (OutlineRes = GDI_ERROR) then
  398. exit;
  399. w := gm.gmBlackBoxX;
  400. h := gm.gmBlackBoxY;
  401. srcW := (Integer(Size) div h) * 8;
  402. if (w <= 0) or (h <= 0) then
  403. exit;
  404. aImage.CreateEmpty(aFormat, w, h);
  405. pSrc := Buffer;
  406. for y := 0 to h-1 do begin
  407. pDst := aImage.Scanline[y];
  408. srcX := srcW;
  409. x := w;
  410. while (srcX > 0) do
  411. ExpandByte;
  412. end;
  413. finally
  414. Freemem(Buffer);
  415. end;
  416. end;
  417. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  418. procedure TtsFontGDI.GetCharImageAANormal(const aDC: HDC; const aCharCode: WideChar; const aImage: TtsImage; const aFormat: TtsFormat);
  419. var
  420. gm: TGlyphMetrics;
  421. OutlineRes: DWORD;
  422. GlyphIndex, tmp, Spacer, x, y, w, h: Integer;
  423. Size: Cardinal;
  424. Buffer, pSrc, pDst: PByte;
  425. procedure CopyPixel;
  426. var
  427. i: Integer;
  428. tmp, cnt: Cardinal;
  429. c: TtsColor4f;
  430. begin
  431. cnt := min(x, fMat2.eM11.value);
  432. tmp := 0;
  433. for i := 0 to cnt-1 do begin
  434. tmp := tmp + pSrc^;
  435. inc(pSrc, 1);
  436. end;
  437. dec(x, cnt);
  438. c := tsColor4f(1, 1, 1, tmp / $40);
  439. tsFormatMap(aFormat, pDst, c);
  440. end;
  441. begin
  442. FillChar(gm{%H-}, SizeOf(gm), #0);
  443. GlyphIndex := GetGlyphIndex(aCharCode);
  444. if (GlyphIndex < 0) then
  445. exit;
  446. Size := GetGlyphOutlineA(aDC, GlyphIndex, GGO_GRAY8_BITMAP or GGO_GLYPH_INDEX, @gm, 0, nil, @fMat2);
  447. if (Size = GDI_ERROR) or (Size = 0) then
  448. exit;
  449. GetMem(Buffer, Size);
  450. try
  451. OutlineRes := GetGlyphOutlineA(aDC, GlyphIndex, GGO_GRAY8_BITMAP or GGO_GLYPH_INDEX, @gm, Size, Buffer, @fMat2);
  452. if (OutlineRes = GDI_ERROR) then
  453. exit;
  454. w := Integer(gm.gmBlackBoxX) div fMat2.eM11.value;
  455. h := gm.gmBlackBoxY;
  456. tmp := Integer(gm.gmBlackBoxX) mod fMat2.eM11.value;
  457. if (tmp <> 0) then
  458. w := w + fMat2.eM11.value - tmp;
  459. if (w <= 0) or (h <= 0) then
  460. exit;
  461. // spacer
  462. Spacer := gm.gmBlackBoxX mod 4;
  463. if (Spacer <> 0) then
  464. Spacer := 4 - Spacer;
  465. // copy image
  466. aImage.CreateEmpty(aFormat, w, h);
  467. pSrc := Buffer;
  468. for y := 0 to h-1 do begin
  469. pDst := aImage.Scanline[y];
  470. x := gm.gmBlackBoxX;
  471. while (x > 0) do
  472. CopyPixel;
  473. inc(pSrc, Spacer);
  474. end;
  475. finally
  476. FreeMem(Buffer);
  477. end;
  478. end;
  479. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  480. constructor TtsFontGDI.Create(const aHandle: THandle; const aCreator: TtsFontCreator; const aMetric: TtsFontMetric);
  481. begin
  482. inherited Create(aCreator, aMetric);
  483. FillChar(fMat2, SizeOf(fMat2), #0);
  484. fMat2.eM11.value := 1;
  485. fMat2.eM22.value := 1;
  486. fHandle := aHandle;
  487. end;
  488. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  489. procedure TtsFontGDI.GetCharImage(const aCharCode: WideChar; const aCharImage: TtsImage; const aFormat: TtsFormat);
  490. var
  491. DC: HDC;
  492. begin
  493. DC := CreateCompatibleDC(0);
  494. try
  495. SelectObject(DC, fHandle);
  496. case Metric.AntiAliasing of
  497. tsAANone:
  498. GetCharImageAANone(DC, aCharCode, aCharImage, aFormat);
  499. tsAANormal:
  500. GetCharImageAANormal(DC, aCharCode, aCharImage, aFormat);
  501. end;
  502. finally
  503. DeleteDC(DC);
  504. end;
  505. end;
  506. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  507. function TtsFontGDI.GetGlyphMetrics(const aCharCode: WideChar; out aGlyphOrigin, aGlyphSize: TtsPosition; out aAdvance: Integer): Boolean;
  508. var
  509. GlyphIndex: Integer;
  510. DC: HDC;
  511. gm: TGlyphMetrics;
  512. Size: Cardinal;
  513. begin
  514. result := false;
  515. aGlyphOrigin.x := 0;
  516. aGlyphOrigin.x := 0;
  517. aGlyphSize.x := 0;
  518. aGlyphSize.y := 0;
  519. aAdvance := 0;
  520. GlyphIndex := GetGlyphIndex(aCharCode);
  521. if (GlyphIndex < 0) then
  522. exit;
  523. DC := CreateCompatibleDC(0);
  524. try
  525. SelectObject(DC, fHandle);
  526. case Metric.AntiAliasing of
  527. tsAANone: begin
  528. Size := GetGlyphOutlineA(DC, GlyphIndex, GGO_BITMAP or GGO_GLYPH_INDEX, @gm, 0, nil, @fMat2);
  529. end;
  530. tsAANormal: begin
  531. Size := GetGlyphOutlineA(DC, GlyphIndex, GGO_GRAY8_BITMAP or GGO_GLYPH_INDEX, @gm, 0, nil, @fMat2);
  532. end;
  533. else
  534. Size := GDI_ERROR;
  535. end;
  536. if (Size = GDI_ERROR) then
  537. Size := GetGlyphOutlineA(DC, GlyphIndex, GGO_METRICS or GGO_GLYPH_INDEX, @gm, 0, nil, @fMat2);
  538. if (Size <> GDI_ERROR) then begin
  539. aGlyphOrigin.x := gm.gmptGlyphOrigin.x;
  540. aGlyphOrigin.y := gm.gmptGlyphOrigin.y;
  541. aGlyphSize.x := gm.gmBlackBoxX;
  542. aGlyphSize.y := gm.gmBlackBoxY;
  543. aAdvance := gm.gmCellIncX;
  544. result := true;
  545. end;
  546. finally
  547. DeleteDC(DC);
  548. end;
  549. end;
  550. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  551. destructor TtsFontGDI.Destroy;
  552. begin
  553. DeleteObject(fHandle);
  554. inherited Destroy;
  555. end;
  556. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  557. //TtsFontRegistration///////////////////////////////////////////////////////////////////////////////////////////////////
  558. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  559. destructor TtsFontRegistration.Destroy;
  560. begin
  561. if fIsRegistered then
  562. UnregisterFont;
  563. inherited Destroy;
  564. end;
  565. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  566. //TtsFontRegistrationFile///////////////////////////////////////////////////////////////////////////////////////////////
  567. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  568. procedure TtsFontRegistrationFile.UnregisterFont;
  569. begin
  570. if Assigned(RemoveFontResourceExA) then
  571. RemoveFontResourceExA(PAnsiChar(AnsiString(fFilename)), 0, nil)
  572. else if Assigned(RemoveFontResourceA) then
  573. RemoveFontResourceA(PAnsiChar(AnsiString(fFilename)));
  574. end;
  575. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  576. constructor TtsFontRegistrationFile.Create(const aFilename: String);
  577. var
  578. lang: AnsiString;
  579. begin
  580. inherited Create;
  581. fFilename := aFilename;
  582. // get Fontname
  583. SetLength(lang, 4);
  584. GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_ILANGUAGE, @lang[1], 4);
  585. fFontname := GetTTFontFullNameFromFile(aFilename, StrToInt('$' + String(lang)));
  586. // register font
  587. if Assigned(AddFontResourceExA) then
  588. fIsRegistered := (AddFontResourceExA(PAnsiChar(AnsiString(fFilename)), 0, nil) > 0)
  589. else if Assigned(AddFontResourceA) then
  590. fIsRegistered := (AddFontResourceA(PAnsiChar(AnsiString(fFilename))) > 0)
  591. else
  592. fIsRegistered := false;
  593. end;
  594. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  595. //TtsFontRegistrationStream/////////////////////////////////////////////////////////////////////////////////////////////
  596. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  597. procedure TtsFontRegistrationStream.UnregisterFont;
  598. begin
  599. if Assigned(RemoveFontMemResourceEx) then
  600. RemoveFontMemResourceEx(fHandle);
  601. end;
  602. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  603. constructor TtsFontRegistrationStream.Create(const aStream: TStream);
  604. var
  605. lang: AnsiString;
  606. ms: TMemoryStream;
  607. cnt: DWORD;
  608. begin
  609. inherited Create;
  610. fHandle := 0;
  611. fIsRegistered := false;
  612. // get Fontname
  613. SetLength(Lang, 4);
  614. GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_ILANGUAGE, @lang[1], 4);
  615. fFontname := GetTTFontFullNameFromStream(aStream, StrToInt('$' + String(Lang)));
  616. // register font
  617. ms := TMemoryStream.Create;
  618. try
  619. ms.CopyFrom(aStream, 0);
  620. if Assigned(AddFontMemResourceEx) then
  621. fHandle := AddFontMemResourceEx(ms.Memory, ms.Size, nil, @cnt);
  622. fIsRegistered := (fHandle > 0);
  623. finally
  624. FreeAndNil(ms);
  625. end;
  626. end;
  627. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  628. //TtsRegistredFontGDI///////////////////////////////////////////////////////////////////////////////////////////////////
  629. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  630. constructor TtsRegistredFontGDI.Create(const aRegistration: TtsFontRegistration; const aHandle: THandle; const aCreator: TtsFontCreator; const aMetric: TtsFontMetric);
  631. begin
  632. inherited Create(aHandle, aCreator, aMetric);
  633. fRegistration := aRegistration;
  634. end;
  635. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  636. destructor TtsRegistredFontGDI.Destroy;
  637. begin
  638. FreeAndNil(fRegistration);
  639. inherited Destroy;
  640. end;
  641. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  642. //TtsFontCreatorGDI/////////////////////////////////////////////////////////////////////////////////////////////////////
  643. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  644. function TtsFontCreatorGDI.CreateFont(const aFontname: String; const aSize: Integer; const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing; out aMetric: TtsFontMetric): THandle;
  645. var
  646. LogFont: TLogFontA;
  647. i: Integer;
  648. DC: HDC;
  649. TableName, BufSize: Cardinal;
  650. Buffer: PByte;
  651. Lang, tmpName: AnsiString;
  652. TextMetric: TTextMetricW;
  653. OutlineMetric: TOutlineTextmetricW;
  654. function _(e: Boolean; a, b: Integer): Integer;
  655. begin
  656. if e then
  657. result := a
  658. else
  659. result := b;
  660. end;
  661. begin
  662. FillChar(aMetric{%H-}, SizeOf(aMetric), #0);
  663. aMetric.Size := aSize;
  664. aMetric.Style := aStyle;
  665. aMetric.AntiAliasing := aAntiAliasing;
  666. aMetric.Fontname := aFontname;
  667. // prepare font attribs
  668. FillChar(LogFont{%H-}, SizeOf(LogFont), #0);
  669. tmpName := AnsiString(aFontname);
  670. for i := 1 to min(Length(aFontname), Length(LogFont.lfFaceName)) do
  671. LogFont.lfFaceName[i-1] := tmpName[i];
  672. LogFont.lfCharSet := DEFAULT_CHARSET;
  673. LogFont.lfHeight := -aSize;
  674. LogFont.lfWeight := _(tsStyleBold in aStyle, FW_BOLD, FW_NORMAL);
  675. LogFont.lfItalic := _(tsStyleItalic in aStyle, 1, 0);
  676. LogFont.lfUnderline := _(tsStyleUnderline in aStyle, 1, 0);
  677. LogFont.lfQuality := _(aAntiAliasing = tsAANormal, ANTIALIASED_QUALITY, NONANTIALIASED_QUALITY);
  678. result := CreateFontIndirectA(LogFont);
  679. DC := CreateCompatibleDC(0);
  680. try try
  681. SelectObject(DC, result);
  682. TableName := MakeTTTableName('n', 'a', 'm', 'e');
  683. BufSize := GetFontData(DC, TableName, 0, nil, 0);
  684. if (BufSize <> GDI_ERROR) then begin
  685. GetMem(Buffer, BufSize);
  686. try
  687. if (GetFontData(DC, TableName, 0, Buffer, BufSize) <> GDI_ERROR) then begin
  688. SetLength(Lang, 4);
  689. GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_ILANGUAGE, @Lang[1], 4);
  690. GetTTString(Buffer, BufSize, NAME_ID_COPYRIGHT, StrToInt('$' + String(Lang)), aMetric.Copyright);
  691. GetTTString(Buffer, BufSize, NAME_ID_FACE_NAME, StrToInt('$' + String(Lang)), aMetric.FaceName);
  692. GetTTString(Buffer, BufSize, NAME_ID_STYLE_NAME, StrToInt('$' + String(Lang)), aMetric.StyleName);
  693. GetTTString(Buffer, BufSize, NAME_ID_FULL_NAME, StrToInt('$' + String(Lang)), aMetric.FullName);
  694. end;
  695. finally
  696. FreeMem(Buffer);
  697. end;
  698. end;
  699. if GetTextMetricsW(DC, TextMetric{%H-}) then begin
  700. aMetric.Ascent := TextMetric.tmAscent;
  701. aMetric.Descent := TextMetric.tmDescent;
  702. aMetric.ExternalLeading := TextMetric.tmExternalLeading;
  703. aMetric.DefaultChar := TextMetric.tmDefaultChar;
  704. end;
  705. if (GetOutlineTextMetricsW(DC, SizeOf(OutlineMetric), OutlineMetric{%H-}) > 0) then begin
  706. aMetric.UnderlinePos := OutlineMetric.otmsUnderscorePosition;
  707. aMetric.UnderlineSize := Min(1, OutlineMetric.otmsUnderscoreSize);
  708. aMetric.StrikeoutPos := OutlineMetric.otmsStrikeoutPosition;
  709. aMetric.StrikeoutSize := Min(1, OutlineMetric.otmsStrikeoutSize);
  710. end;
  711. except
  712. DeleteObject(result);
  713. result := 0;
  714. end;
  715. finally
  716. DeleteDC(DC);
  717. end;
  718. end;
  719. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  720. function TtsFontCreatorGDI.GetFontByName(const aFontname: String; const aSize: Integer; const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont;
  721. var
  722. handle: THandle;
  723. metric: TtsFontMetric;
  724. begin
  725. handle := CreateFont(aFontname, aSize, aStyle, aAntiAliasing, metric);
  726. if (handle = 0) then
  727. raise EtsException.Create('unable to create font from name: ' + aFontname);
  728. result := TtsFontGDI.Create(handle, self, metric);
  729. end;
  730. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  731. function TtsFontCreatorGDI.GetFontByFile(const aFilename: String; const aSize: Integer; const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont;
  732. var
  733. reg: TtsFontRegistrationFile;
  734. handle: THandle;
  735. metric: TtsFontMetric;
  736. begin
  737. reg := TtsFontRegistrationFile.Create(aFilename);
  738. try
  739. if not reg.IsRegistered then
  740. raise EtsException.Create('unable to register font file: ' + aFilename);
  741. handle := CreateFont(reg.Fontname, aSize, aStyle, aAntiAliasing, metric);
  742. if (handle = 0) then
  743. raise EtsException.Create('unable to create font from file: ' + aFilename);
  744. except
  745. FreeAndNil(reg);
  746. raise;
  747. end;
  748. result := TtsRegistredFontGDI.Create(reg, handle, self, metric);
  749. end;
  750. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  751. function TtsFontCreatorGDI.GetFontByStream(const aStream: TStream; const aSize: Integer; const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont;
  752. var
  753. reg: TtsFontRegistrationStream;
  754. handle: THandle;
  755. metric: TtsFontMetric;
  756. begin
  757. reg := TtsFontRegistrationStream.Create(aStream);
  758. if not reg.IsRegistered then
  759. raise EtsException.Create('unable to register font from stream');
  760. handle := CreateFont(reg.Fontname, aSize, aStyle, aAntiAliasing, metric);
  761. if (handle = 0) then
  762. raise EtsException.Create('unable to create font from stream: ' + reg.Fontname);
  763. result := TtsRegistredFontGDI.Create(reg, handle, self, metric);
  764. end;
  765. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  766. constructor TtsFontCreatorGDI.Create(const aContext: TtsContext);
  767. begin
  768. inherited Create(aContext);
  769. InitGDI;
  770. end;
  771. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  772. destructor TtsFontCreatorGDI.Destroy;
  773. begin
  774. inherited Destroy; // first free all fonts (managed by parent class)
  775. QuitGDI;
  776. end;
  777. end.