Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

410 lignes
14 KiB

  1. unit utsFontCreatorFreeType;
  2. {$IFDEF FPC}
  3. {$mode delphi}{$H+}
  4. {$ENDIF}
  5. interface
  6. uses
  7. Classes, SysUtils,
  8. utsTextSuite, utsTypes, utsFreeType;
  9. type
  10. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  11. TtsFreeTypeFaceHandle = class
  12. private
  13. fFace: FT_Face;
  14. public
  15. constructor Create(const aFace: FT_Face);
  16. destructor Destroy; override;
  17. end;
  18. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  19. TtsFontFreeType = class(TtsFont)
  20. private
  21. fHandle: TtsFreeTypeFaceHandle;
  22. public
  23. constructor Create(const aHandle: TtsFreeTypeFaceHandle; const aRenderer: TtsRenderer;
  24. const aGenerator: TtsFontGenerator; const aProperties: TtsFontProperties);
  25. destructor Destroy; override;
  26. end;
  27. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  28. TtsFontGeneratorFreeType = class(TtsFontGenerator)
  29. private
  30. fHandle: FT_Library;
  31. function ConvertFont(const aFont: TtsFont): TtsFontFreeType;
  32. procedure LoadNames(const aFace: FT_Face; var aProperties: TtsFontProperties);
  33. function CreateFont(const aFace: FT_Face; const aRenderer: TtsRenderer; const aSize: Integer;
  34. const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont;
  35. protected
  36. function GetGlyphMetrics(const aFont: TtsFont; const aCharCode: WideChar;
  37. out aGlyphOrigin, aGlyphSize: TtsPosition; out aAdvance: Integer): Boolean; override;
  38. procedure GetCharImage(const aFont: TtsFont; const aCharCode: WideChar;
  39. const aCharImage: TtsImage); override;
  40. public
  41. function GetFontByFile(const aFilename: String; const aRenderer: TtsRenderer; const aSize: Integer;
  42. const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont; overload;
  43. function GetFontByStream(const aStream: TStream; const aRenderer: TtsRenderer; const aSize: Integer;
  44. const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont; overload;
  45. constructor Create(const aContext: TtsContext);
  46. destructor Destroy; override;
  47. end;
  48. implementation
  49. uses
  50. utsUtils, math;
  51. const
  52. FT_SIZE_FACTOR = 64;
  53. FT_SIZE_RES = 72; //dpi
  54. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  55. //TtsFreeTypeFaceHandle/////////////////////////////////////////////////////////////////////////////////////////////////
  56. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  57. constructor TtsFreeTypeFaceHandle.Create(const aFace: FT_Face);
  58. begin
  59. inherited Create;
  60. fFace := aFace;
  61. end;
  62. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  63. destructor TtsFreeTypeFaceHandle.Destroy;
  64. begin
  65. FT_Done_Face(fFace);
  66. inherited Destroy;
  67. end;
  68. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  69. //TtsFontFreeType///////////////////////////////////////////////////////////////////////////////////////////////////////
  70. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  71. constructor TtsFontFreeType.Create(const aHandle: TtsFreeTypeFaceHandle; const aRenderer: TtsRenderer;
  72. const aGenerator: TtsFontGenerator; const aProperties: TtsFontProperties);
  73. begin
  74. inherited Create(aRenderer, aGenerator, aProperties);
  75. fHandle := aHandle;
  76. end;
  77. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  78. destructor TtsFontFreeType.Destroy;
  79. begin
  80. FreeAndNil(fHandle);
  81. inherited Destroy;
  82. end;
  83. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  84. //TtsFontGeneratorFreeType//////////////////////////////////////////////////////////////////////////////////////////////
  85. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  86. function TtsFontGeneratorFreeType.ConvertFont(const aFont: TtsFont): TtsFontFreeType;
  87. begin
  88. if not (aFont is TtsFontFreeType) then
  89. raise EtsException.Create('aFont need to be a TtsFontGDI object');
  90. result := (aFont as TtsFontFreeType);
  91. end;
  92. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  93. procedure TtsFontGeneratorFreeType.LoadNames(const aFace: FT_Face; var aProperties: TtsFontProperties);
  94. var
  95. i, cnt: FT_Int;
  96. err: FT_Error;
  97. name: FT_SfntName;
  98. function DecodeAnsi(const aCodePage: TtsCodePage): String;
  99. var
  100. tmp: WideString;
  101. len: Integer;
  102. begin
  103. SetLength(tmp, name.string_len);
  104. len := tsAnsiSBCDToWide(@tmp[1], name.string_len, PAnsiChar(name.string_), aCodePage, '?');
  105. SetLength(tmp, len);
  106. result := UTF8Encode(tmp);
  107. end;
  108. function Decode: String;
  109. var
  110. tmp: WideString;
  111. len: Integer;
  112. begin
  113. result := '';
  114. case name.platform_id of
  115. TT_PLATFORM_APPLE_UNICODE: begin
  116. case name.encoding_id of
  117. TT_APPLE_ID_DEFAULT,
  118. TT_APPLE_ID_UNICODE_1_1,
  119. TT_APPLE_ID_UNICODE_2_0: begin
  120. SetLength(tmp, name.string_len);
  121. len := tsUTFBE16ToWide(@tmp[1], name.string_len, name.string_, name.string_len, '?');
  122. SetLength(tmp, len);
  123. result := UTF8Encode(tmp);
  124. end;
  125. end;
  126. end;
  127. TT_PLATFORM_ISO: begin
  128. case name.encoding_id of
  129. TT_ISO_ID_8859_1:
  130. result := DecodeAnsi(tsISO_8859_1);
  131. end;
  132. end;
  133. end;
  134. end;
  135. begin
  136. cnt := FT_Get_Sfnt_Name_Count(aFace);
  137. for i := 0 to cnt-1 do begin
  138. err := FT_Get_Sfnt_Name(aFace, i, @name);
  139. if (err <> 0) then
  140. continue;
  141. case name.name_id of
  142. TT_NAME_ID_COPYRIGHT:
  143. if (aProperties.Copyright = '') then
  144. aProperties.Copyright := Decode;
  145. TT_NAME_ID_FONT_FAMILY:
  146. if (aProperties.Fontname = '') then
  147. aProperties.Fontname := Decode;
  148. TT_NAME_ID_FULL_NAME:
  149. if (aProperties.FullName = '') then
  150. aProperties.FullName := Decode;
  151. end;
  152. end;
  153. end;
  154. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  155. function TtsFontGeneratorFreeType.CreateFont(const aFace: FT_Face; const aRenderer: TtsRenderer; const aSize: Integer;
  156. const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont;
  157. var
  158. err: FT_Error;
  159. prop: TtsFontProperties;
  160. os2: PTT_OS2;
  161. hz: PTT_HoriHeader;
  162. begin
  163. err := FT_Set_Char_Size(aFace, 0, aSize * FT_SIZE_FACTOR, FT_SIZE_RES, FT_SIZE_RES);
  164. if (err <> 0) then
  165. raise EtsException.Create('unable to set char size: error=' + IntToStr(err));
  166. FillByte(prop{%H-}, SizeOf(prop), 0);
  167. prop.AntiAliasing := tsAANormal;
  168. prop.FaceName := aFace^.family_name;
  169. prop.StyleName := aFace^.style_name;
  170. LoadNames(aFace, prop);
  171. prop.Size := aSize;
  172. prop.AntiAliasing := aAntiAliasing;
  173. prop.DefaultChar := '?';
  174. prop.Style := aStyle + [tsStyleBold, tsStyleItalic];
  175. if ((aFace^.style_flags and FT_STYLE_FLAG_BOLD) = 0) then
  176. Exclude(prop.Style, tsStyleBold);
  177. if ((aFace^.style_flags and FT_STYLE_FLAG_ITALIC) = 0) then
  178. Exclude(prop.Style, tsStyleItalic);
  179. prop.Ascent := aFace^.size^.metrics.ascender div FT_SIZE_FACTOR;
  180. prop.Descent := -aFace^.size^.metrics.descender div FT_SIZE_FACTOR;
  181. prop.ExternalLeading := 0;
  182. prop.BaseLineOffset := 0;
  183. prop.UnderlinePos := aFace^.underline_position div FT_SIZE_FACTOR;
  184. prop.UnderlineSize := aFace^.underline_thickness div FT_SIZE_FACTOR;
  185. os2 := PTT_OS2(FT_Get_Sfnt_Table(aFace, FT_SFNT_OS2));
  186. if Assigned(os2) and (os2^.version <> $FFFF) then begin
  187. prop.StrikeoutPos := os2^.yStrikeoutPosition div FT_SIZE_FACTOR;
  188. prop.StrikeoutSize := os2^.yStrikeoutSize div FT_SIZE_FACTOR;
  189. end;
  190. hz := PTT_HoriHeader(FT_Get_Sfnt_Table(aFace, FT_SFNT_HHEA));
  191. if Assigned(hz) then begin
  192. prop.ExternalLeading := hz^.Line_Gap div FT_SIZE_FACTOR;
  193. end;
  194. result := TtsFontFreeType.Create(TtsFreeTypeFaceHandle.Create(aFace), aRenderer, self, prop);
  195. end;
  196. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  197. function TtsFontGeneratorFreeType.GetGlyphMetrics(const aFont: TtsFont; const aCharCode: WideChar; out aGlyphOrigin, aGlyphSize: TtsPosition; out aAdvance: Integer): Boolean;
  198. var
  199. font: TtsFontFreeType;
  200. err: FT_Error;
  201. begin
  202. result := false;
  203. aGlyphOrigin.x := 0;
  204. aGlyphOrigin.x := 0;
  205. aGlyphSize.x := 0;
  206. aGlyphSize.y := 0;
  207. aAdvance := 0;
  208. font := ConvertFont(aFont);
  209. case font.Properties.AntiAliasing of
  210. tsAANormal:
  211. err := FT_Load_Char(font.fHandle.fFace, Ord(aCharCode), FT_LOAD_DEFAULT);
  212. tsAANone:
  213. err := FT_Load_Char(font.fHandle.fFace, Ord(aCharCode), FT_LOAD_MONOCHROME);
  214. else
  215. raise EtsException.Create('unknown anti aliasing');
  216. end;
  217. case err of
  218. FT_ERR_None:
  219. { nop };
  220. FT_ERR_Invalid_Character_Code:
  221. exit;
  222. else
  223. raise EtsException.Create('unable to set glyph metrix: error=' + IntToStr(err));
  224. end;
  225. result := true;
  226. with font.fHandle.fFace^.glyph^.metrics do begin
  227. aAdvance := horiAdvance div FT_SIZE_FACTOR;
  228. aGlyphOrigin.x := horiBearingX div FT_SIZE_FACTOR;
  229. aGlyphOrigin.y := horiBearingY div FT_SIZE_FACTOR;
  230. aGlyphSize.x := width div FT_SIZE_FACTOR;
  231. aGlyphSize.y := height div FT_SIZE_FACTOR;
  232. end;
  233. end;
  234. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  235. procedure TtsFontGeneratorFreeType.GetCharImage(const aFont: TtsFont; const aCharCode: WideChar; const aCharImage: TtsImage);
  236. var
  237. font: TtsFontFreeType;
  238. err: FT_Error;
  239. g: FT_GlyphSlot;
  240. b: PFT_Bitmap;
  241. procedure CopyGray;
  242. var
  243. x, y: Integer;
  244. src, dst: PByte;
  245. c: TtsColor4f;
  246. begin
  247. aCharImage.CreateEmpty(font.Renderer.Format, b^.width, b^.rows);
  248. c := tsColor4f(1, 1, 1, 1);
  249. for y := 0 to b^.rows-1 do begin
  250. src := b^.buffer + y * b^.pitch;
  251. dst := aCharImage.Scanline[y];
  252. for x := 0 to b^.width-1 do begin
  253. c.a := src^ / $FF;
  254. inc(src, 1);
  255. tsFormatMap(aCharImage.Format, dst, c);
  256. end;
  257. end;
  258. end;
  259. procedure CopyMono;
  260. var
  261. x, y, i, cnt: Integer;
  262. src, dst: PByte;
  263. tmp: Byte;
  264. c: TtsColor4f;
  265. begin
  266. aCharImage.CreateEmpty(font.Renderer.Format, b^.width, b^.rows);
  267. c := tsColor4f(1, 1, 1, 1);
  268. for y := 0 to b^.rows-1 do begin
  269. src := b^.buffer + y * b^.pitch;
  270. dst := aCharImage.Scanline[y];
  271. x := b^.width;
  272. while (x > 0) do begin
  273. cnt := min(8, x);
  274. tmp := src^;
  275. inc(src, 1);
  276. for i := 1 to cnt do begin
  277. if ((tmp and $80) > 0) then
  278. c.a := 1.0
  279. else
  280. c.a := 0.0;
  281. tmp := (tmp and not $80) shl 1;
  282. tsFormatMap(aCharImage.Format, dst, c);
  283. end;
  284. dec(x, cnt);
  285. end;
  286. end;
  287. end;
  288. begin
  289. font := ConvertFont(aFont);
  290. g := font.fHandle.fFace^.glyph;
  291. if not (font.Properties.AntiAliasing in [tsAANormal, tsAANone]) then
  292. raise Exception.Create('unknown anti aliasing');
  293. case font.Properties.AntiAliasing of
  294. tsAANormal:
  295. err := FT_Load_Char(font.fHandle.fFace, Ord(aCharCode), FT_LOAD_DEFAULT or FT_LOAD_RENDER);
  296. tsAANone:
  297. err := FT_Load_Char(font.fHandle.fFace, Ord(aCharCode), FT_LOAD_MONOCHROME or FT_LOAD_TARGET_MONO or FT_LOAD_RENDER);
  298. end;
  299. if (err <> 0) then
  300. raise EtsException.Create('unable to set glyph metrix: error=' + IntToStr(err));
  301. if (g^.format <> FT_GLYPH_FORMAT_BITMAP) then
  302. raise EtsException.Create('invalid glyph format');
  303. b := @g^.bitmap;
  304. case b^.pixel_mode of
  305. FT_PIXEL_MODE_MONO:
  306. CopyMono;
  307. FT_PIXEL_MODE_GRAY:
  308. CopyGray;
  309. else
  310. raise EtsException.Create('unknown glyph bitmap format');
  311. end;
  312. end;
  313. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  314. function TtsFontGeneratorFreeType.GetFontByFile(const aFilename: String; const aRenderer: TtsRenderer;
  315. const aSize: Integer; const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont;
  316. var
  317. face: FT_Face;
  318. err: FT_Error;
  319. begin
  320. err := FT_New_Face(fHandle, PAnsiChar(aFilename), 0, @face);
  321. if (err <> 0) then
  322. raise EtsException.Create('unable to create free type face from file: ' + aFilename + ' error=' + IntToStr(err));
  323. result := CreateFont(face, aRenderer, aSize, aStyle, aAntiAliasing);
  324. end;
  325. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  326. function TtsFontGeneratorFreeType.GetFontByStream(const aStream: TStream; const aRenderer: TtsRenderer;
  327. const aSize: Integer; const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont;
  328. var
  329. face: FT_Face;
  330. err: FT_Error;
  331. ms: TMemoryStream;
  332. begin
  333. if (aStream is TMemoryStream) then begin
  334. ms := (aStream as TMemoryStream);
  335. err := FT_New_Memory_Face(fHandle, PByte(ms.Memory) + ms.Position, ms.Size - ms.Position, 0, @face);
  336. end else begin
  337. ms := TMemoryStream.Create;
  338. try
  339. ms.CopyFrom(aStream, aStream.Size - aStream.Position);
  340. err := FT_New_Memory_Face(fHandle, PByte(ms.Memory), ms.Size, 0, @face);
  341. finally
  342. FreeAndNil(ms);
  343. end;
  344. end;
  345. if (err <> 0) then
  346. raise EtsException.Create('unable to create free type face from stream: error=' + IntToStr(err));
  347. result := CreateFont(face, aRenderer, aSize, aStyle, aAntiAliasing);
  348. end;
  349. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  350. constructor TtsFontGeneratorFreeType.Create(const aContext: TtsContext);
  351. begin
  352. inherited Create(aContext);
  353. fHandle := InitFreeType;
  354. end;
  355. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  356. destructor TtsFontGeneratorFreeType.Destroy;
  357. begin
  358. inherited Destroy; // first call interited
  359. QuitFreeType; // QuitFreeType will free callpacks
  360. end;
  361. end.