Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

411 строки
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. face: FT_Face;
  159. err: FT_Error;
  160. prop: TtsFontProperties;
  161. os2: PTT_OS2;
  162. hz: PTT_HoriHeader;
  163. begin
  164. err := FT_Set_Char_Size(aFace, 0, aSize * FT_SIZE_FACTOR, FT_SIZE_RES, FT_SIZE_RES);
  165. if (err <> 0) then
  166. raise EtsException.Create('unable to set char size: error=' + IntToStr(err));
  167. FillByte(prop{%H-}, SizeOf(prop), 0);
  168. prop.AntiAliasing := tsAANormal;
  169. prop.FaceName := aFace^.family_name;
  170. prop.StyleName := aFace^.style_name;
  171. LoadNames(aFace, prop);
  172. prop.Size := aSize;
  173. prop.AntiAliasing := aAntiAliasing;
  174. prop.DefaultChar := '?';
  175. prop.Style := aStyle + [tsStyleBold, tsStyleItalic];
  176. if ((aFace^.style_flags and FT_STYLE_FLAG_BOLD) = 0) then
  177. Exclude(prop.Style, tsStyleBold);
  178. if ((aFace^.style_flags and FT_STYLE_FLAG_ITALIC) = 0) then
  179. Exclude(prop.Style, tsStyleItalic);
  180. prop.Ascent := aFace^.size^.metrics.ascender div FT_SIZE_FACTOR;
  181. prop.Descent := -aFace^.size^.metrics.descender div FT_SIZE_FACTOR;
  182. prop.ExternalLeading := 0;
  183. prop.BaseLineOffset := 0;
  184. prop.UnderlinePos := aFace^.underline_position div FT_SIZE_FACTOR;
  185. prop.UnderlineSize := aFace^.underline_thickness div FT_SIZE_FACTOR;
  186. os2 := PTT_OS2(FT_Get_Sfnt_Table(aFace, FT_SFNT_OS2));
  187. if Assigned(os2) and (os2^.version <> $FFFF) then begin
  188. prop.StrikeoutPos := os2^.yStrikeoutPosition div FT_SIZE_FACTOR;
  189. prop.StrikeoutSize := os2^.yStrikeoutSize div FT_SIZE_FACTOR;
  190. end;
  191. hz := PTT_HoriHeader(FT_Get_Sfnt_Table(aFace, FT_SFNT_HHEA));
  192. if Assigned(hz) then begin
  193. prop.ExternalLeading := hz^.Line_Gap div FT_SIZE_FACTOR;
  194. end;
  195. result := TtsFontFreeType.Create(TtsFreeTypeFaceHandle.Create(aFace), aRenderer, self, prop);
  196. end;
  197. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  198. function TtsFontGeneratorFreeType.GetGlyphMetrics(const aFont: TtsFont; const aCharCode: WideChar; out aGlyphOrigin, aGlyphSize: TtsPosition; out aAdvance: Integer): Boolean;
  199. var
  200. font: TtsFontFreeType;
  201. err: FT_Error;
  202. begin
  203. result := false;
  204. aGlyphOrigin.x := 0;
  205. aGlyphOrigin.x := 0;
  206. aGlyphSize.x := 0;
  207. aGlyphSize.y := 0;
  208. aAdvance := 0;
  209. font := ConvertFont(aFont);
  210. case font.Properties.AntiAliasing of
  211. tsAANormal:
  212. err := FT_Load_Char(font.fHandle.fFace, Ord(aCharCode), FT_LOAD_DEFAULT);
  213. tsAANone:
  214. err := FT_Load_Char(font.fHandle.fFace, Ord(aCharCode), FT_LOAD_MONOCHROME);
  215. else
  216. raise EtsException.Create('unknown anti aliasing');
  217. end;
  218. case err of
  219. FT_ERR_None:
  220. { nop };
  221. FT_ERR_Invalid_Character_Code:
  222. exit;
  223. else
  224. raise EtsException.Create('unable to set glyph metrix: error=' + IntToStr(err));
  225. end;
  226. result := true;
  227. with font.fHandle.fFace^.glyph^.metrics do begin
  228. aAdvance := horiAdvance div FT_SIZE_FACTOR;
  229. aGlyphOrigin.x := horiBearingX div FT_SIZE_FACTOR;
  230. aGlyphOrigin.y := horiBearingY div FT_SIZE_FACTOR;
  231. aGlyphSize.x := width div FT_SIZE_FACTOR;
  232. aGlyphSize.y := height div FT_SIZE_FACTOR;
  233. end;
  234. end;
  235. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  236. procedure TtsFontGeneratorFreeType.GetCharImage(const aFont: TtsFont; const aCharCode: WideChar; const aCharImage: TtsImage);
  237. var
  238. font: TtsFontFreeType;
  239. err: FT_Error;
  240. g: FT_GlyphSlot;
  241. b: PFT_Bitmap;
  242. procedure CopyGray;
  243. var
  244. x, y: Integer;
  245. src, dst: PByte;
  246. c: TtsColor4f;
  247. begin
  248. aCharImage.CreateEmpty(font.Renderer.Format, b^.width, b^.rows);
  249. c := tsColor4f(1, 1, 1, 1);
  250. for y := 0 to b^.rows-1 do begin
  251. src := b^.buffer + y * b^.pitch;
  252. dst := aCharImage.Scanline[y];
  253. for x := 0 to b^.width-1 do begin
  254. c.a := src^ / $FF;
  255. inc(src, 1);
  256. tsFormatMap(aCharImage.Format, dst, c);
  257. end;
  258. end;
  259. end;
  260. procedure CopyMono;
  261. var
  262. x, y, i, cnt: Integer;
  263. src, dst: PByte;
  264. tmp: Byte;
  265. c: TtsColor4f;
  266. begin
  267. aCharImage.CreateEmpty(font.Renderer.Format, b^.width, b^.rows);
  268. c := tsColor4f(1, 1, 1, 1);
  269. for y := 0 to b^.rows-1 do begin
  270. src := b^.buffer + y * b^.pitch;
  271. dst := aCharImage.Scanline[y];
  272. x := b^.width;
  273. while (x > 0) do begin
  274. cnt := min(8, x);
  275. tmp := src^;
  276. inc(src, 1);
  277. for i := 1 to cnt do begin
  278. if ((tmp and $80) > 0) then
  279. c.a := 1.0
  280. else
  281. c.a := 0.0;
  282. tmp := (tmp and not $80) shl 1;
  283. tsFormatMap(aCharImage.Format, dst, c);
  284. end;
  285. dec(x, cnt);
  286. end;
  287. end;
  288. end;
  289. begin
  290. font := ConvertFont(aFont);
  291. g := font.fHandle.fFace^.glyph;
  292. if not (font.Properties.AntiAliasing in [tsAANormal, tsAANone]) then
  293. raise Exception.Create('unknown anti aliasing');
  294. case font.Properties.AntiAliasing of
  295. tsAANormal:
  296. err := FT_Load_Char(font.fHandle.fFace, Ord(aCharCode), FT_LOAD_DEFAULT or FT_LOAD_RENDER);
  297. tsAANone:
  298. err := FT_Load_Char(font.fHandle.fFace, Ord(aCharCode), FT_LOAD_MONOCHROME or FT_LOAD_TARGET_MONO or FT_LOAD_RENDER);
  299. end;
  300. if (err <> 0) then
  301. raise EtsException.Create('unable to set glyph metrix: error=' + IntToStr(err));
  302. if (g^.format <> FT_GLYPH_FORMAT_BITMAP) then
  303. raise EtsException.Create('invalid glyph format');
  304. b := @g^.bitmap;
  305. case b^.pixel_mode of
  306. FT_PIXEL_MODE_MONO:
  307. CopyMono;
  308. FT_PIXEL_MODE_GRAY:
  309. CopyGray;
  310. else
  311. raise EtsException.Create('unknown glyph bitmap format');
  312. end;
  313. end;
  314. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  315. function TtsFontGeneratorFreeType.GetFontByFile(const aFilename: String; const aRenderer: TtsRenderer;
  316. const aSize: Integer; const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont;
  317. var
  318. face: FT_Face;
  319. err: FT_Error;
  320. begin
  321. err := FT_New_Face(fHandle, PAnsiChar(aFilename), 0, @face);
  322. if (err <> 0) then
  323. raise EtsException.Create('unable to create free type face from file: ' + aFilename + ' error=' + IntToStr(err));
  324. result := CreateFont(face, aRenderer, aSize, aStyle, aAntiAliasing);
  325. end;
  326. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  327. function TtsFontGeneratorFreeType.GetFontByStream(const aStream: TStream; const aRenderer: TtsRenderer;
  328. const aSize: Integer; const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont;
  329. var
  330. face: FT_Face;
  331. err: FT_Error;
  332. ms: TMemoryStream;
  333. begin
  334. if (aStream is TMemoryStream) then begin
  335. ms := (aStream as TMemoryStream);
  336. err := FT_New_Memory_Face(fHandle, PByte(ms.Memory) + ms.Position, ms.Size - ms.Position, 0, @face);
  337. end else begin
  338. ms := TMemoryStream.Create;
  339. try
  340. ms.CopyFrom(aStream, aStream.Size - aStream.Position);
  341. err := FT_New_Memory_Face(fHandle, PByte(ms.Memory), ms.Size, 0, @face);
  342. finally
  343. FreeAndNil(ms);
  344. end;
  345. end;
  346. if (err <> 0) then
  347. raise EtsException.Create('unable to create free type face from stream: error=' + IntToStr(err));
  348. result := CreateFont(face, aRenderer, aSize, aStyle, aAntiAliasing);
  349. end;
  350. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  351. constructor TtsFontGeneratorFreeType.Create(const aContext: TtsContext);
  352. begin
  353. inherited Create(aContext);
  354. fHandle := InitFreeType;
  355. end;
  356. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  357. destructor TtsFontGeneratorFreeType.Destroy;
  358. begin
  359. inherited Destroy; // first call interited
  360. QuitFreeType; // QuitFreeType will free callpacks
  361. end;
  362. end.