Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

409 rindas
14 KiB

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