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.

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