diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:43:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:43:14 +0000 |
commit | 8dd16259287f58f9273002717ec4d27e97127719 (patch) | |
tree | 3863e62a53829a84037444beab3abd4ed9dfc7d0 /gfx/ots | |
parent | Releasing progress-linux version 126.0.1-1~progress7.99u1. (diff) | |
download | firefox-8dd16259287f58f9273002717ec4d27e97127719.tar.xz firefox-8dd16259287f58f9273002717ec4d27e97127719.zip |
Merging upstream version 127.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/ots')
-rw-r--r-- | gfx/ots/include/ots-memory-stream.h | 4 | ||||
-rw-r--r-- | gfx/ots/moz.yaml | 5 | ||||
-rw-r--r-- | gfx/ots/ots-1850314.patch | 177 | ||||
-rw-r--r-- | gfx/ots/src/cff.cc | 140 | ||||
-rw-r--r-- | gfx/ots/src/colr.cc | 13 | ||||
-rw-r--r-- | gfx/ots/src/glyf.cc | 8 | ||||
-rw-r--r-- | gfx/ots/src/glyf.h | 8 | ||||
-rw-r--r-- | gfx/ots/src/maxp.cc | 76 | ||||
-rw-r--r-- | gfx/ots/src/ots.cc | 10 |
9 files changed, 153 insertions, 288 deletions
diff --git a/gfx/ots/include/ots-memory-stream.h b/gfx/ots/include/ots-memory-stream.h index cb844709c2..ec0b362fea 100644 --- a/gfx/ots/include/ots-memory-stream.h +++ b/gfx/ots/include/ots-memory-stream.h @@ -26,7 +26,7 @@ class MemoryStream : public OTSStream { return false; } std::memcpy(static_cast<char*>(ptr_) + off_, data, length); - off_ += length; + off_ += static_cast<off_t>(length); return true; } @@ -82,7 +82,7 @@ class ExpandingMemoryStream : public OTSStream { return WriteRaw(data, length); } std::memcpy(static_cast<char*>(ptr_) + off_, data, length); - off_ += length; + off_ += static_cast<off_t>(length); return true; } diff --git a/gfx/ots/moz.yaml b/gfx/ots/moz.yaml index 50ce2b4e8f..bff78ff44b 100644 --- a/gfx/ots/moz.yaml +++ b/gfx/ots/moz.yaml @@ -10,8 +10,8 @@ origin: url: https://github.com/khaledhosny/ots - release: 6ba665aa307ea360283191736814863ca398398d (2023-08-16T17:30:00Z). - revision: 6ba665aa307ea360283191736814863ca398398d + release: d51fceea592507b78cf6c5fd90ba9ef6b969ffd1 (2024-03-26T11:28:30Z). + revision: d51fceea592507b78cf6c5fd90ba9ef6b969ffd1 license: BSD-3-Clause license-file: LICENSE @@ -39,4 +39,3 @@ vendoring: - ots-lz4.patch - ots-rlbox.patch - ots-visibility.patch - - ots-1850314.patch diff --git a/gfx/ots/ots-1850314.patch b/gfx/ots/ots-1850314.patch deleted file mode 100644 index 88088f5064..0000000000 --- a/gfx/ots/ots-1850314.patch +++ /dev/null @@ -1,177 +0,0 @@ -commit 362a59be47f9e187eec43df0938def661be6c972 -Author: Jonathan Kew <jkew@mozilla.com> -Date: Wed Aug 30 12:55:02 2023 +0000 - - Bug 1850314 - Don't do glyph bounding-box fixup for "tricky" fonts, because it may disrupt glyph rendering on macOS. r=gfx-reviewers,lsalzman - - Differential Revision: https://phabricator.services.mozilla.com/D187096 - -diff --git a/src/glyf.cc b/src/glyf.cc -index 0ed9515ef16d6..31487957bf99b 100644 ---- a/src/glyf.cc -+++ b/src/glyf.cc -@@ -10,6 +10,7 @@ - #include "head.h" - #include "loca.h" - #include "maxp.h" -+#include "name.h" - - // glyf - Glyph Data - // http://www.microsoft.com/typography/otspec/glyf.htm -@@ -97,7 +98,8 @@ bool OpenTypeGLYF::ParseSimpleGlyph(Buffer &glyph, - int16_t& xmin, - int16_t& ymin, - int16_t& xmax, -- int16_t& ymax) { -+ int16_t& ymax, -+ bool is_tricky_font) { - // read the end-points array - uint16_t num_flags = 0; - for (int i = 0; i < num_contours; ++i) { -@@ -219,27 +221,32 @@ bool OpenTypeGLYF::ParseSimpleGlyph(Buffer &glyph, - } - - if (adjusted_bbox) { -- Warning("Glyph bbox was incorrect; adjusting (glyph %u)", gid); -- // copy the numberOfContours field -- this->iov.push_back(std::make_pair(glyph.buffer(), 2)); -- // output a fixed-up version of the bounding box -- uint8_t* fixed_bbox = new uint8_t[8]; -- fixed_bboxes.push_back(fixed_bbox); -- xmin = ots_htons(xmin); -- std::memcpy(fixed_bbox, &xmin, 2); -- ymin = ots_htons(ymin); -- std::memcpy(fixed_bbox + 2, &ymin, 2); -- xmax = ots_htons(xmax); -- std::memcpy(fixed_bbox + 4, &xmax, 2); -- ymax = ots_htons(ymax); -- std::memcpy(fixed_bbox + 6, &ymax, 2); -- this->iov.push_back(std::make_pair(fixed_bbox, 8)); -- // copy the remainder of the glyph data -- this->iov.push_back(std::make_pair(glyph.buffer() + 10, glyph.offset() - 10)); -- } else { -- this->iov.push_back(std::make_pair(glyph.buffer(), glyph.offset())); -+ if (is_tricky_font) { -+ Warning("Glyph bbox was incorrect; NOT adjusting tricky font (glyph %u)", gid); -+ } else { -+ Warning("Glyph bbox was incorrect; adjusting (glyph %u)", gid); -+ // copy the numberOfContours field -+ this->iov.push_back(std::make_pair(glyph.buffer(), 2)); -+ // output a fixed-up version of the bounding box -+ uint8_t* fixed_bbox = new uint8_t[8]; -+ fixed_bboxes.push_back(fixed_bbox); -+ xmin = ots_htons(xmin); -+ std::memcpy(fixed_bbox, &xmin, 2); -+ ymin = ots_htons(ymin); -+ std::memcpy(fixed_bbox + 2, &ymin, 2); -+ xmax = ots_htons(xmax); -+ std::memcpy(fixed_bbox + 4, &xmax, 2); -+ ymax = ots_htons(ymax); -+ std::memcpy(fixed_bbox + 6, &ymax, 2); -+ this->iov.push_back(std::make_pair(fixed_bbox, 8)); -+ // copy the remainder of the glyph data -+ this->iov.push_back(std::make_pair(glyph.buffer() + 10, glyph.offset() - 10)); -+ return true; -+ } - } - -+ this->iov.push_back(std::make_pair(glyph.buffer(), glyph.offset())); -+ - return true; - } - -@@ -342,6 +349,10 @@ bool OpenTypeGLYF::Parse(const uint8_t *data, size_t length) { - return Error("Missing maxp or loca or head table needed by glyf table"); - } - -+ OpenTypeNAME *name = static_cast<OpenTypeNAME*>( -+ GetFont()->GetTypedTable(OTS_TAG_NAME)); -+ bool is_tricky = name->IsTrickyFont(); -+ - this->maxp = maxp; - - const unsigned num_glyphs = maxp->num_glyphs; -@@ -397,7 +408,7 @@ bool OpenTypeGLYF::Parse(const uint8_t *data, size_t length) { - // does we will simply ignore it. - glyph.set_offset(0); - } else if (num_contours > 0) { -- if (!ParseSimpleGlyph(glyph, i, num_contours, xmin, ymin, xmax, ymax)) { -+ if (!ParseSimpleGlyph(glyph, i, num_contours, xmin, ymin, xmax, ymax, is_tricky)) { - return Error("Failed to parse glyph %d", i); - } - } else { -diff --git a/src/glyf.h b/src/glyf.h -index 05e846f1cb6e8..f85fdc4652fcf 100644 ---- a/src/glyf.h -+++ b/src/glyf.h -@@ -51,7 +51,8 @@ class OpenTypeGLYF : public Table { - int16_t& xmin, - int16_t& ymin, - int16_t& xmax, -- int16_t& ymax); -+ int16_t& ymax, -+ bool is_tricky_font); - bool ParseCompositeGlyph( - Buffer &glyph, - ComponentPointCount* component_point_count); -diff --git a/src/name.cc b/src/name.cc -index fc5074b0587a3..7526e1f72b9ea 100644 ---- a/src/name.cc -+++ b/src/name.cc -@@ -366,4 +366,44 @@ bool OpenTypeNAME::IsValidNameId(uint16_t nameID, bool addIfMissing) { - return this->name_ids.count(nameID); - } - -+// List of font names considered "tricky" (dependent on applying original TrueType instructions) by FreeType, see -+// https://gitlab.freedesktop.org/freetype/freetype/-/blob/2d9fce53d4ce89f36075168282fcdd7289e082f9/src/truetype/ttobjs.c#L170-241 -+static const char* tricky_font_names[] = { -+ "cpop", -+ "DFGirl-W6-WIN-BF", -+ "DFGothic-EB", -+ "DFGyoSho-Lt", -+ "DFHei", -+ "DFHSGothic-W5", -+ "DFHSMincho-W3", -+ "DFHSMincho-W7", -+ "DFKaiSho-SB", -+ "DFKaiShu", -+ "DFKai-SB", -+ "DFMing", -+ "DLC", -+ "HuaTianKaiTi?", -+ "HuaTianSongTi?", -+ "Ming(for ISO10646)", -+ "MingLiU", -+ "MingMedium", -+ "PMingLiU", -+ "MingLi43" -+}; -+ -+bool OpenTypeNAME::IsTrickyFont() const { -+ for (const auto& name : this->names) { -+ const uint16_t id = name.name_id; -+ if (id != 1) { -+ continue; -+ } -+ for (const auto* p : tricky_font_names) { -+ if (name.text.find(p) != std::string::npos) { -+ return true; -+ } -+ } -+ } -+ return false; -+} -+ - } // namespace -diff --git a/src/name.h b/src/name.h -index 68c7ac096d3f8..a241e77ee26bb 100644 ---- a/src/name.h -+++ b/src/name.h -@@ -52,6 +52,7 @@ class OpenTypeNAME : public Table { - bool Parse(const uint8_t *data, size_t length); - bool Serialize(OTSStream *out); - bool IsValidNameId(uint16_t nameID, bool addIfMissing = false); -+ bool IsTrickyFont() const; - - private: - std::vector<NameRecord> names; diff --git a/gfx/ots/src/cff.cc b/gfx/ots/src/cff.cc index af54a4a16c..95ff7f744b 100644 --- a/gfx/ots/src/cff.cc +++ b/gfx/ots/src/cff.cc @@ -41,7 +41,7 @@ enum FONT_FORMAT { // see Appendix. A const size_t kNStdString = 390; -typedef std::pair<uint32_t, DICT_OPERAND_TYPE> Operand; +typedef std::pair<int32_t, DICT_OPERAND_TYPE> Operand; bool ReadOffset(ots::Buffer &table, uint8_t off_size, uint32_t *offset) { if (off_size > 4) { @@ -172,7 +172,7 @@ bool CheckOffset(const Operand& operand, size_t table_length) { if (operand.second != DICT_OPERAND_INTEGER) { return OTS_FAILURE(); } - if (operand.first >= table_length) { + if (operand.first >= static_cast<int32_t>(table_length) || operand.first < 0) { return OTS_FAILURE(); } return true; @@ -182,7 +182,7 @@ bool CheckSid(const Operand& operand, size_t sid_max) { if (operand.second != DICT_OPERAND_INTEGER) { return OTS_FAILURE(); } - if (operand.first > sid_max) { + if (operand.first > static_cast<int32_t>(sid_max) || operand.first < 0) { return OTS_FAILURE(); } return true; @@ -202,15 +202,13 @@ bool ParseDictDataBcd(ots::Buffer &table, std::vector<Operand> &operands) { if ((nibble & 0xf) == 0xf) { // TODO(yusukes): would be better to store actual double value, // rather than the dummy integer. - operands.push_back(std::make_pair(static_cast<uint32_t>(0), - DICT_OPERAND_REAL)); + operands.push_back(std::make_pair(0, DICT_OPERAND_REAL)); return true; } return OTS_FAILURE(); } if ((nibble & 0x0f) == 0x0f) { - operands.push_back(std::make_pair(static_cast<uint32_t>(0), - DICT_OPERAND_REAL)); + operands.push_back(std::make_pair(0, DICT_OPERAND_REAL)); return true; } @@ -256,7 +254,7 @@ bool ParseDictDataEscapedOperator(ots::Buffer &table, if ((op <= 14) || (op >= 17 && op <= 23) || (op >= 30 && op <= 38)) { - operands.push_back(std::make_pair((12U << 8) + op, DICT_OPERATOR)); + operands.push_back(std::make_pair((12 << 8) + op, DICT_OPERATOR)); return true; } @@ -277,8 +275,9 @@ bool ParseDictDataNumber(ots::Buffer &table, uint8_t b0, !table.ReadU8(&b2)) { return OTS_FAILURE(); } + //the two-byte value needs to be casted to int16_t in order to get the right sign operands.push_back(std::make_pair( - static_cast<uint32_t>((b1 << 8) + b2), DICT_OPERAND_INTEGER)); + static_cast<int16_t>((b1 << 8) + b2), DICT_OPERAND_INTEGER)); return true; case 29: // longint @@ -289,7 +288,7 @@ bool ParseDictDataNumber(ots::Buffer &table, uint8_t b0, return OTS_FAILURE(); } operands.push_back(std::make_pair( - static_cast<uint32_t>((b1 << 24) + (b2 << 16) + (b3 << 8) + b4), + (b1 << 24) + (b2 << 16) + (b3 << 8) + b4, DICT_OPERAND_INTEGER)); return true; @@ -300,7 +299,7 @@ bool ParseDictDataNumber(ots::Buffer &table, uint8_t b0, break; } - uint32_t result; + int32_t result; if (b0 >=32 && b0 <=246) { result = b0 - 139; } else if (b0 >=247 && b0 <= 250) { @@ -332,7 +331,7 @@ bool ParseDictDataReadNext(ots::Buffer &table, return ParseDictDataEscapedOperator(table, operands); } operands.push_back(std::make_pair( - static_cast<uint32_t>(op), DICT_OPERATOR)); + static_cast<int32_t>(op), DICT_OPERATOR)); return true; } else if (op <= 27 || op == 31 || op == 255) { // reserved area. @@ -367,13 +366,13 @@ bool ParseDictDataReadOperands(ots::Buffer& dict, return true; } -bool ValidCFF2DictOp(uint32_t op, DICT_DATA_TYPE type) { +bool ValidCFF2DictOp(int32_t op, DICT_DATA_TYPE type) { if (type == DICT_DATA_TOPLEVEL) { switch (op) { - case (12U << 8) + 7: // FontMatrix + case (12 << 8) + 7: // FontMatrix case 17: // CharStrings - case (12U << 8) + 36: // FDArray - case (12U << 8) + 37: // FDSelect + case (12 << 8) + 36: // FDArray + case (12 << 8) + 37: // FDSelect case 24: // vstore return true; default: @@ -384,8 +383,8 @@ bool ValidCFF2DictOp(uint32_t op, DICT_DATA_TYPE type) { return true; } else if (type == DICT_DATA_PRIVATE) { switch (op) { - case (12U << 8) + 14: // ForceBold - case (12U << 8) + 19: // initialRandomSeed + case (12 << 8) + 14: // ForceBold + case (12 << 8) + 19: // initialRandomSeed case 20: // defaultWidthX case 21: // nominalWidthX return false; @@ -426,7 +425,7 @@ bool ParsePrivateDictData( } // got operator - const uint32_t op = operands.back().first; + const int32_t op = operands.back().first; operands.pop_back(); if (cff2 && !ValidCFF2DictOp(op, DICT_DATA_PRIVATE)) { @@ -446,8 +445,8 @@ bool ParsePrivateDictData( break; // array - case (12U << 8) + 12: // StemSnapH (delta) - case (12U << 8) + 13: // StemSnapV (delta) + case (12 << 8) + 12: // StemSnapH (delta) + case (12 << 8) + 13: // StemSnapV (delta) if (operands.empty()) { return OTS_FAILURE(); } @@ -458,12 +457,12 @@ bool ParsePrivateDictData( case 11: // StdVW case 20: // defaultWidthX case 21: // nominalWidthX - case (12U << 8) + 9: // BlueScale - case (12U << 8) + 10: // BlueShift - case (12U << 8) + 11: // BlueFuzz - case (12U << 8) + 17: // LanguageGroup - case (12U << 8) + 18: // ExpansionFactor - case (12U << 8) + 19: // initialRandomSeed + case (12 << 8) + 9: // BlueScale + case (12 << 8) + 10: // BlueShift + case (12 << 8) + 11: // BlueFuzz + case (12 << 8) + 17: // LanguageGroup + case (12 << 8) + 18: // ExpansionFactor + case (12 << 8) + 19: // initialRandomSeed if (operands.size() != 1) { return OTS_FAILURE(); } @@ -477,7 +476,14 @@ bool ParsePrivateDictData( if (operands.back().second != DICT_OPERAND_INTEGER) { return OTS_FAILURE(); } - if (operands.back().first >= 1024 * 1024 * 1024) { + // In theory a negative operand could occur here, if the Local Subrs + // were stored before the Private dict, but this does not seem to be + // well supported by implementations, and mishandling of a negative + // offset (e.g. by using unsigned offset arithmetic) might become a + // vector for exploitation. AFAIK no major font creation tool will + // generate such an offset, so to be on the safe side, we don't allow + // it here. + if (operands.back().first >= 1024 * 1024 * 1024 || operands.back().first < 0) { return OTS_FAILURE(); } if (operands.back().first + offset >= table.length()) { @@ -505,14 +511,14 @@ bool ParsePrivateDictData( } // boolean - case (12U << 8) + 14: // ForceBold + case (12 << 8) + 14: // ForceBold if (operands.size() != 1) { return OTS_FAILURE(); } if (operands.back().second != DICT_OPERAND_INTEGER) { return OTS_FAILURE(); } - if (operands.back().first >= 2) { + if (operands.back().first >= 2 || operands.back().first < 0) { return OTS_FAILURE(); } break; @@ -532,7 +538,7 @@ bool ParsePrivateDictData( } vsindex = operands.back().first; if (vsindex < 0 || - vsindex >= (int32_t)out_cff->region_index_count.size()) { + vsindex >= static_cast<int32_t>(out_cff->region_index_count.size())) { return OTS_FAILURE(); } out_cff->vsindex_per_font.back() = vsindex; @@ -546,11 +552,15 @@ bool ParsePrivateDictData( if (operands.size() < 1) { return OTS_FAILURE(); } - if (vsindex >= (int32_t)out_cff->region_index_count.size()) { + if (vsindex >= static_cast<int32_t>(out_cff->region_index_count.size())) { return OTS_FAILURE(); } uint16_t k = out_cff->region_index_count.at(vsindex); - uint16_t n = operands.back().first; + + if (operands.back().first > static_cast<uint16_t>(0xffff) || operands.back().first < 0){ + return OTS_FAILURE(); + } + uint16_t n = static_cast<uint16_t>(operands.back().first); if (operands.size() < n * (k + 1) + 1) { return OTS_FAILURE(); } @@ -644,7 +654,7 @@ bool ParseDictData(ots::Buffer& table, ots::Buffer& dict, if (operands.back().second != DICT_OPERATOR) continue; // got operator - const uint32_t op = operands.back().first; + const int32_t op = operands.back().first; operands.pop_back(); if (op == 18 && type == DICT_DATA_FDARRAY) { @@ -686,7 +696,7 @@ bool ParseDictData(ots::Buffer& table, ots::Buffer& dict, if (operands.back().second != DICT_OPERATOR) continue; // got operator - const uint32_t op = operands.back().first; + const int32_t op = operands.back().first; operands.pop_back(); if (cff2 && !ValidCFF2DictOp(op, type)) { @@ -700,10 +710,10 @@ bool ParseDictData(ots::Buffer& table, ots::Buffer& dict, case 2: // Copyright case 3: // FullName case 4: // FamilyName - case (12U << 8) + 0: // Copyright - case (12U << 8) + 21: // PostScript - case (12U << 8) + 22: // BaseFontName - case (12U << 8) + 38: // FontName + case (12 << 8) + 0: // Copyright + case (12 << 8) + 21: // PostScript + case (12 << 8) + 22: // BaseFontName + case (12 << 8) + 38: // FontName if (operands.size() != 1) { return OTS_FAILURE(); } @@ -715,8 +725,8 @@ bool ParseDictData(ots::Buffer& table, ots::Buffer& dict, // array case 5: // FontBBox case 14: // XUID - case (12U << 8) + 7: // FontMatrix - case (12U << 8) + 23: // BaseFontBlend (delta) + case (12 << 8) + 7: // FontMatrix + case (12 << 8) + 23: // BaseFontBlend (delta) if (operands.empty()) { return OTS_FAILURE(); } @@ -724,21 +734,21 @@ bool ParseDictData(ots::Buffer& table, ots::Buffer& dict, // number case 13: // UniqueID - case (12U << 8) + 2: // ItalicAngle - case (12U << 8) + 3: // UnderlinePosition - case (12U << 8) + 4: // UnderlineThickness - case (12U << 8) + 5: // PaintType - case (12U << 8) + 8: // StrokeWidth - case (12U << 8) + 20: // SyntheticBase + case (12 << 8) + 2: // ItalicAngle + case (12 << 8) + 3: // UnderlinePosition + case (12 << 8) + 4: // UnderlineThickness + case (12 << 8) + 5: // PaintType + case (12 << 8) + 8: // StrokeWidth + case (12 << 8) + 20: // SyntheticBase if (operands.size() != 1) { return OTS_FAILURE(); } break; - case (12U << 8) + 31: // CIDFontVersion - case (12U << 8) + 32: // CIDFontRevision - case (12U << 8) + 33: // CIDFontType - case (12U << 8) + 34: // CIDCount - case (12U << 8) + 35: // UIDBase + case (12 << 8) + 31: // CIDFontVersion + case (12 << 8) + 32: // CIDFontRevision + case (12 << 8) + 33: // CIDFontType + case (12 << 8) + 34: // CIDCount + case (12 << 8) + 35: // UIDBase if (operands.size() != 1) { return OTS_FAILURE(); } @@ -746,7 +756,7 @@ bool ParseDictData(ots::Buffer& table, ots::Buffer& dict, return OTS_FAILURE(); } break; - case (12U << 8) + 6: // CharstringType + case (12 << 8) + 6: // CharstringType if (operands.size() != 1) { return OTS_FAILURE(); } @@ -761,14 +771,14 @@ bool ParseDictData(ots::Buffer& table, ots::Buffer& dict, break; // boolean - case (12U << 8) + 1: // isFixedPitch + case (12 << 8) + 1: // isFixedPitch if (operands.size() != 1) { return OTS_FAILURE(); } if (operands.back().second != DICT_OPERAND_INTEGER) { return OTS_FAILURE(); } - if (operands.back().first >= 2) { + if (operands.back().first >= 2 || operands.back().first < 0) { return OTS_FAILURE(); } break; @@ -778,7 +788,7 @@ bool ParseDictData(ots::Buffer& table, ots::Buffer& dict, if (operands.size() != 1) { return OTS_FAILURE(); } - if (operands.back().first <= 2) { + if (operands.back().first <= 2 && operands.back().first >= 0) { // predefined charset, ISOAdobe, Expert or ExpertSubset, is used. break; } @@ -795,7 +805,7 @@ bool ParseDictData(ots::Buffer& table, ots::Buffer& dict, if (operands.size() != 1) { return OTS_FAILURE(); } - if (operands.back().first <= 1) { + if (operands.back().first <= 1 && operands.back().first >= 0) { break; // predefined encoding, "Standard" or "Expert", is used. } if (!CheckOffset(operands.back(), table.length())) { @@ -856,7 +866,7 @@ bool ParseDictData(ots::Buffer& table, ots::Buffer& dict, break; } - case (12U << 8) + 36: { // FDArray + case (12 << 8) + 36: { // FDArray if (type != DICT_DATA_TOPLEVEL) { return OTS_FAILURE(); } @@ -885,7 +895,7 @@ bool ParseDictData(ots::Buffer& table, ots::Buffer& dict, break; } - case (12U << 8) + 37: { // FDSelect + case (12 << 8) + 37: { // FDSelect if (type != DICT_DATA_TOPLEVEL) { return OTS_FAILURE(); } @@ -1043,19 +1053,19 @@ bool ParseDictData(ots::Buffer& table, ots::Buffer& dict, if (operands.back().second != DICT_OPERAND_INTEGER) { return OTS_FAILURE(); } - const uint32_t private_offset = operands.back().first; + const int32_t private_offset = operands.back().first; operands.pop_back(); if (operands.back().second != DICT_OPERAND_INTEGER) { return OTS_FAILURE(); } - const uint32_t private_length = operands.back().first; - if (private_offset > table.length()) { + const int32_t private_length = operands.back().first; + if (private_offset > static_cast<int32_t>(table.length())) { return OTS_FAILURE(); } - if (private_length >= table.length()) { + if (private_length >= static_cast<int32_t>(table.length()) || private_length < 0) { return OTS_FAILURE(); } - if (private_length + private_offset > table.length()) { + if (private_length + private_offset > static_cast<int32_t>(table.length()) || private_length + private_offset < 0) { return OTS_FAILURE(); } // parse "15. Private DICT data" @@ -1067,7 +1077,7 @@ bool ParseDictData(ots::Buffer& table, ots::Buffer& dict, } // ROS - case (12U << 8) + 30: + case (12 << 8) + 30: if (font_format != FORMAT_UNKNOWN) { return OTS_FAILURE(); } diff --git a/gfx/ots/src/colr.cc b/gfx/ots/src/colr.cc index 8931c77c32..a09d3ab3a4 100644 --- a/gfx/ots/src/colr.cc +++ b/gfx/ots/src/colr.cc @@ -193,7 +193,15 @@ bool ParsePaintColrLayers(const ots::Font* font, colrState& state) { if (setContains(state.visited, data)) { +#ifdef OTS_COLR_CYCLE_CHECK + // A cycle would imply an infinite loop during painting, unless the renderer + // detects and breaks it. To be safe, reject the table. return OTS_FAILURE_MSG("Cycle detected in PaintColrLayers"); +#else + // Just issue a warning and return (as we've already checked this subgraph). + OTS_WARNING("Cycle detected in COLRv1 glyph paint graph (PaintColrLayers)\n"); + return true; +#endif } state.visited.insert(data); @@ -393,7 +401,12 @@ bool ParsePaintColrGlyph(const ots::Font* font, colrState& state) { if (setContains(state.visited, data)) { +#ifdef OTS_COLR_CYCLE_CHECK return OTS_FAILURE_MSG("Cycle detected in PaintColrGlyph"); +#else + OTS_WARNING("Cycle detected in COLRv1 glyph paint graph (PaintColrGlyph)\n"); + return true; +#endif } state.visited.insert(data); diff --git a/gfx/ots/src/glyf.cc b/gfx/ots/src/glyf.cc index 31487957bf..12c0537f64 100644 --- a/gfx/ots/src/glyf.cc +++ b/gfx/ots/src/glyf.cc @@ -95,10 +95,10 @@ bool OpenTypeGLYF::ParseFlagsForSimpleGlyph(Buffer &glyph, bool OpenTypeGLYF::ParseSimpleGlyph(Buffer &glyph, unsigned gid, int16_t num_contours, - int16_t& xmin, - int16_t& ymin, - int16_t& xmax, - int16_t& ymax, + int16_t xmin, + int16_t ymin, + int16_t xmax, + int16_t ymax, bool is_tricky_font) { // read the end-points array uint16_t num_flags = 0; diff --git a/gfx/ots/src/glyf.h b/gfx/ots/src/glyf.h index f85fdc4652..c0d5871edf 100644 --- a/gfx/ots/src/glyf.h +++ b/gfx/ots/src/glyf.h @@ -48,10 +48,10 @@ class OpenTypeGLYF : public Table { bool ParseSimpleGlyph(Buffer &glyph, unsigned gid, int16_t num_contours, - int16_t& xmin, - int16_t& ymin, - int16_t& xmax, - int16_t& ymax, + int16_t xmin, + int16_t ymin, + int16_t xmax, + int16_t ymax, bool is_tricky_font); bool ParseCompositeGlyph( Buffer &glyph, diff --git a/gfx/ots/src/maxp.cc b/gfx/ots/src/maxp.cc index 232e4a9889..90f53b0e39 100644 --- a/gfx/ots/src/maxp.cc +++ b/gfx/ots/src/maxp.cc @@ -29,39 +29,49 @@ bool OpenTypeMAXP::Parse(const uint8_t *data, size_t length) { return Error("numGlyphs is 0"); } - if (version >> 16 == 1) { - this->version_1 = true; - if (!table.ReadU16(&this->max_points) || - !table.ReadU16(&this->max_contours) || - !table.ReadU16(&this->max_c_points) || - !table.ReadU16(&this->max_c_contours) || - !table.ReadU16(&this->max_zones) || - !table.ReadU16(&this->max_t_points) || - !table.ReadU16(&this->max_storage) || - !table.ReadU16(&this->max_fdefs) || - !table.ReadU16(&this->max_idefs) || - !table.ReadU16(&this->max_stack) || - !table.ReadU16(&this->max_size_glyf_instructions) || - !table.ReadU16(&this->max_c_components) || - !table.ReadU16(&this->max_c_depth)) { - return Error("Failed to read version 1 table data"); - } - - if (this->max_zones == 0) { - // 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. - 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; + this->version_1 = false; + + // 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 it's version 0.5, there is nothing more to read. + if (version == 0x00005000) { + return true; + } + + if (version != 0x00010000) { + Warning("Unexpected version 0x%08x; attempting to read as version 1.0", + version); + } + + // Otherwise, try to read the version 1.0 fields: + if (!table.ReadU16(&this->max_points) || + !table.ReadU16(&this->max_contours) || + !table.ReadU16(&this->max_c_points) || + !table.ReadU16(&this->max_c_contours) || + !table.ReadU16(&this->max_zones) || + !table.ReadU16(&this->max_t_points) || + !table.ReadU16(&this->max_storage) || + !table.ReadU16(&this->max_fdefs) || + !table.ReadU16(&this->max_idefs) || + !table.ReadU16(&this->max_stack) || + !table.ReadU16(&this->max_size_glyf_instructions) || + !table.ReadU16(&this->max_c_components) || + !table.ReadU16(&this->max_c_depth)) { + Warning("Failed to read version 1.0 fields, downgrading to version 0.5"); + return true; + } + + this->version_1 = true; + + 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 > 2) { + // workaround for Ecolier-*.ttf fonts and bad fonts in some PDFs + Warning("Bad maxZones: %u", this->max_zones); + this->max_zones = 2; } return true; diff --git a/gfx/ots/src/ots.cc b/gfx/ots/src/ots.cc index 73b1d235cf..1ee4498363 100644 --- a/gfx/ots/src/ots.cc +++ b/gfx/ots/src/ots.cc @@ -728,12 +728,18 @@ bool ProcessGeneric(ots::FontFile *header, ots::Table *loca = font->GetTable(OTS_TAG_LOCA); ots::Table *cff = font->GetTable(OTS_TAG_CFF); ots::Table *cff2 = font->GetTable(OTS_TAG_CFF2); + ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>( + font->GetTypedTable(OTS_TAG_MAXP)); if (glyf && loca) { if (font->version != 0x000010000) { OTS_WARNING_MSG_HDR("wrong sfntVersion for glyph data"); font->version = 0x000010000; } + if (!maxp->version_1) { + return OTS_FAILURE_MSG_TAG("wrong maxp version for glyph data", + OTS_TAG_MAXP); + } if (cff) cff->Drop("font contains both CFF and glyf/loca tables"); if (cff2) @@ -747,6 +753,10 @@ bool ProcessGeneric(ots::FontFile *header, glyf->Drop("font contains both CFF and glyf tables"); if (loca) loca->Drop("font contains both CFF and loca tables"); + if (maxp->version_1) { + OTS_WARNING_MSG_HDR("fixing incorrect maxp version for CFF font"); + maxp->version_1 = false; + } } else if (font->GetTable(OTS_TAG('C','B','D','T')) && font->GetTable(OTS_TAG('C','B','L','C'))) { // We don't sanitize bitmap tables, but don’t reject bitmap-only fonts if |