diff options
| author | Po Lu | 2023-08-24 16:32:42 +0800 |
|---|---|---|
| committer | Po Lu | 2023-08-24 16:36:13 +0800 |
| commit | ad5e17d0638be46f982d1448e8fef1d28d224735 (patch) | |
| tree | cd9fd66de4de975c1624d41ef9a5603d8ba2f081 /src | |
| parent | b9917f11521d47426330d79c65f9726c8fd7f8f7 (diff) | |
| download | emacs-ad5e17d0638be46f982d1448e8fef1d28d224735.tar.gz emacs-ad5e17d0638be46f982d1448e8fef1d28d224735.zip | |
Properly parse format 4 cmap tables
* src/sfnt.c (sfnt_read_cmap_format_4): Read range_shift field
propery. Prior to this, it would be inadvertently treated as an
entry within the segment end code array, which only functioned
by happenstance.
(sfnt_lookup_glyph_4): Remove workaround grounded upon an
erroneous interpretation of the bug fixed by the aformentioned
change.
* src/sfnt.h (struct sfnt_cmap_format_4): Introduce previously
absent `range_shift' field.
Diffstat (limited to 'src')
| -rw-r--r-- | src/sfnt.c | 26 | ||||
| -rw-r--r-- | src/sfnt.h | 3 |
2 files changed, 6 insertions, 23 deletions
diff --git a/src/sfnt.c b/src/sfnt.c index c987ec42441..87a11019b13 100644 --- a/src/sfnt.c +++ b/src/sfnt.c | |||
| @@ -432,7 +432,7 @@ sfnt_read_cmap_format_4 (int fd, | |||
| 432 | int seg_count, i; | 432 | int seg_count, i; |
| 433 | 433 | ||
| 434 | min_bytes = SFNT_ENDOF (struct sfnt_cmap_format_4, | 434 | min_bytes = SFNT_ENDOF (struct sfnt_cmap_format_4, |
| 435 | entry_selector, uint16_t); | 435 | range_shift, uint16_t); |
| 436 | 436 | ||
| 437 | /* Check that the length is at least min_bytes. */ | 437 | /* Check that the length is at least min_bytes. */ |
| 438 | if (header->length < min_bytes) | 438 | if (header->length < min_bytes) |
| @@ -460,6 +460,7 @@ sfnt_read_cmap_format_4 (int fd, | |||
| 460 | sfnt_swap16 (&format4->seg_count_x2); | 460 | sfnt_swap16 (&format4->seg_count_x2); |
| 461 | sfnt_swap16 (&format4->search_range); | 461 | sfnt_swap16 (&format4->search_range); |
| 462 | sfnt_swap16 (&format4->entry_selector); | 462 | sfnt_swap16 (&format4->entry_selector); |
| 463 | sfnt_swap16 (&format4->range_shift); | ||
| 463 | 464 | ||
| 464 | /* Get the number of segments to read. */ | 465 | /* Get the number of segments to read. */ |
| 465 | seg_count = format4->seg_count_x2 / 2; | 466 | seg_count = format4->seg_count_x2 / 2; |
| @@ -467,7 +468,7 @@ sfnt_read_cmap_format_4 (int fd, | |||
| 467 | /* Now calculate whether or not the size is sufficiently large. */ | 468 | /* Now calculate whether or not the size is sufficiently large. */ |
| 468 | bytes_minus_format4 | 469 | bytes_minus_format4 |
| 469 | = format4->length - SFNT_ENDOF (struct sfnt_cmap_format_4, | 470 | = format4->length - SFNT_ENDOF (struct sfnt_cmap_format_4, |
| 470 | entry_selector, uint16_t); | 471 | range_shift, uint16_t); |
| 471 | variable_size = (seg_count * sizeof *format4->end_code | 472 | variable_size = (seg_count * sizeof *format4->end_code |
| 472 | + sizeof *format4->reserved_pad | 473 | + sizeof *format4->reserved_pad |
| 473 | + seg_count * sizeof *format4->start_code | 474 | + seg_count * sizeof *format4->start_code |
| @@ -1222,27 +1223,6 @@ sfnt_lookup_glyph_4 (sfnt_char character, | |||
| 1222 | if (glyph) | 1223 | if (glyph) |
| 1223 | return glyph; | 1224 | return glyph; |
| 1224 | 1225 | ||
| 1225 | /* Droid Sans Mono has overlapping segments in its format 4 cmap | ||
| 1226 | subtable where the first segment's end code is 32, while the | ||
| 1227 | second segment's start code is also 32. The TrueType Reference | ||
| 1228 | Manual says that mapping should begin by searching for the first | ||
| 1229 | segment whose end code is greater than or equal to the character | ||
| 1230 | being indexed, but that results in the first subtable being | ||
| 1231 | found, which doesn't work, while the second table does. Try to | ||
| 1232 | detect this situation and use the second table if possible. */ | ||
| 1233 | |||
| 1234 | if (!glyph | ||
| 1235 | /* The character being looked up is the current segment's end | ||
| 1236 | code. */ | ||
| 1237 | && code == format4->end_code[segment] | ||
| 1238 | /* There is an additional segment. */ | ||
| 1239 | && segment + 1 < format4->seg_count_x2 / 2 | ||
| 1240 | /* That segment's start code is the same as this segment's end | ||
| 1241 | code. */ | ||
| 1242 | && format4->start_code[segment + 1] == format4->end_code[segment]) | ||
| 1243 | /* Try the second segment. */ | ||
| 1244 | return sfnt_lookup_glyph_4_1 (character, segment + 1, format4); | ||
| 1245 | |||
| 1246 | /* Fail. */ | 1226 | /* Fail. */ |
| 1247 | return 0; | 1227 | return 0; |
| 1248 | } | 1228 | } |
diff --git a/src/sfnt.h b/src/sfnt.h index 0fb67e918a6..1a6b2209abc 100644 --- a/src/sfnt.h +++ b/src/sfnt.h | |||
| @@ -364,6 +364,9 @@ struct sfnt_cmap_format_4 | |||
| 364 | /* log2(searchRange/2) */ | 364 | /* log2(searchRange/2) */ |
| 365 | uint16_t entry_selector; | 365 | uint16_t entry_selector; |
| 366 | 366 | ||
| 367 | /* (2 * segCount) - searchRange */ | ||
| 368 | uint16_t range_shift; | ||
| 369 | |||
| 367 | /* Variable-length data. */ | 370 | /* Variable-length data. */ |
| 368 | uint16_t *end_code; | 371 | uint16_t *end_code; |
| 369 | uint16_t *reserved_pad; | 372 | uint16_t *reserved_pad; |