You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

399 lines
13 KiB

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