25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

920 lines
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(const aContext: TtsContext);
  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.0
  486. else
  487. c.a := 0.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. x := w;
  521. while (srcX > 0) do
  522. ExpandByte;
  523. end;
  524. finally
  525. Freemem(Buffer);
  526. end;
  527. end;
  528. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  529. procedure TtsFontGeneratorGDI.GetCharImageAANormal(const aDC: HDC; const aFont: TtsFontGDI; const aCharCode: WideChar; const aImage: TtsImage);
  530. var
  531. Metric: TGlyphMetrics;
  532. GlyphIndex, OutlineRes, tmp, Spacer, x, y, w, h: Integer;
  533. Size: Cardinal;
  534. Buffer, pSrc, pDst: PByte;
  535. procedure CopyPixel;
  536. var
  537. i: Integer;
  538. tmp, cnt: Cardinal;
  539. c: TtsColor4f;
  540. begin
  541. cnt := min(x, aFont.fMat2.eM11.value);
  542. tmp := 0;
  543. for i := 0 to cnt-1 do begin
  544. tmp := tmp + pSrc^;
  545. inc(pSrc, 1);
  546. end;
  547. dec(x, cnt);
  548. c := tsColor4f(1, 1, 1, tmp / $40);
  549. tsFormatMap(aFont.Renderer.Format, pDst, c);
  550. end;
  551. begin
  552. FillByte(Metric, SizeOf(Metric), 0);
  553. GlyphIndex := GetGlyphIndex(aFont, aCharCode);
  554. if (GlyphIndex < 0) then
  555. exit;
  556. Size := GetGlyphOutlineA(aDC, GlyphIndex, GGO_GRAY8_BITMAP or GGO_GLYPH_INDEX, @Metric, 0, nil, @aFont.fMat2);
  557. if (Size = GDI_ERROR) or (Size = 0) then
  558. exit;
  559. GetMem(Buffer, Size);
  560. try
  561. OutlineRes := GetGlyphOutlineA(aDC, GlyphIndex, GGO_GRAY8_BITMAP or GGO_GLYPH_INDEX, @Metric, Size, Buffer, @aFont.fMat2);
  562. if (OutlineRes = GDI_ERROR) then
  563. exit;
  564. w := Integer(Metric.gmBlackBoxX) div aFont.fMat2.eM11.value;
  565. h := Metric.gmBlackBoxY;
  566. tmp := Integer(Metric.gmBlackBoxX) mod aFont.fMat2.eM11.value;
  567. if (tmp <> 0) then
  568. w := w + aFont.fMat2.eM11.value - tmp;
  569. if (w <= 0) or (h <= 0) then
  570. exit;
  571. // spacer
  572. Spacer := Metric.gmBlackBoxX mod 4;
  573. if (Spacer <> 0) then
  574. Spacer := 4 - Spacer;
  575. // copy image
  576. aImage.CreateEmpty(aFont.Renderer.Format, w, h);
  577. pSrc := Buffer;
  578. for y := 0 to h-1 do begin
  579. pDst := aImage.Scanline[y];
  580. x := Metric.gmBlackBoxX;
  581. while (x > 0) do
  582. CopyPixel;
  583. inc(pSrc, Spacer);
  584. end;
  585. finally
  586. FreeMem(Buffer);
  587. end;
  588. end;
  589. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  590. function TtsFontGeneratorGDI.CreateFont(const aFontname: String; const aSize: Integer; const aStyle: TtsFontStyles;
  591. const aAntiAliasing: TtsAntiAliasing; out aProperties: TtsFontProperties): THandle;
  592. var
  593. LogFont: TLogFontA;
  594. i: Integer;
  595. DC: HDC;
  596. TableName, BufSize: Cardinal;
  597. Buffer: PByte;
  598. Lang: AnsiString;
  599. TextMetric: TTextMetricW;
  600. OutlineMetric: TOutlineTextmetricW;
  601. function _(e: Boolean; a, b: Integer): Integer;
  602. begin
  603. if e then
  604. result := a
  605. else
  606. result := b;
  607. end;
  608. begin
  609. result := 0;
  610. FillByte(aProperties, SizeOf(aProperties), 0);
  611. aProperties.Size := aSize;
  612. aProperties.Style := aStyle;
  613. aProperties.AntiAliasing := aAntiAliasing;
  614. aProperties.Fontname := aFontname;
  615. // prepare font attribs
  616. FillByte(LogFont, SizeOf(LogFont), 0);
  617. for i := 1 to min(Length(aFontname), Length(LogFont.lfFaceName)) do
  618. LogFont.lfFaceName[i-1] := aFontname[i];
  619. LogFont.lfCharSet := DEFAULT_CHARSET;
  620. LogFont.lfHeight := -aSize;
  621. LogFont.lfWeight := _(tsStyleBold in aStyle, FW_BOLD, FW_NORMAL);
  622. LogFont.lfItalic := _(tsStyleItalic in aStyle, 1, 0);
  623. LogFont.lfUnderline := _(tsStyleUnderline in aStyle, 1, 0);
  624. LogFont.lfQuality := _(aAntiAliasing = tsAANormal, ANTIALIASED_QUALITY, NONANTIALIASED_QUALITY);
  625. result := CreateFontIndirectA(LogFont);
  626. DC := CreateCompatibleDC(0);
  627. try try
  628. SelectObject(DC, result);
  629. TableName := MakeTTTableName('n', 'a', 'm', 'e');
  630. BufSize := GetFontData(DC, TableName, 0, nil, 0);
  631. if (BufSize <> GDI_ERROR) then begin
  632. GetMem(Buffer, BufSize);
  633. try
  634. if (GetFontData(DC, TableName, 0, Buffer, BufSize) <> GDI_ERROR) then begin
  635. SetLength(Lang, 4);
  636. GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_ILANGUAGE, @Lang[1], 4);
  637. GetTTString(Buffer, BufSize, NAME_ID_COPYRIGHT, StrToInt('$' + String(Lang)), aProperties.Copyright);
  638. GetTTString(Buffer, BufSize, NAME_ID_FACE_NAME, StrToInt('$' + String(Lang)), aProperties.FaceName);
  639. GetTTString(Buffer, BufSize, NAME_ID_STYLE_NAME, StrToInt('$' + String(Lang)), aProperties.StyleName);
  640. GetTTString(Buffer, BufSize, NAME_ID_FULL_NAME, StrToInt('$' + String(Lang)), aProperties.FullName);
  641. end;
  642. finally
  643. FreeMem(Buffer);
  644. end;
  645. end;
  646. if GetTextMetricsW(DC, TextMetric) then begin
  647. aProperties.Ascent := TextMetric.tmAscent;
  648. aProperties.Descent := TextMetric.tmDescent;
  649. aProperties.ExternalLeading := TextMetric.tmExternalLeading;
  650. aProperties.DefaultChar := TextMetric.tmDefaultChar;
  651. end;
  652. if (GetOutlineTextMetricsW(DC, SizeOf(OutlineMetric), OutlineMetric) > 0) then begin
  653. aProperties.UnderlinePos := OutlineMetric.otmsUnderscorePosition;
  654. aProperties.UnderlineSize := Min(1, OutlineMetric.otmsUnderscoreSize);
  655. aProperties.StrikeoutPos := OutlineMetric.otmsStrikeoutPosition;
  656. aProperties.StrikeoutSize := Min(1, OutlineMetric.otmsStrikeoutSize);
  657. end;
  658. except
  659. DeleteObject(result);
  660. result := 0;
  661. end;
  662. finally
  663. DeleteDC(DC);
  664. end;
  665. end;
  666. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  667. function TtsFontGeneratorGDI.GetGlyphMetrics(const aFont: TtsFont; const aCharCode: WideChar; out aGlyphOrigin, aGlyphSize: TtsPosition; out aAdvance: Integer): Boolean;
  668. var
  669. GlyphIndex: Integer;
  670. font: TtsFontGDI;
  671. DC: HDC;
  672. Metric: TGlyphMetrics;
  673. Size: Cardinal;
  674. begin
  675. result := false;
  676. aGlyphOrigin.x := 0;
  677. aGlyphOrigin.x := 0;
  678. aGlyphSize.x := 0;
  679. aGlyphSize.y := 0;
  680. aAdvance := 0;
  681. font := ConvertFont(aFont);
  682. GlyphIndex := GetGlyphIndex(font, aCharCode);
  683. if (GlyphIndex < 0) then
  684. exit;
  685. DC := CreateCompatibleDC(0);
  686. try
  687. SelectObject(DC, font.fHandle);
  688. case font.Properties.AntiAliasing of
  689. tsAANone: begin
  690. Size := GetGlyphOutlineA(DC, GlyphIndex, GGO_BITMAP or GGO_GLYPH_INDEX, @Metric, 0, nil, @font.fMat2);
  691. end;
  692. tsAANormal: begin
  693. Size := GetGlyphOutlineA(DC, GlyphIndex, GGO_GRAY8_BITMAP or GGO_GLYPH_INDEX, @Metric, 0, nil, @font.fMat2);
  694. end;
  695. else
  696. Size := GDI_ERROR;
  697. end;
  698. if (Size = GDI_ERROR) then
  699. Size := GetGlyphOutlineA(DC, GlyphIndex, GGO_METRICS or GGO_GLYPH_INDEX, @Metric, 0, nil, @font.fMat2);
  700. if (Size <> GDI_ERROR) then begin
  701. aGlyphOrigin.x := Round(Metric.gmptGlyphOrigin.x / font.fMat2.eM11.value);
  702. aGlyphOrigin.y := Metric.gmptGlyphOrigin.y;
  703. aGlyphSize.x := Round(Metric.gmBlackBoxX / font.fMat2.eM11.value);
  704. aGlyphSize.y := Metric.gmBlackBoxY;
  705. aAdvance := Round(Metric.gmCellIncX / font.fMat2.eM11.value);
  706. result := true;
  707. end;
  708. finally
  709. DeleteDC(DC);
  710. end;
  711. end;
  712. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  713. procedure TtsFontGeneratorGDI.GetCharImage(const aFont: TtsFont; const aCharCode: WideChar; const aCharImage: TtsImage);
  714. var
  715. DC: HDC;
  716. font: TtsFontGDI;
  717. begin
  718. font := ConvertFont(aFont);
  719. DC := CreateCompatibleDC(0);
  720. try
  721. SelectObject(DC, font.fHandle);
  722. case font.Properties.AntiAliasing of
  723. tsAANone:
  724. GetCharImageAANone(DC, font, aCharCode, aCharImage);
  725. tsAANormal:
  726. GetCharImageAANormal(DC, font, aCharCode, aCharImage);
  727. end;
  728. finally
  729. DeleteDC(DC);
  730. end;
  731. end;
  732. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  733. function TtsFontGeneratorGDI.GetFontByName(const aFontname: String; const aRenderer: TtsRenderer;
  734. const aSize: Integer; const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont;
  735. var
  736. handle: THandle;
  737. prop: TtsFontProperties;
  738. begin
  739. handle := CreateFont(aFontname, aSize, aStyle, aAntiAliasing, prop);
  740. if (handle = 0) then
  741. raise EtsException.Create('unable to create font from name: ' + aFontname);
  742. result := TtsFontGDI.Create(aRenderer, self, prop, handle);
  743. end;
  744. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  745. function TtsFontGeneratorGDI.GetFontByFile(const aFilename: String; const aRenderer: TtsRenderer;
  746. const aSize: Integer; const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont;
  747. var
  748. reg: TtsFontRegistrationFile;
  749. handle: THandle;
  750. prop: TtsFontProperties;
  751. begin
  752. reg := TtsFontRegistrationFile.Create(aFilename);
  753. if not reg.IsRegistered then
  754. raise EtsException.Create('unable to register font file: ' + aFilename);
  755. handle := CreateFont(reg.Fontname, aSize, aStyle, aAntiAliasing, prop);
  756. if (handle = 0) then
  757. raise EtsException.Create('unable to create font from file: ' + aFilename);
  758. result := TtsRegistredFontGDI.Create(aRenderer, self, reg, prop, handle);
  759. end;
  760. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  761. function TtsFontGeneratorGDI.GetFontByStream(const aStream: TStream; const aRenderer: TtsRenderer;
  762. const aSize: Integer; const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont;
  763. var
  764. reg: TtsFontRegistrationStream;
  765. handle: THandle;
  766. prop: TtsFontProperties;
  767. begin
  768. reg := TtsFontRegistrationStream.Create(aStream);
  769. if not reg.IsRegistered then
  770. raise EtsException.Create('unable to register font from stream');
  771. handle := CreateFont(reg.Fontname, aSize, aStyle, aAntiAliasing, prop);
  772. if (handle = 0) then
  773. raise EtsException.Create('unable to create font from stream: ' + reg.Fontname);
  774. result := TtsRegistredFontGDI.Create(aRenderer, self, reg, prop, handle);
  775. end;
  776. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  777. constructor TtsFontGeneratorGDI.Create(const aContext: TtsContext);
  778. begin
  779. inherited Create(aContext);
  780. gdiCritSec.Enter;
  781. try
  782. inc(gdiRefCount, 1);
  783. if not gdiInitialized then
  784. InitGDI;
  785. finally
  786. gdiCritSec.Leave;
  787. end;
  788. end;
  789. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  790. destructor TtsFontGeneratorGDI.Destroy;
  791. begin
  792. inherited Destroy; // first free all fonts (managed by parent class)
  793. gdiCritSec.Enter;
  794. try
  795. dec(gdiRefCount, 1);
  796. if (gdiRefCount <= 0) then
  797. QuitGDI;
  798. finally
  799. gdiCritSec.Leave;
  800. end;
  801. end;
  802. initialization
  803. gdiRefCount := 0;
  804. gdiInitialized := false;
  805. gdiCritSec := TCriticalSection.Create;
  806. finalization
  807. if gdiInitialized then
  808. QuitGDI;
  809. FreeAndNil(gdiCritSec);
  810. end.