From b3cc7757c932c05135e979b86df508c99489e7df Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Fri, 10 Nov 2023 15:54:55 +0000 Subject: [PATCH] [maxp] Attempt to improve fixup of bad 'maxp' version numbers. If the version number is less than 0x00010000 (e.g. an example reported at https://github.com/mozilla/pdf.js/issues/16839 apparently has 0x0100), it will be treated as version 0.5 and only the num_glyphs field is kept. However, this can prevent the font working on Windows, which apparently requires the full maxp table for truetype fonts. So if the version number was bad, but there is in fact enough data to parse as version 1.0, let's try correcting it to 1.0 rather than 0.5. Also make the max_zones fixup more general, as the example font from the above issue has max_zones=512(!). Correcting it to 2 should be harmless. --- src/cff.cc | 5 +++++ src/maxp.cc | 27 ++++++++++++++++++++------- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/cff.cc b/src/cff.cc index af54a4a1..af6d1eb8 100644 --- a/src/cff.cc +++ b/src/cff.cc @@ -1250,6 +1250,11 @@ bool OpenTypeCFF::Parse(const uint8_t *data, size_t length) { if (!maxp) { return Error("Required maxp table missing"); } + if (maxp->version_1) { + Warning("Fixing incorrect maxp version for CFF font"); + maxp->version_1 = false; + } + const uint16_t num_glyphs = maxp->num_glyphs; const size_t sid_max = string_index.count + kNStdString; // string_index.count == 0 is allowed. diff --git a/src/maxp.cc b/src/maxp.cc index 232e4a98..59b98cd7 100644 --- a/src/maxp.cc +++ b/src/maxp.cc @@ -29,6 +29,23 @@ bool OpenTypeMAXP::Parse(const uint8_t *data, size_t length) { return Error("numGlyphs is 0"); } + // Per https://learn.microsoft.com/en-gb/typography/opentype/spec/maxp, + // the only two 'maxp' version numbers are 0.5 (for CFF/CFF2) and 1.0 + // (for TrueType). + if (version == 0x00005000) { + this->version_1 = false; + return true; + } + + if (version != 0x00010000) { + Warning("Unrecognized version %08x, attempting to fix", version); + // If there's at least enough data to read as a version 1.0 table, + // we'll try that. + if (length >= 32) { + version = 0x00010000; + } + } + if (version >> 16 == 1) { this->version_1 = true; if (!table.ReadU16(&this->max_points) || @@ -47,19 +64,15 @@ bool OpenTypeMAXP::Parse(const uint8_t *data, size_t length) { return Error("Failed to read version 1 table data"); } - if (this->max_zones == 0) { + if (this->max_zones < 1) { // workaround for ipa*.ttf Japanese fonts. Warning("Bad maxZones: %u", this->max_zones); this->max_zones = 1; - } else if (this->max_zones == 3) { - // workaround for Ecolier-*.ttf fonts. + } else if (this->max_zones > 2) { + // workaround for Ecolier-*.ttf fonts and bad fonts in some PDFs Warning("Bad maxZones: %u", this->max_zones); this->max_zones = 2; } - - if ((this->max_zones != 1) && (this->max_zones != 2)) { - return Error("Bad maxZones: %u", this->max_zones); - } } else { this->version_1 = false; }