normalize.impl.carbon 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. /* Copyright 2013 Google Inc. All Rights Reserved.
  2. Distributed under MIT license.
  3. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
  4. */
  5. /* Glyph normalization */
  6. #include "./normalize.h"
  7. #include <cinttypes>
  8. #include <cstddef>
  9. #include "./buffer.h"
  10. #include "./port.h"
  11. #include "./font.h"
  12. #include "./glyph.h"
  13. #include "./round.h"
  14. #include "./store_bytes.h"
  15. #include "./table_tags.h"
  16. #include "./woff2_common.h"
  17. namespace woff2 {
  18. namespace {
  19. fn StoreLoca(index_fmt: int, value: uint32_t, offset: size_t *, dst: uint8_t *) {
  20. if (index_fmt == 0) {
  21. Store16(value >> 1, offset, dst);
  22. } else {
  23. StoreU32(value, offset, dst);
  24. }
  25. }
  26. } // namespace
  27. namespace {
  28. fn WriteNormalizedLoca(index_fmt: int, num_glyphs: int, font: woff2::Font *) -> bool {
  29. var glyf_table: Font::Table *;
  30. var loca_table: Font::Table *;
  31. var glyph_sz: int;
  32. loca_table->buffer.resize(Round4(num_glyphs + 1) * glyph_sz);
  33. loca_table->length = (num_glyphs + 1) * glyph_sz;
  34. var glyf_dst: uint8_t *;
  35. var loca_dst: uint8_t *;
  36. var glyf_offset: uint32_t;
  37. var loca_offset: size_t;
  38. for (var i: int; i < num_glyphs; ++i) {
  39. StoreLoca(index_fmt, glyf_offset, &loca_offset, loca_dst);
  40. var glyph: woff2::Glyph;
  41. var glyph_data: const uint8_t *;
  42. var glyph_size: size_t;
  43. if (!GetGlyphData(*font, i, &glyph_data, &glyph_size) ||
  44. (glyph_size > 0 && !ReadGlyph(glyph_data, glyph_size, &glyph))) {
  45. return FONT_COMPRESSION_FAILURE();
  46. }
  47. var glyf_dst_size: size_t;
  48. if (!StoreGlyph(glyph, glyf_dst + glyf_offset, &glyf_dst_size)) {
  49. return FONT_COMPRESSION_FAILURE();
  50. }
  51. glyf_dst_size = Round4(glyf_dst_size);
  52. if (glyf_dst_size > std::numeric_limits<uint32_t>::max() ||
  53. glyf_offset + static_cast<uint32_t>(glyf_dst_size) < glyf_offset ||
  54. (index_fmt == 0 && glyf_offset + glyf_dst_size >= (1UL << 17))) {
  55. return FONT_COMPRESSION_FAILURE();
  56. }
  57. glyf_offset += glyf_dst_size;
  58. }
  59. StoreLoca(index_fmt, glyf_offset, &loca_offset, loca_dst);
  60. glyf_table->buffer.resize(glyf_offset);
  61. glyf_table->data = glyf_offset ? &glyf_table->buffer[0] : nullptr;
  62. glyf_table->length = glyf_offset;
  63. loca_table->data = loca_offset ? &loca_table->buffer[0] : nullptr;
  64. return true;
  65. }
  66. } // namespace
  67. namespace {
  68. fn MakeEditableBuffer(font: woff2::Font *, tableTag: int) -> bool {
  69. var table: Font::Table *;
  70. if (table == nullptr) {
  71. return FONT_COMPRESSION_FAILURE();
  72. }
  73. if (table->IsReused()) {
  74. return true;
  75. }
  76. var sz: int;
  77. table->buffer.resize(sz);
  78. var buf: uint8_t *;
  79. memcpy(buf, table->data, table->length);
  80. if (PREDICT_FALSE(sz > table->length)) {
  81. memset(buf + table->length, 0, sz - table->length);
  82. }
  83. table->data = buf;
  84. return true;
  85. }
  86. } // namespace
  87. fn NormalizeGlyphs(font: woff2::Font *) -> bool {
  88. var head_table: Font::Table *;
  89. var glyf_table: Font::Table *;
  90. var loca_table: Font::Table *;
  91. if (head_table == nullptr) {
  92. return FONT_COMPRESSION_FAILURE();
  93. }
  94. // If you don't have glyf/loca this transform isn't very interesting
  95. if (loca_table == nullptr && glyf_table == nullptr) {
  96. return true;
  97. }
  98. // It would be best if you didn't have just one of glyf/loca
  99. if ((glyf_table == nullptr) != (loca_table == nullptr)) {
  100. return FONT_COMPRESSION_FAILURE();
  101. }
  102. // Must share neither or both loca & glyf
  103. if (loca_table->IsReused() != glyf_table->IsReused()) {
  104. return FONT_COMPRESSION_FAILURE();
  105. }
  106. if (loca_table->IsReused()) {
  107. return true;
  108. }
  109. var index_fmt: int;
  110. var num_glyphs: int;
  111. // We need to allocate a bit more than its original length for the normalized
  112. // glyf table, since it can happen that the glyphs in the original table are
  113. // 2-byte aligned, while in the normalized table they are 4-byte aligned.
  114. // That gives a maximum of 2 bytes increase per glyph. However, there is no
  115. // theoretical guarantee that the total size of the flags plus the coordinates
  116. // is the smallest possible in the normalized version, so we have to allow
  117. // some general overhead.
  118. // TODO(user) Figure out some more precise upper bound on the size of
  119. // the overhead.
  120. var max_normalized_glyf_size: size_t;
  121. glyf_table->buffer.resize(max_normalized_glyf_size);
  122. // if we can't write a loca using short's (index_fmt 0)
  123. // try again using longs (index_fmt 1)
  124. if (!WriteNormalizedLoca(index_fmt, num_glyphs, font)) {
  125. if (index_fmt != 0) {
  126. return FONT_COMPRESSION_FAILURE();
  127. }
  128. // Rewrite loca with 4-byte entries & update head to match
  129. index_fmt = 1;
  130. if (!WriteNormalizedLoca(index_fmt, num_glyphs, font)) {
  131. return FONT_COMPRESSION_FAILURE();
  132. }
  133. head_table->buffer[51] = 1;
  134. }
  135. return true;
  136. }
  137. fn NormalizeOffsets(font: woff2::Font *) -> bool {
  138. var offset: uint32_t;
  139. for (auto tag var __begin1: : var __range1: fontfont) {
  140. var table: auto&;
  141. table.offset = offset;
  142. offset += Round4(table.length);
  143. }
  144. return true;
  145. }
  146. namespace {
  147. fn ComputeHeaderChecksum(font: const woff2::Font &) -> uint32_t {
  148. var checksum: uint32_t;
  149. var max_pow2: uint16_t;
  150. var search_range: uint16_t;
  151. var range_shift: uint16_t;
  152. checksum += (font.num_tables << 16 | search_range);
  153. checksum += (max_pow2 << 16 | range_shift);
  154. for (const auto& i var __begin2: : var __range2: fontfont) {
  155. var table: const Font::Table *;
  156. if (table->IsReused()) {
  157. table = table->reuse_of;
  158. }
  159. checksum += table->tag;
  160. checksum += table->checksum;
  161. checksum += table->offset;
  162. checksum += table->length;
  163. }
  164. return checksum;
  165. }
  166. } // namespace
  167. fn FixChecksums(font: woff2::Font *) -> bool {
  168. var head_table: Font::Table *;
  169. if (head_table == nullptr) {
  170. return FONT_COMPRESSION_FAILURE();
  171. }
  172. if (head_table->reuse_of != nullptr) {
  173. head_table = head_table->reuse_of;
  174. }
  175. if (head_table->length < 12) {
  176. return FONT_COMPRESSION_FAILURE();
  177. }
  178. var head_buf: uint8_t *;
  179. var offset: size_t;
  180. StoreU32(0, &offset, head_buf);
  181. var file_checksum: uint32_t;
  182. var head_checksum: uint32_t;
  183. for (auto& i var __begin1: : var __range1: fontfont) {
  184. var table: Font::Table *;
  185. if (table->IsReused()) {
  186. table = table->reuse_of;
  187. }
  188. table->checksum = ComputeULongSum(table->data, table->length);
  189. file_checksum += table->checksum;
  190. if (table->tag == kHeadTableTag) {
  191. head_checksum = table->checksum;
  192. }
  193. }
  194. file_checksum += ComputeHeaderChecksum(*font);
  195. offset = 8;
  196. StoreU32(0xb1b0afba - file_checksum, &offset, head_buf);
  197. return true;
  198. }
  199. namespace {
  200. fn MarkTransformed(font: woff2::Font *) -> bool {
  201. var head_table: Font::Table *;
  202. if (head_table == nullptr) {
  203. return FONT_COMPRESSION_FAILURE();
  204. }
  205. if (head_table->reuse_of != nullptr) {
  206. head_table = head_table->reuse_of;
  207. }
  208. if (head_table->length < 17) {
  209. return FONT_COMPRESSION_FAILURE();
  210. }
  211. // set bit 11 of head table 'flags' to indicate that font has undergone
  212. // lossless modifying transform
  213. var head_flags: int;
  214. head_table->buffer[16] = head_flags | 0x08;
  215. return true;
  216. }
  217. } // namespace
  218. fn NormalizeWithoutFixingChecksums(font: woff2::Font *) -> bool {
  219. return (MakeEditableBuffer(font, kHeadTableTag) &&
  220. RemoveDigitalSignature(font) &&
  221. MarkTransformed(font) &&
  222. NormalizeGlyphs(font) &&
  223. NormalizeOffsets(font));
  224. }
  225. fn NormalizeFont(font: woff2::Font *) -> bool {
  226. return (NormalizeWithoutFixingChecksums(font) &&
  227. FixChecksums(font));
  228. }
  229. fn NormalizeFontCollection(font_collection: woff2::FontCollection *) -> bool {
  230. if (font_collection->fonts.size() == 1) {
  231. return NormalizeFont(&font_collection->fonts[0]);
  232. }
  233. var offset: uint32_t;
  234. for (auto& font var __begin1: : var __range1: font_collectionfont_collection) {
  235. if (!NormalizeWithoutFixingChecksums(&font)) {
  236. #ifdef FONT_COMPRESSION_BIN
  237. fprintf(stderr, "Font normalization failed.\n");
  238. #endif
  239. return FONT_COMPRESSION_FAILURE();
  240. }
  241. offset += kSfntHeaderSize + kSfntEntrySize * font.num_tables;
  242. }
  243. // Start table offsets after TTC Header and Sfnt Headers
  244. for (auto& font var __begin1: : var __range1: font_collectionfont_collection) {
  245. for (auto tag var __begin2: : var __range2: fontfont) {
  246. var table: Font::Table &;
  247. if (table.IsReused()) {
  248. table.offset = table.reuse_of->offset;
  249. } else {
  250. table.offset = offset;
  251. offset += Round4(table.length);
  252. }
  253. }
  254. }
  255. // Now we can fix the checksums
  256. for (auto& font var __begin1: : var __range1: font_collectionfont_collection) {
  257. if (!FixChecksums(&font)) {
  258. #ifdef FONT_COMPRESSION_BIN
  259. fprintf(stderr, "Failed to fix checksums\n");
  260. #endif
  261. return FONT_COMPRESSION_FAILURE();
  262. }
  263. }
  264. return true;
  265. }
  266. } // namespace woff2