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

368 строки
8.6 KiB

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