Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

919 řádky
31 KiB

  1. unit utsFontCreatorGDI;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. Classes, SysUtils, syncobjs, dynlibs,
  6. utsTextSuite, utsTypes;
  7. type
  8. HDC = Cardinal;
  9. TFixed = packed record
  10. fract: Word;
  11. value: Smallint;
  12. end;
  13. TMat2 = packed record
  14. eM11: TFixed;
  15. eM12: TFixed;
  16. eM21: TFixed;
  17. eM22: TFixed;
  18. end;
  19. PMat2 = ^TMat2;
  20. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  21. TtsFontGDI = class(TtsFont)
  22. private
  23. fHandle: THandle;
  24. fMat2: TMat2;
  25. protected
  26. constructor Create(const aRenderer: TtsRenderer; const aCreator: TtsFontGenerator; const aProperties: TtsFontProperties; const aHandle: THandle);
  27. public
  28. destructor Destroy; override;
  29. end;
  30. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  31. TtsFontRegistration = class(TObject)
  32. protected
  33. fIsRegistered: Boolean;
  34. fFontname: String;
  35. procedure UnregisterFont; virtual; abstract;
  36. public
  37. property IsRegistered: Boolean read fIsRegistered;
  38. property Fontname: String read fFontname;
  39. destructor Destroy; override;
  40. end;
  41. TtsFontRegistrationFile = class(TtsFontRegistration)
  42. private
  43. fFilename: String;
  44. protected
  45. procedure UnregisterFont; override;
  46. public
  47. constructor Create(const aFilename: String);
  48. end;
  49. TtsFontRegistrationStream = class(TtsFontRegistration)
  50. private
  51. fHandle: THandle;
  52. protected
  53. procedure UnregisterFont; override;
  54. public
  55. constructor Create(const aStream: TStream);
  56. end;
  57. TtsRegistredFontGDI = class(TtsFontGDI)
  58. private
  59. fRegistration: TtsFontRegistration;
  60. public
  61. constructor Create(const aRenderer: TtsRenderer; const aCreator: TtsFontGenerator;
  62. const aRegistration: TtsFontRegistration; const aProperties: TtsFontProperties; const aHandle: THandle);
  63. destructor Destroy; override;
  64. end;
  65. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  66. TtsFontGeneratorGDI = class(TtsFontGenerator)
  67. private
  68. function ConvertFont(const aFont: TtsFont): TtsFontGDI;
  69. function GetGlyphIndex(const aFont: TtsFontGDI; const aCharCode: WideChar): Integer;
  70. procedure GetCharImageAANone(const aDC: HDC; const aFont: TtsFontGDI; const aCharCode: WideChar; const aImage: TtsImage);
  71. procedure GetCharImageAANormal(const aDC: HDC; const aFont: TtsFontGDI; const aCharCode: WideChar; const aImage: TtsImage);
  72. function CreateFont(const aFontname: String; const aSize: Integer; const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing; out aProperties: TtsFontProperties): THandle;
  73. protected
  74. function GetGlyphMetrics(const aFont: TtsFont; const aCharCode: WideChar; out aGlyphOrigin, aGlyphSize: TtsPosition; out aAdvance: Integer): Boolean; override;
  75. procedure GetCharImage(const aFont: TtsFont; const aCharCode: WideChar; const aCharImage: TtsImage); override;
  76. public
  77. function GetFontByName(const aFontname: String; const aRenderer: TtsRenderer; const aSize: Integer; const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont; overload;
  78. function GetFontByFile(const aFilename: String; const aRenderer: TtsRenderer; const aSize: Integer; const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont; overload;
  79. function GetFontByStream(const aStream: TStream; const aRenderer: TtsRenderer; const aSize: Integer; const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont; overload;
  80. constructor Create;
  81. destructor Destroy; override;
  82. end;
  83. implementation
  84. uses
  85. math, utsTtfUtils;
  86. const
  87. LIB_GDI32 = 'gdi32.dll';
  88. LIB_KERNEL32 = 'kernel32.dll';
  89. GDI_ERROR = DWORD($FFFFFFFF);
  90. FW_NORMAL = 400;
  91. FW_BOLD = 700;
  92. DEFAULT_CHARSET = 1;
  93. NONANTIALIASED_QUALITY = 3;
  94. ANTIALIASED_QUALITY = 4;
  95. GGO_METRICS = 0;
  96. GGO_BITMAP = 1;
  97. GGO_GRAY8_BITMAP = 6;
  98. GGO_GLYPH_INDEX = $80;
  99. FR_PRIVATE = $10;
  100. FR_NOT_ENUM = $20;
  101. LOCALE_USER_DEFAULT = $0400;
  102. LOCALE_ILANGUAGE = $1;
  103. GCP_MAXEXTENT = $100000;
  104. TMPF_FIXED_PITCH = 1;
  105. type
  106. HFONT = Cardinal;
  107. HGDIOBJ = Cardinal;
  108. TLogFontA = record
  109. lfHeight: Longint;
  110. lfWidth: Longint;
  111. lfEscapement: Longint;
  112. lfOrientation: Longint;
  113. lfWeight: Longint;
  114. lfItalic: Byte;
  115. lfUnderline: Byte;
  116. lfStrikeOut: Byte;
  117. lfCharSet: Byte;
  118. lfOutPrecision: Byte;
  119. lfClipPrecision: Byte;
  120. lfQuality: Byte;
  121. lfPitchAndFamily: Byte;
  122. lfFaceName: array[0..31] of AnsiChar;
  123. end;
  124. PLogFontA = ^TLogFontA;
  125. TTextMetricW = record
  126. tmHeight: Longint;
  127. tmAscent: Longint;
  128. tmDescent: Longint;
  129. tmInternalLeading: Longint;
  130. tmExternalLeading: Longint;
  131. tmAveCharWidth: Longint;
  132. tmMaxCharWidth: Longint;
  133. tmWeight: Longint;
  134. tmOverhang: Longint;
  135. tmDigitizedAspectX: Longint;
  136. tmDigitizedAspectY: Longint;
  137. tmFirstChar: WideChar;
  138. tmLastChar: WideChar;
  139. tmDefaultChar: WideChar;
  140. tmBreakChar: WideChar;
  141. tmItalic: Byte;
  142. tmUnderlined: Byte;
  143. tmStruckOut: Byte;
  144. tmPitchAndFamily: Byte;
  145. tmCharSet: Byte;
  146. end;
  147. PTextMetricW = ^TTextMetricW;
  148. TGlyphMetrics = record
  149. gmBlackBoxX: Cardinal;
  150. gmBlackBoxY: Cardinal;
  151. gmptGlyphOrigin: TtsPosition;
  152. gmCellIncX: Smallint;
  153. gmCellIncY: Smallint;
  154. end;
  155. PGlyphMetrics = ^TGlyphMetrics;
  156. TGCPResultsW = record
  157. lStructSize: DWORD;
  158. lpOutString: PWideChar;
  159. lpOrder: PDWORD;
  160. lpDx: PInteger;
  161. lpCaretPos: PInteger;
  162. lpClass: PChar;
  163. lpGlyphs: PCardinal;
  164. nGlyphs: Cardinal;
  165. nMaxFit: Cardinal;
  166. end;
  167. PGCPResultsW = ^TGCPResultsW;
  168. TPanose = record
  169. bFamilyType: Byte;
  170. bSerifStyle: Byte;
  171. bWeight: Byte;
  172. bProportion: Byte;
  173. bContrast: Byte;
  174. bStrokeVariation: Byte;
  175. bArmStyle: Byte;
  176. bLetterform: Byte;
  177. bMidline: Byte;
  178. bXHeight: Byte;
  179. end;
  180. PPanose = ^TPanose;
  181. TOutlineTextmetricW = record
  182. otmSize: LongWord;
  183. otmTextMetrics: TTextMetricW;
  184. otmFiller: Byte;
  185. otmPanoseNumber: TPanose;
  186. otmfsSelection: LongWord;
  187. otmfsType: LongWord;
  188. otmsCharSlopeRise: Integer;
  189. otmsCharSlopeRun: Integer;
  190. otmItalicAngle: Integer;
  191. otmEMSquare: LongWord;
  192. otmAscent: Integer;
  193. otmDescent: Integer;
  194. otmLineGap: LongWord;
  195. otmsCapEmHeight: LongWord;
  196. otmsXHeight: LongWord;
  197. otmrcFontBox: TtsRect;
  198. otmMacAscent: Integer;
  199. otmMacDescent: Integer;
  200. otmMacLineGap: LongWord;
  201. otmusMinimumPPEM: LongWord;
  202. otmptSubscriptSize: TtsPosition;
  203. otmptSubscriptOffset: TtsPosition;
  204. otmptSuperscriptSize: TtsPosition;
  205. otmptSuperscriptOffset: TtsPosition;
  206. otmsStrikeoutSize: LongWord;
  207. otmsStrikeoutPosition: Integer;
  208. otmsUnderscoreSize: Integer;
  209. otmsUnderscorePosition: Integer;
  210. otmpFamilyName: PWideChar;
  211. otmpFaceName: PWideChar;
  212. otmpStyleName: PWideChar;
  213. otmpFullName: PWideChar;
  214. end;
  215. POutlineTextmetricW = ^TOutlineTextmetricW;
  216. TCreateFontIndirectA = function (const p1: TLogFontA): HFONT; stdcall;
  217. TAddFontResourceA = function(Filename: PAnsiChar): Integer; stdcall;
  218. TAddFontResourceExA = function(Filename: PAnsiChar; Flag: DWORD; pdv: Pointer): Integer; stdcall;
  219. TAddFontMemResourceEx = function(pbFont: Pointer; cbFont: DWORD; pdv: Pointer; pcFonts: PDWORD): THandle; stdcall;
  220. TRemoveFontResourceA = function(Filename: PAnsiChar): Boolean; stdcall;
  221. TRemoveFontResourceExA = function(filename: PAnsiChar; Flag: DWORD; pdv: Pointer): Boolean; stdcall;
  222. TRemoveFontMemResourceEx = function(fh: THandle): Boolean; stdcall;
  223. TGetTextMetricsW = function(DC: HDC; var TM: TTextMetricW): Boolean; stdcall;
  224. TGetGlyphOutlineA = function(DC: HDC; uChar, uFormat: Cardinal; lpgm: PGlyphMetrics; cbBuffer: DWORD; lpvBuffer: Pointer; lpmat2: PMat2): DWORD; stdcall;
  225. TGetCharacterPlacementW = function(DC: HDC; Str: PWideChar; Count, MaxExtent: Integer; Result: PGCPResultsW; Flags: DWORD): DWORD; stdcall;
  226. TGetFontData = function(DC: HDC; TableName, Offset: DWORD; Buffer: Pointer; Data: DWORD): DWORD; stdcall;
  227. TCreateCompatibleDC = function(DC: HDC): HDC; stdcall;
  228. TDeleteDC = function(DC: HDC): Boolean; stdcall;
  229. TSelectObject = function(DC: HDC; p2: HGDIOBJ): HGDIOBJ; stdcall;
  230. TDeleteObject = function(p1: HGDIOBJ): Boolean; stdcall;
  231. TGetOutlineTextMetricsW = function(DC: HDC; p2: LongWord; var OTMetricStructs: TOutlineTextmetricW): LongWord; stdcall;
  232. TGetLocaleInfoA = function(Locale: DWORD; LCType: DWORD; lpLCData: pAnsiChar; cchData: Integer): Integer; stdcall;
  233. var
  234. gdiRefCount: Integer;
  235. gdiCritSec: TCriticalSection;
  236. gdiInitialized: Boolean;
  237. gdiLibHandle: TLibHandle = 0;
  238. kernel32LibHandle: TLibHandle = 0;
  239. CreateFontIndirectA: TCreateFontIndirectA;
  240. AddFontResourceA: TAddFontResourceA;
  241. AddFontResourceExA: TAddFontResourceExA;
  242. AddFontMemResourceEx: TAddFontMemResourceEx;
  243. RemoveFontResourceA: TRemoveFontResourceA;
  244. RemoveFontResourceExA: TRemoveFontResourceExA;
  245. RemoveFontMemResourceEx: TRemoveFontMemResourceEx;
  246. GetTextMetricsW: TGetTextMetricsW;
  247. GetGlyphOutlineA: TGetGlyphOutlineA;
  248. GetCharacterPlacementW: TGetCharacterPlacementW;
  249. GetFontData: TGetFontData;
  250. CreateCompatibleDC: TCreateCompatibleDC;
  251. DeleteDC: TDeleteDC;
  252. SelectObject: TSelectObject;
  253. DeleteObject: TDeleteObject;
  254. GetOutlineTextMetricsW: TGetOutlineTextMetricsW;
  255. GetLocaleInfoA: TGetLocaleInfoA;
  256. procedure InitGDI;
  257. function GetProcAddr(const aLibHandle: TLibHandle; const aName: String): Pointer;
  258. begin
  259. result := GetProcAddress(aLibHandle, aName);
  260. if not Assigned(result) then
  261. raise EtsException.Create('unable to load procedure from library: ' + aName);
  262. end;
  263. begin
  264. try
  265. if (gdiLibHandle = 0) then begin
  266. gdiLibHandle := LoadLibrary(LIB_GDI32);
  267. if (gdiLibHandle = 0) then
  268. raise EtsException.Create('unable to load gdi lib: ' + LIB_GDI32);
  269. end;
  270. if (kernel32LibHandle = 0) then begin
  271. kernel32LibHandle := LoadLibrary(LIB_KERNEL32);
  272. if (kernel32LibHandle = 0) then
  273. raise EtsException.Create('unable to load kernel lib: ' + LIB_KERNEL32);
  274. end;
  275. CreateFontIndirectA := TCreateFontIndirectA( GetProcAddr(gdiLibHandle, 'CreateFontIndirectA'));
  276. AddFontResourceA := TAddFontResourceA( GetProcAddr(gdiLibHandle, 'AddFontResourceA'));
  277. AddFontResourceExA := TAddFontResourceExA( GetProcAddr(gdiLibHandle, 'AddFontResourceExA'));
  278. AddFontMemResourceEx := TAddFontMemResourceEx( GetProcAddr(gdiLibHandle, 'AddFontMemResourceEx'));
  279. RemoveFontResourceA := TRemoveFontResourceA( GetProcAddr(gdiLibHandle, 'RemoveFontResourceA'));
  280. RemoveFontResourceExA := TRemoveFontResourceExA( GetProcAddr(gdiLibHandle, 'RemoveFontResourceExA'));
  281. RemoveFontMemResourceEx := TRemoveFontMemResourceEx(GetProcAddr(gdiLibHandle, 'RemoveFontMemResourceEx'));
  282. GetTextMetricsW := TGetTextMetricsW( GetProcAddr(gdiLibHandle, 'GetTextMetricsW'));
  283. GetGlyphOutlineA := TGetGlyphOutlineA( GetProcAddr(gdiLibHandle, 'GetGlyphOutlineA'));
  284. GetCharacterPlacementW := TGetCharacterPlacementW( GetProcAddr(gdiLibHandle, 'GetCharacterPlacementW'));
  285. GetFontData := TGetFontData( GetProcAddr(gdiLibHandle, 'GetFontData'));
  286. CreateCompatibleDC := TCreateCompatibleDC( GetProcAddr(gdiLibHandle, 'CreateCompatibleDC'));
  287. DeleteDC := TDeleteDC( GetProcAddr(gdiLibHandle, 'DeleteDC'));
  288. SelectObject := TSelectObject( GetProcAddr(gdiLibHandle, 'SelectObject'));
  289. DeleteObject := TDeleteObject( GetProcAddr(gdiLibHandle, 'DeleteObject'));
  290. GetOutlineTextMetricsW := TGetOutlineTextMetricsW( GetProcAddr(gdiLibHandle, 'GetOutlineTextMetricsW'));
  291. GetLocaleInfoA := TGetLocaleInfoA(GetProcAddr(kernel32LibHandle, 'GetLocaleInfoA'));
  292. gdiInitialized := true;
  293. except
  294. gdiInitialized := false;
  295. FreeLibrary(gdiLibHandle);
  296. FreeLibrary(kernel32LibHandle);
  297. end;
  298. end;
  299. procedure QuitGDI;
  300. begin
  301. CreateFontIndirectA := nil;
  302. AddFontResourceA := nil;
  303. AddFontResourceExA := nil;
  304. RemoveFontResourceA := nil;
  305. RemoveFontResourceExA := nil;
  306. GetTextMetricsW := nil;
  307. GetGlyphOutlineA := nil;
  308. GetCharacterPlacementW := nil;
  309. GetFontData := nil;
  310. CreateCompatibleDC := nil;
  311. DeleteDC := nil;
  312. SelectObject := nil;
  313. DeleteObject := nil;
  314. GetLocaleInfoA := nil;
  315. if (gdiLibHandle <> 0) then begin
  316. FreeLibrary(gdiLibHandle);
  317. gdiLibHandle := 0;
  318. end;
  319. if (kernel32LibHandle <> 0) then begin
  320. FreeLibrary(kernel32LibHandle);
  321. kernel32LibHandle := 0;
  322. end;
  323. gdiInitialized := false;
  324. end;
  325. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  326. //TtsFontGDI////////////////////////////////////////////////////////////////////////////////////////////////////////////
  327. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  328. constructor TtsFontGDI.Create(const aRenderer: TtsRenderer; const aCreator: TtsFontGenerator; const aProperties: TtsFontProperties; const aHandle: THandle);
  329. begin
  330. inherited Create(aRenderer, aCreator, aProperties);
  331. FillByte(fMat2, SizeOf(fMat2), 0);
  332. fMat2.eM11.value := 1;
  333. fMat2.eM22.value := 1;
  334. fHandle := aHandle;
  335. end;
  336. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  337. destructor TtsFontGDI.Destroy;
  338. begin
  339. DeleteObject(fHandle);
  340. inherited Destroy;
  341. end;
  342. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  343. //TtsFontRegistration///////////////////////////////////////////////////////////////////////////////////////////////////
  344. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  345. destructor TtsFontRegistration.Destroy;
  346. begin
  347. if fIsRegistered then
  348. UnregisterFont;
  349. inherited Destroy;
  350. end;
  351. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  352. //TtsFontRegistrationFile///////////////////////////////////////////////////////////////////////////////////////////////
  353. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  354. procedure TtsFontRegistrationFile.UnregisterFont;
  355. begin
  356. if Assigned(RemoveFontResourceExA) then
  357. RemoveFontResourceExA(PAnsiChar(fFilename), 0, nil)
  358. else if Assigned(RemoveFontResourceA) then
  359. RemoveFontResourceA(PAnsiChar(fFilename));
  360. end;
  361. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  362. constructor TtsFontRegistrationFile.Create(const aFilename: String);
  363. var
  364. lang: AnsiString;
  365. begin
  366. inherited Create;
  367. fFilename := aFilename;
  368. // get Fontname
  369. SetLength(lang, 4);
  370. GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_ILANGUAGE, @lang[1], 4);
  371. fFontname := GetTTFontFullNameFromFile(aFilename, StrToInt('$' + String(lang)));
  372. // register font
  373. if Assigned(AddFontResourceExA) then
  374. fIsRegistered := (AddFontResourceExA(PAnsiChar(fFilename), 0, nil) > 0)
  375. else if Assigned(AddFontResourceA) then
  376. fIsRegistered := (AddFontResourceA(PAnsiChar(fFilename)) > 0)
  377. else
  378. fIsRegistered := false;
  379. end;
  380. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  381. //TtsFontRegistrationStream/////////////////////////////////////////////////////////////////////////////////////////////
  382. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  383. procedure TtsFontRegistrationStream.UnregisterFont;
  384. begin
  385. if Assigned(RemoveFontMemResourceEx) then
  386. RemoveFontMemResourceEx(fHandle);
  387. end;
  388. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  389. constructor TtsFontRegistrationStream.Create(const aStream: TStream);
  390. var
  391. lang: AnsiString;
  392. ms: TMemoryStream;
  393. cnt: DWORD;
  394. begin
  395. inherited Create;
  396. fHandle := 0;
  397. fIsRegistered := false;
  398. // get Fontname
  399. SetLength(Lang, 4);
  400. GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_ILANGUAGE, @lang[1], 4);
  401. fFontname := GetTTFontFullNameFromStream(aStream, StrToInt('$' + String(Lang)));
  402. // register font
  403. ms := TMemoryStream.Create;
  404. try
  405. ms.CopyFrom(aStream, 0);
  406. if Assigned(AddFontMemResourceEx) then
  407. fHandle := AddFontMemResourceEx(ms.Memory, ms.Size, nil, @cnt);
  408. fIsRegistered := (fHandle > 0);
  409. finally
  410. FreeAndNil(ms);
  411. end;
  412. end;
  413. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  414. //TtsRegistredFontGDI///////////////////////////////////////////////////////////////////////////////////////////////////
  415. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  416. constructor TtsRegistredFontGDI.Create(const aRenderer: TtsRenderer; const aCreator: TtsFontGenerator;
  417. const aRegistration: TtsFontRegistration; const aProperties: TtsFontProperties; const aHandle: THandle);
  418. begin
  419. inherited Create(aRenderer, aCreator, aProperties, aHandle);
  420. fRegistration := aRegistration;
  421. end;
  422. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  423. destructor TtsRegistredFontGDI.Destroy;
  424. begin
  425. FreeAndNil(fRegistration);
  426. inherited Destroy;
  427. end;
  428. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  429. //TtsFontCreatorGDIFontFace/////////////////////////////////////////////////////////////////////////////////////////////
  430. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  431. function TtsFontGeneratorGDI.ConvertFont(const aFont: TtsFont): TtsFontGDI;
  432. begin
  433. if not (aFont is TtsFontGDI) then
  434. raise EtsException.Create('aFont need to be a TtsFontGDI object');
  435. result := (aFont as TtsFontGDI);
  436. end;
  437. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  438. function TtsFontGeneratorGDI.GetGlyphIndex(const aFont: TtsFontGDI; const aCharCode: WideChar): Integer;
  439. var
  440. DC: HDC;
  441. GCPRes: TGCPResultsW;
  442. begin
  443. result := -1;
  444. DC := CreateCompatibleDC(0);
  445. try
  446. SelectObject(DC, aFont.fHandle);
  447. if Assigned(GetCharacterPlacementW) then begin
  448. FillByte(GCPRes, SizeOf(GCPRes), 0);
  449. GetMem(GCPRes.lpGlyphs, SizeOf(Cardinal));
  450. try
  451. GCPRes.lStructSize := SizeOf(GCPRes);
  452. GCPRes.lpGlyphs^ := 0;
  453. GCPRes.nGlyphs := 1;
  454. if (GetCharacterPlacementW(DC, @aCharCode, 1, GCP_MAXEXTENT, @GCPRes, 0) <> GDI_ERROR) and
  455. (GCPRes.nGlyphs = 1) and
  456. (GCPRes.lpGlyphs <> nil) then
  457. begin
  458. result := GCPRes.lpGlyphs^;
  459. end;
  460. finally
  461. FreeMem(GCPRes.lpGlyphs);
  462. end;
  463. end;
  464. finally
  465. DeleteDC(DC);
  466. end;
  467. end;
  468. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  469. procedure TtsFontGeneratorGDI.GetCharImageAANone(const aDC: HDC; const aFont: TtsFontGDI; const aCharCode: WideChar; const aImage: TtsImage);
  470. var
  471. Metric: TGlyphMetrics;
  472. GlyphIndex, srcW, srcX, w, h, x, y: Integer;
  473. Size, OutlineRes: Cardinal;
  474. Buffer, pSrc, pDst: PByte;
  475. procedure ExpandByte;
  476. var
  477. i, cnt, srcCnt: Integer;
  478. c: TtsColor4f;
  479. begin
  480. srcCnt := min(8, srcX);
  481. cnt := min(8, x);
  482. for i := 1 to cnt do begin
  483. c := tsColor4f(1, 1, 1, 1);
  484. if ((pSrc^ and $80) > 0) then
  485. c.a := 1
  486. else
  487. c.a := 0;
  488. pSrc^ := (pSrc^ and not $80) shl 1;
  489. tsFormatMap(aFont.Renderer.Format, pDst, c);
  490. end;
  491. dec(srcX, srcCnt);
  492. dec(x, cnt);
  493. inc(pSrc);
  494. end;
  495. begin
  496. if (aFont.fMat2.eM11.value <> 1) then
  497. raise EtsException.Create('invalid value');
  498. FillByte(Metric, SizeOf(Metric), 0);
  499. GlyphIndex := GetGlyphIndex(aFont, aCharCode);
  500. if (GlyphIndex < 0) then
  501. exit;
  502. Size := GetGlyphOutlineA(aDC, GlyphIndex, GGO_BITMAP or GGO_GLYPH_INDEX, @Metric, 0, nil, @aFont.fMat2);
  503. if (Size = GDI_ERROR) or (Size = 0) then
  504. exit;
  505. GetMem(Buffer, Size);
  506. try
  507. OutlineRes := GetGlyphOutlineA(aDC, GlyphIndex, GGO_BITMAP or GGO_GLYPH_INDEX, @Metric, Size, Buffer, @aFont.fMat2);
  508. if (OutlineRes = GDI_ERROR) then
  509. exit;
  510. w := Metric.gmBlackBoxX;
  511. h := Metric.gmBlackBoxY;
  512. srcW := (Size div h) * 8;
  513. if (w <= 0) or (h <= 0) then
  514. exit;
  515. aImage.CreateEmpty(aFont.Renderer.Format, w, h);
  516. pSrc := Buffer;
  517. for y := 0 to h-1 do begin
  518. pDst := aImage.Scanline[y];
  519. srcX := srcW;
  520. while (srcX > 0) do
  521. ExpandByte;
  522. end;
  523. finally
  524. Freemem(Buffer);
  525. end;
  526. end;
  527. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  528. procedure TtsFontGeneratorGDI.GetCharImageAANormal(const aDC: HDC; const aFont: TtsFontGDI; const aCharCode: WideChar; const aImage: TtsImage);
  529. var
  530. Metric: TGlyphMetrics;
  531. GlyphIndex, OutlineRes, tmp, Spacer, x, y, w, h: Integer;
  532. Size: Cardinal;
  533. Buffer, pSrc, pDst: PByte;
  534. procedure CopyPixel;
  535. var
  536. i: Integer;
  537. tmp, cnt: Cardinal;
  538. c: TtsColor4f;
  539. begin
  540. cnt := min(x, aFont.fMat2.eM11.value);
  541. tmp := 0;
  542. for i := 0 to cnt-1 do begin
  543. tmp := tmp + pSrc^;
  544. inc(pSrc, 1);
  545. end;
  546. dec(x, cnt);
  547. c := tsColor4f(1, 1, 1, tmp / ($40 * Cardinal(aFont.fMat2.eM11.value)));
  548. tsFormatMap(aFont.Renderer.Format, pDst, c);
  549. end;
  550. begin
  551. FillByte(Metric, SizeOf(Metric), 0);
  552. GlyphIndex := GetGlyphIndex(aFont, aCharCode);
  553. if (GlyphIndex < 0) then
  554. exit;
  555. Size := GetGlyphOutlineA(aDC, GlyphIndex, GGO_GRAY8_BITMAP or GGO_GLYPH_INDEX, @Metric, 0, nil, @aFont.fMat2);
  556. if (Size = GDI_ERROR) or (Size = 0) then
  557. exit;
  558. GetMem(Buffer, Size);
  559. try
  560. OutlineRes := GetGlyphOutlineA(aDC, GlyphIndex, GGO_GRAY8_BITMAP or GGO_GLYPH_INDEX, @Metric, Size, Buffer, @aFont.fMat2);
  561. if (OutlineRes = GDI_ERROR) then
  562. exit;
  563. w := Integer(Metric.gmBlackBoxX) div aFont.fMat2.eM11.value;
  564. h := Metric.gmBlackBoxY;
  565. tmp := Integer(Metric.gmBlackBoxX) mod aFont.fMat2.eM11.value;
  566. if (tmp <> 0) then
  567. w := w + aFont.fMat2.eM11.value - tmp;
  568. if (w <= 0) or (h <= 0) then
  569. exit;
  570. // spacer
  571. Spacer := Metric.gmBlackBoxX mod 4;
  572. if (Spacer <> 0) then
  573. Spacer := 4 - Spacer;
  574. // copy image
  575. aImage.CreateEmpty(aFont.Renderer.Format, w, h);
  576. pSrc := Buffer;
  577. for y := 0 to h-1 do begin
  578. pDst := aImage.Scanline[y];
  579. x := Metric.gmBlackBoxX;
  580. while (x > 0) do
  581. CopyPixel;
  582. inc(pSrc, Spacer);
  583. end;
  584. finally
  585. FreeMem(Buffer);
  586. end;
  587. end;
  588. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  589. function TtsFontGeneratorGDI.CreateFont(const aFontname: String; const aSize: Integer; const aStyle: TtsFontStyles;
  590. const aAntiAliasing: TtsAntiAliasing; out aProperties: TtsFontProperties): THandle;
  591. var
  592. LogFont: TLogFontA;
  593. i: Integer;
  594. DC: HDC;
  595. TableName, BufSize: Cardinal;
  596. Buffer: PByte;
  597. Lang: AnsiString;
  598. TextMetric: TTextMetricW;
  599. OutlineMetric: TOutlineTextmetricW;
  600. function _(e: Boolean; a, b: Integer): Integer;
  601. begin
  602. if e then
  603. result := a
  604. else
  605. result := b;
  606. end;
  607. begin
  608. result := 0;
  609. FillByte(aProperties, SizeOf(aProperties), 0);
  610. aProperties.Size := aSize;
  611. aProperties.Style := aStyle;
  612. aProperties.AntiAliasing := aAntiAliasing;
  613. aProperties.Fontname := aFontname;
  614. // prepare font attribs
  615. FillByte(LogFont, SizeOf(LogFont), 0);
  616. for i := 1 to min(Length(aFontname), Length(LogFont.lfFaceName)) do
  617. LogFont.lfFaceName[i-1] := aFontname[i];
  618. LogFont.lfCharSet := DEFAULT_CHARSET;
  619. LogFont.lfHeight := -aSize;
  620. LogFont.lfWeight := _(tsStyleBold in aStyle, FW_BOLD, FW_NORMAL);
  621. LogFont.lfItalic := _(tsStyleItalic in aStyle, 1, 0);
  622. LogFont.lfUnderline := _(tsStyleUnderline in aStyle, 1, 0);
  623. LogFont.lfQuality := _(aAntiAliasing = tsAANormal, ANTIALIASED_QUALITY, NONANTIALIASED_QUALITY);
  624. result := CreateFontIndirectA(LogFont);
  625. DC := CreateCompatibleDC(0);
  626. try try
  627. SelectObject(DC, result);
  628. TableName := MakeTTTableName('n', 'a', 'm', 'e');
  629. BufSize := GetFontData(DC, TableName, 0, nil, 0);
  630. if (BufSize <> GDI_ERROR) then begin
  631. GetMem(Buffer, BufSize);
  632. try
  633. if (GetFontData(DC, TableName, 0, Buffer, BufSize) <> GDI_ERROR) then begin
  634. SetLength(Lang, 4);
  635. GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_ILANGUAGE, @Lang[1], 4);
  636. GetTTString(Buffer, BufSize, NAME_ID_COPYRIGHT, StrToInt('$' + String(Lang)), aProperties.Copyright);
  637. GetTTString(Buffer, BufSize, NAME_ID_FACE_NAME, StrToInt('$' + String(Lang)), aProperties.FaceName);
  638. GetTTString(Buffer, BufSize, NAME_ID_STYLE_NAME, StrToInt('$' + String(Lang)), aProperties.StyleName);
  639. GetTTString(Buffer, BufSize, NAME_ID_FULL_NAME, StrToInt('$' + String(Lang)), aProperties.FullName);
  640. end;
  641. finally
  642. FreeMem(Buffer);
  643. end;
  644. end;
  645. if GetTextMetricsW(DC, TextMetric) then begin
  646. aProperties.Ascent := TextMetric.tmAscent;
  647. aProperties.Descent := TextMetric.tmDescent;
  648. aProperties.ExternalLeading := TextMetric.tmExternalLeading;
  649. aProperties.DefaultChar := TextMetric.tmDefaultChar;
  650. end;
  651. if (GetOutlineTextMetricsW(DC, SizeOf(OutlineMetric), OutlineMetric) > 0) then begin
  652. aProperties.UnderlinePos := OutlineMetric.otmsUnderscorePosition;
  653. aProperties.UnderlineSize := Min(1, OutlineMetric.otmsUnderscoreSize);
  654. aProperties.StrikeoutPos := OutlineMetric.otmsStrikeoutPosition;
  655. aProperties.StrikeoutSize := Min(1, OutlineMetric.otmsStrikeoutSize);
  656. end;
  657. except
  658. DeleteObject(result);
  659. result := 0;
  660. end;
  661. finally
  662. DeleteDC(DC);
  663. end;
  664. end;
  665. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  666. function TtsFontGeneratorGDI.GetGlyphMetrics(const aFont: TtsFont; const aCharCode: WideChar; out aGlyphOrigin, aGlyphSize: TtsPosition; out aAdvance: Integer): Boolean;
  667. var
  668. GlyphIndex: Integer;
  669. font: TtsFontGDI;
  670. DC: HDC;
  671. Metric: TGlyphMetrics;
  672. Size: Cardinal;
  673. begin
  674. result := false;
  675. aGlyphOrigin.x := 0;
  676. aGlyphOrigin.x := 0;
  677. aGlyphSize.x := 0;
  678. aGlyphSize.y := 0;
  679. aAdvance := 0;
  680. font := ConvertFont(aFont);
  681. GlyphIndex := GetGlyphIndex(font, aCharCode);
  682. if (GlyphIndex < 0) then
  683. exit;
  684. DC := CreateCompatibleDC(0);
  685. try
  686. SelectObject(DC, font.fHandle);
  687. case font.Properties.AntiAliasing of
  688. tsAANone: begin
  689. Size := GetGlyphOutlineA(DC, GlyphIndex, GGO_BITMAP or GGO_GLYPH_INDEX, @Metric, 0, nil, @font.fMat2);
  690. end;
  691. tsAANormal: begin
  692. Size := GetGlyphOutlineA(DC, GlyphIndex, GGO_GRAY8_BITMAP or GGO_GLYPH_INDEX, @Metric, 0, nil, @font.fMat2);
  693. end;
  694. else
  695. Size := GDI_ERROR;
  696. end;
  697. if (Size = GDI_ERROR) then
  698. Size := GetGlyphOutlineA(DC, GlyphIndex, GGO_METRICS or GGO_GLYPH_INDEX, @Metric, 0, nil, @font.fMat2);
  699. if (Size <> GDI_ERROR) then begin
  700. aGlyphOrigin.x := Round(Metric.gmptGlyphOrigin.x / font.fMat2.eM11.value);
  701. aGlyphOrigin.y := Metric.gmptGlyphOrigin.y;
  702. aGlyphSize.x := Round(Metric.gmBlackBoxX / font.fMat2.eM11.value);
  703. aGlyphSize.y := Metric.gmBlackBoxY;
  704. aAdvance := Round(Metric.gmCellIncX / font.fMat2.eM11.value);
  705. result := true;
  706. end;
  707. finally
  708. DeleteDC(DC);
  709. end;
  710. end;
  711. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  712. procedure TtsFontGeneratorGDI.GetCharImage(const aFont: TtsFont; const aCharCode: WideChar; const aCharImage: TtsImage);
  713. var
  714. DC: HDC;
  715. font: TtsFontGDI;
  716. begin
  717. font := ConvertFont(aFont);
  718. DC := CreateCompatibleDC(0);
  719. try
  720. SelectObject(DC, font.fHandle);
  721. case font.Properties.AntiAliasing of
  722. tsAANone:
  723. GetCharImageAANone(DC, font, aCharCode, aCharImage);
  724. tsAANormal:
  725. GetCharImageAANormal(DC, font, aCharCode, aCharImage);
  726. end;
  727. finally
  728. DeleteDC(DC);
  729. end;
  730. end;
  731. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  732. function TtsFontGeneratorGDI.GetFontByName(const aFontname: String; const aRenderer: TtsRenderer;
  733. const aSize: Integer; const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont;
  734. var
  735. handle: THandle;
  736. prop: TtsFontProperties;
  737. begin
  738. handle := CreateFont(aFontname, aSize, aStyle, aAntiAliasing, prop);
  739. if (handle = 0) then
  740. raise EtsException.Create('unable to create font from name: ' + aFontname);
  741. result := TtsFontGDI.Create(aRenderer, self, prop, handle);
  742. end;
  743. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  744. function TtsFontGeneratorGDI.GetFontByFile(const aFilename: String; const aRenderer: TtsRenderer;
  745. const aSize: Integer; const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont;
  746. var
  747. reg: TtsFontRegistrationFile;
  748. handle: THandle;
  749. prop: TtsFontProperties;
  750. begin
  751. reg := TtsFontRegistrationFile.Create(aFilename);
  752. if not reg.IsRegistered then
  753. raise EtsException.Create('unable to register font file: ' + aFilename);
  754. handle := CreateFont(reg.Fontname, aSize, aStyle, aAntiAliasing, prop);
  755. if (handle = 0) then
  756. raise EtsException.Create('unable to create font from file: ' + aFilename);
  757. result := TtsRegistredFontGDI.Create(aRenderer, self, reg, prop, handle);
  758. end;
  759. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  760. function TtsFontGeneratorGDI.GetFontByStream(const aStream: TStream; const aRenderer: TtsRenderer;
  761. const aSize: Integer; const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont;
  762. var
  763. reg: TtsFontRegistrationStream;
  764. handle: THandle;
  765. prop: TtsFontProperties;
  766. begin
  767. reg := TtsFontRegistrationStream.Create(aStream);
  768. if not reg.IsRegistered then
  769. raise EtsException.Create('unable to register font from stream');
  770. handle := CreateFont(reg.Fontname, aSize, aStyle, aAntiAliasing, prop);
  771. if (handle = 0) then
  772. raise EtsException.Create('unable to create font from stream: ' + reg.Fontname);
  773. result := TtsRegistredFontGDI.Create(aRenderer, self, reg, prop, handle);
  774. end;
  775. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  776. constructor TtsFontGeneratorGDI.Create;
  777. begin
  778. inherited Create;
  779. gdiCritSec.Enter;
  780. try
  781. inc(gdiRefCount, 1);
  782. if not gdiInitialized then
  783. InitGDI;
  784. finally
  785. gdiCritSec.Leave;
  786. end;
  787. end;
  788. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  789. destructor TtsFontGeneratorGDI.Destroy;
  790. begin
  791. gdiCritSec.Enter;
  792. try
  793. dec(gdiRefCount, 1);
  794. if (gdiRefCount <= 0) then
  795. QuitGDI;
  796. finally
  797. gdiCritSec.Leave;
  798. end;
  799. inherited Destroy;
  800. end;
  801. initialization
  802. gdiRefCount := 0;
  803. gdiInitialized := false;
  804. gdiCritSec := TCriticalSection.Create;
  805. finalization
  806. if gdiInitialized then
  807. QuitGDI;
  808. FreeAndNil(gdiCritSec);
  809. end.