Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.

324 righe
7.9 KiB

  1. unit utsTtfUtils;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. Classes, SysUtils;
  6. const
  7. NAME_ID_COPYRIGHT = 0;
  8. NAME_ID_FACE_NAME = 1;
  9. NAME_ID_STYLE_NAME = 2;
  10. NAME_ID_FULL_NAME = 4;
  11. function MakeTTTableName(const ch1, ch2, ch3, ch4: Char): Cardinal;
  12. function GetTTString(pBuffer: Pointer; BufferSize: Integer; NameID, LanguageID: Cardinal; var Text: AnsiString): Boolean;
  13. function GetTTFontFullNameFromStream(Stream: TStream; LanguageID: Cardinal): AnsiString;
  14. function GetTTFontFullNameFromFile(Filename: AnsiString; LanguageID: Cardinal): AnsiString;
  15. implementation
  16. uses
  17. utsUtils;
  18. type
  19. TT_OFFSET_TABLE = packed record
  20. uMajorVersion: Word;
  21. uMinorVersion: Word;
  22. uNumOfTables: Word;
  23. uSearchRange: Word;
  24. uEntrySelector: Word;
  25. uRangeShift: Word;
  26. end;
  27. TT_TABLE_DIRECTORY = packed record
  28. TableName: Cardinal; // table name
  29. uCheckSum: Cardinal; // Check sum
  30. uOffset: Cardinal; // Offset from beginning of file
  31. uLength: Cardinal; // length of the table in bytes
  32. end;
  33. TT_NAME_TABLE_HEADER = packed record
  34. uFSelector: Word; //format selector. Always 0
  35. uNRCount: Word; //Name Records count
  36. uStorageOffset: Word; //Offset for strings storage, from start of the table
  37. end;
  38. TT_NAME_RECORD = packed record
  39. uPlatformID: Word;
  40. uEncodingID: Word;
  41. uLanguageID: Word;
  42. uNameID: Word;
  43. uStringLength: Word;
  44. uStringOffset: Word; //from start of storage area
  45. end;
  46. const
  47. PLATFORM_ID_APPLE_UNICODE = 0;
  48. PLATFORM_ID_MACINTOSH = 1;
  49. PLATFORM_ID_MICROSOFT = 3;
  50. function SWAPWORD(x: Word): Word;
  51. begin
  52. Result := x and $FF;
  53. Result := Result shl 8;
  54. Result := Result or (x shr 8);
  55. end;
  56. function SWAPLONG(x: Cardinal): Cardinal;
  57. begin
  58. Result := (x and $FF) shl 24;
  59. x := x shr 8;
  60. Result := Result or ((x and $FF) shl 16);
  61. x := x shr 8;
  62. Result := Result or ((x and $FF) shl 8);
  63. x := x shr 8;
  64. Result := Result or x;
  65. end;
  66. function GetTTTableData(Stream: TStream; TableName: Cardinal; pBuff: Pointer; var Size: Integer): Boolean;
  67. var
  68. Pos: Int64;
  69. OffsetTable: TT_OFFSET_TABLE;
  70. TableDir: TT_TABLE_DIRECTORY;
  71. Idx: Integer;
  72. begin
  73. Result := False;
  74. Pos := Stream.Position;
  75. // Reading table header
  76. Stream.Read(OffsetTable, sizeof(TT_OFFSET_TABLE));
  77. OffsetTable.uNumOfTables := SWAPWORD(OffsetTable.uNumOfTables);
  78. OffsetTable.uMajorVersion := SWAPWORD(OffsetTable.uMajorVersion);
  79. OffsetTable.uMinorVersion := SWAPWORD(OffsetTable.uMinorVersion);
  80. //check is this is a true type font and the version is 1.0
  81. if (OffsetTable.uMajorVersion <> 1) or (OffsetTable.uMinorVersion <> 0) then
  82. Exit;
  83. // seaching table with name
  84. for Idx := 0 to OffsetTable.uNumOfTables -1 do begin
  85. Stream.Read(TableDir, sizeof(TT_TABLE_DIRECTORY));
  86. if (TableName = TableDir.TableName) then begin
  87. TableDir.uOffset := SWAPLONG(TableDir.uOffset);
  88. TableDir.uLength := SWAPLONG(TableDir.uLength);
  89. // copying tabledata
  90. if (pBuff <> nil) and (Size >= Integer(TableDir.uLength)) then begin
  91. Stream.Seek(TableDir.uOffset, soBeginning);
  92. Size := Stream.Read(pBuff^, TableDir.uLength);
  93. Result := Size = Integer(TableDir.uLength);
  94. end else
  95. begin
  96. // restoring streamposition
  97. Stream.Position := Pos;
  98. Size := TableDir.uLength;
  99. Result := True;
  100. end;
  101. break;
  102. end;
  103. end;
  104. end;
  105. function MakeTTTableName(const ch1, ch2, ch3, ch4: Char): Cardinal;
  106. begin
  107. Result := ord(ch4) shl 24 or ord(ch3) shl 16 or ord(ch2) shl 8 or ord(ch1);
  108. end;
  109. function GetTTString(pBuffer: Pointer; BufferSize: Integer; NameID, LanguageID: Cardinal; var Text: AnsiString): Boolean;
  110. var
  111. pActBuffer: pByte;
  112. ttNTHeader: TT_NAME_TABLE_HEADER;
  113. ttRecord: TT_NAME_RECORD;
  114. Idx: Integer;
  115. Prio: Integer;
  116. procedure ExtractName;
  117. var
  118. pTempBuffer: pByte;
  119. pTemp: pWideChar;
  120. uStringLengthH2: Word;
  121. procedure SwapText(pText: pWideChar; Length: Word);
  122. begin
  123. while Length > 0 do begin
  124. pWord(pText)^ := SWAPWORD(pWord(pText)^);
  125. Inc(pText);
  126. Dec(Length);
  127. end;
  128. end;
  129. begin
  130. Result := True;
  131. ttRecord.uStringLength := SWAPWORD(ttRecord.uStringLength);
  132. ttRecord.uStringOffset := SWAPWORD(ttRecord.uStringOffset);
  133. uStringLengthH2 := ttRecord.uStringLength shr 1;
  134. pTempBuffer := pBuffer;
  135. Inc(pTempBuffer, ttNTHeader.uStorageOffset + ttRecord.uStringOffset);
  136. // Unicode
  137. if ((ttRecord.uPlatformID = PLATFORM_ID_MICROSOFT) and (ttRecord.uEncodingID in [0, 1])) or
  138. ((ttRecord.uPlatformID = PLATFORM_ID_APPLE_UNICODE) and (ttRecord.uEncodingID > 0)) then begin
  139. pTemp := tsStrAlloc(uStringLengthH2);
  140. try
  141. // uStringLengthH2 * 2 because possible buffer overrun
  142. Move(pTempBuffer^, pTemp^, uStringLengthH2 * 2);
  143. SwapText(pTemp, uStringLengthH2);
  144. WideCharLenToStrVar(pTemp, uStringLengthH2, Text);
  145. finally
  146. tsStrDispose(pTemp);
  147. end;
  148. end else
  149. // none unicode
  150. begin
  151. SetLength(Text, ttRecord.uStringLength);
  152. Move(pTempBuffer^, Text[1], ttRecord.uStringLength);
  153. end;
  154. end;
  155. begin
  156. Result := False;
  157. pActBuffer := pBuffer;
  158. Move(pActBuffer^, ttNTHeader, sizeof(TT_NAME_TABLE_HEADER));
  159. inc(pActBuffer, sizeof(TT_NAME_TABLE_HEADER));
  160. ttNTHeader.uNRCount := SWAPWORD(ttNTHeader.uNRCount);
  161. ttNTHeader.uStorageOffset := SWAPWORD(ttNTHeader.uStorageOffset);
  162. Prio := -1;
  163. for Idx := 0 to ttNTHeader.uNRCount -1 do begin
  164. Move(pActBuffer^, ttRecord, sizeof(TT_NAME_RECORD));
  165. Inc(pActBuffer, sizeof(TT_NAME_RECORD));
  166. ttRecord.uNameID := SWAPWORD(ttRecord.uNameID);
  167. if ttRecord.uNameID = NameID then begin
  168. ttRecord.uPlatformID := SWAPWORD(ttRecord.uPlatformID);
  169. ttRecord.uEncodingID := SWAPWORD(ttRecord.uEncodingID);
  170. ttRecord.uLanguageID := SWAPWORD(ttRecord.uLanguageID);
  171. // highest priority
  172. if (ttRecord.uPlatformID = PLATFORM_ID_MICROSOFT) then begin
  173. // system language
  174. if (ttRecord.uLanguageID = languageID) then begin
  175. if Prio <= 7 then begin
  176. ExtractName;
  177. Prio := 7;
  178. end;
  179. end else
  180. // english
  181. if (ttRecord.uLanguageID = 1033) then begin
  182. if Prio <= 6 then begin
  183. ExtractName;
  184. Prio := 6;
  185. end;
  186. end else
  187. // all else
  188. if Prio <= 5 then begin
  189. ExtractName;
  190. Prio := 5;
  191. end;
  192. end else
  193. // apple unicode
  194. if (ttRecord.uPlatformID = PLATFORM_ID_APPLE_UNICODE) then begin
  195. ExtractName;
  196. Prio := 4;
  197. end else
  198. // macintosh
  199. if (ttRecord.uPlatformID = PLATFORM_ID_MACINTOSH) then begin
  200. // english
  201. if (ttRecord.uLanguageID = 0) then begin
  202. if Prio <= 3 then begin
  203. ExtractName;
  204. Prio := 3;
  205. end;
  206. end else
  207. // all other
  208. begin
  209. ExtractName;
  210. Prio := 2;
  211. end;
  212. end else
  213. begin
  214. if Prio <= 1 then begin
  215. ExtractName;
  216. Prio := 1;
  217. end;
  218. end;
  219. end;
  220. end;
  221. end;
  222. function GetTTFontFullNameFromStream(Stream: TStream; LanguageID: Cardinal): AnsiString;
  223. var
  224. TableName: Cardinal;
  225. Buffer: Pointer;
  226. BufferSize: Integer;
  227. begin
  228. TableName := MakeTTTableName('n', 'a', 'm', 'e');
  229. if GetTTTableData(Stream, TableName, nil, BufferSize) then begin
  230. GetMem(Buffer, BufferSize);
  231. try
  232. if GetTTTableData(Stream, TableName, Buffer, BufferSize) then begin
  233. if not GetTTString(Buffer, BufferSize, NAME_ID_FULL_NAME, LanguageID, Result) then
  234. if not GetTTString(Buffer, BufferSize, NAME_ID_FACE_NAME, LanguageID, Result) then
  235. Result := '';
  236. end;
  237. finally
  238. FreeMem(Buffer);
  239. end;
  240. end;
  241. end;
  242. function GetTTFontFullNameFromFile(Filename: AnsiString; LanguageID: Cardinal): AnsiString;
  243. var
  244. fs: TFileStream;
  245. begin
  246. fs := TFileStream.Create(String(Filename), fmOpenRead or fmShareDenyWrite);
  247. try
  248. result := GetTTFontFullNameFromStream(fs, LanguageID);
  249. finally
  250. fs.Free;
  251. end;
  252. end;
  253. end.