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

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