diff --git a/font_collector/font/abc_font_face.py b/font_collector/font/abc_font_face.py index 69e9ada..9833fbe 100644 --- a/font_collector/font/abc_font_face.py +++ b/font_collector/font/abc_font_face.py @@ -266,7 +266,7 @@ def get_missing_glyphs( if error: raise FT_Exception(error) ttFont = TTFont(self.font_file.filename, fontNumber=self.font_index) - supported_cmaps = FontParser.get_supported_cmaps(ttFont) + supported_cmaps = FontParser.get_supported_cmaps(ttFont, self.font_file.filename, self.font_index) supported_charmaps = [] for i in range(face.contents.num_charmaps): charmap = face.contents.charmaps[i] diff --git a/font_collector/font/factory_abc_font_face.py b/font_collector/font/factory_abc_font_face.py index d099834..6586e22 100644 --- a/font_collector/font/factory_abc_font_face.py +++ b/font_collector/font/factory_abc_font_face.py @@ -75,7 +75,7 @@ def __create_font(ttFont: TTFont, font_path: Path, font_index: int) -> NormalFon Returns: An FontFace instance that represent the ttFont. """ - cmaps = FontParser.get_supported_cmaps(ttFont) + cmaps = FontParser.get_supported_cmaps(ttFont, font_path, font_index) if len(cmaps) == 0: raise InvalidNormalFontFaceException(f"The font doesn't contain any valid cmap.") diff --git a/font_collector/font/font_parser.py b/font_collector/font/font_parser.py index 98da8ac..b745ac4 100644 --- a/font_collector/font/font_parser.py +++ b/font_collector/font/font_parser.py @@ -496,12 +496,16 @@ def get_symbol_cmap_encoding(face: FT_Face) -> Optional[str]: @staticmethod - def get_supported_cmaps(font: TTFont) -> List[CMap]: + def get_supported_cmaps( + font: TTFont, font_path: Path, font_index: int + ) -> List[CMap]: """ Retrieve supported CMaps from a TrueType font. Args: font: A fontTools object representing the font. + font_path: Font path. + font_index: Font index. Returns: A list of supported CMaps. - To determine which CMaps are supported, refer to FontParser.get_cmap_encoding(). @@ -511,16 +515,29 @@ def get_supported_cmaps(font: TTFont) -> List[CMap]: microsoft_cmaps: List[CMap] = [] macintosh_cmaps: List[CMap] = [] - cmap_tables: List[CmapSubtable] = font["cmap"].tables - - for table in cmap_tables: - encoding = FontParser.get_cmap_encoding(table.platformID, table.platEncID) - if encoding is not None: - cmap = CMap(table.platformID, table.platEncID) - if table.platformID == PlatformID.MICROSOFT: - microsoft_cmaps.append(cmap) - elif table.platformID == PlatformID.MACINTOSH: - macintosh_cmaps.append(cmap) + try: + cmap_tables: List[CmapSubtable] = font["cmap"].tables + + for table in cmap_tables: + encoding = FontParser.get_cmap_encoding(table.platformID, table.platEncID) + if encoding is not None: + cmap = CMap(table.platformID, table.platEncID) + if table.platformID == PlatformID.MICROSOFT: + microsoft_cmaps.append(cmap) + elif table.platformID == PlatformID.MACINTOSH: + macintosh_cmaps.append(cmap) + except Exception: + with font_path.open("rb") as f: + face = Face(f, font_index) + + for charmap in face.charmaps: + encoding = FontParser.get_cmap_encoding(charmap.platform_id, charmap.encoding_id) + if encoding is not None: + cmap = CMap(charmap.platform_id, charmap.encoding_id) + if charmap.platform_id == PlatformID.MICROSOFT: + microsoft_cmaps.append(cmap) + elif charmap.platform_id == PlatformID.MACINTOSH: + macintosh_cmaps.append(cmap) return macintosh_cmaps if len(microsoft_cmaps) == 0 else microsoft_cmaps diff --git a/font_collector/font/variable_font_face.py b/font_collector/font/variable_font_face.py index ea2c37a..66b0d77 100644 --- a/font_collector/font/variable_font_face.py +++ b/font_collector/font/variable_font_face.py @@ -187,7 +187,7 @@ def variable_font_to_collection(self, save_path: Path, cache_generated_font: boo if len(fonts_face) == 0: raise ValueError(f"There is no valid font at the index {self.font_index}") - cmaps = FontParser.get_supported_cmaps(ttFont) + cmaps = FontParser.get_supported_cmaps(ttFont, self.font_file.filename, self.font_index) for font_face in fonts_face: generated_font_face = instancer.instantiateVariableFont(ttFont, font_face.named_instance_coordinates) diff --git a/tests/file/fonts/invalid_cmap.ttf b/tests/file/fonts/invalid_cmap.ttf new file mode 100644 index 0000000..da6418e Binary files /dev/null and b/tests/file/fonts/invalid_cmap.ttf differ diff --git a/tests/font/test_font_loader.py b/tests/font/test_font_loader.py index 087a252..543299a 100644 --- a/tests/font/test_font_loader.py +++ b/tests/font/test_font_loader.py @@ -29,6 +29,7 @@ def test_load_additional_fonts(): Path(os.path.join(font_directory, "font_mac.TTF")), Path(os.path.join(font_directory, "font_with_invalid_os2_table.ttf")), Path(os.path.join(font_directory, "font_without axis_value.ttf")), + Path(os.path.join(font_directory, "invalid_cmap.ttf")), Path(os.path.join(font_directory, "opentype_font_collection.ttc")), Path(os.path.join(font_directory, "PENBOX.otf")), Path(os.path.join(font_directory, "SFProDisplay-Bold.ttf")), diff --git a/tests/font/test_font_parser.py b/tests/font/test_font_parser.py index 9ec693f..b7331b8 100644 --- a/tests/font/test_font_parser.py +++ b/tests/font/test_font_parser.py @@ -204,23 +204,29 @@ def test_get_symbol_cmap_encoding(): def test_get_supported_cmaps(): # This font contain 1 valid mac cmap - font_path = os.path.join(os.path.dirname(dir_path), "file", "fonts", "font_mac.TTF") + font_path = Path(os.path.join(os.path.dirname(dir_path), "file", "fonts", "font_mac.TTF")) font = TTFont(font_path) - cmaps = FontParser.get_supported_cmaps(font) + cmaps = FontParser.get_supported_cmaps(font, font_path, 0) assert cmaps == [CMap(1, 0)] # This font contain unicode cmap and multiple microsoft cmap - font_path = os.path.join(os.path.dirname(dir_path), "file", "fonts", "font_cmap_encoding_1.ttf") + font_path = Path(os.path.join(os.path.dirname(dir_path), "file", "fonts", "font_cmap_encoding_1.ttf")) font = TTFont(font_path) - cmaps = FontParser.get_supported_cmaps(font) + cmaps = FontParser.get_supported_cmaps(font, font_path, 0) assert cmaps == [CMap(3, 1), CMap(3, 10)] # This font contain 1 microsoft cmap, 1 valid mac cmap and 1 unicode cmap - font_path = os.path.join(os.path.dirname(dir_path), "file", "fonts", "font_cmap_encoding_0.ttf") + font_path = Path(os.path.join(os.path.dirname(dir_path), "file", "fonts", "font_cmap_encoding_0.ttf")) font = TTFont(font_path) - cmaps = FontParser.get_supported_cmaps(font) + cmaps = FontParser.get_supported_cmaps(font, font_path, 0) assert cmaps == [CMap(3, 0)] + # This font contain a "invalid" cmap. fontTools raise a exception, so we fallback to freetype which doesn't raise an exception + font_path = Path(os.path.join(os.path.dirname(dir_path), "file", "fonts", "invalid_cmap.ttf")) + font = TTFont(font_path) + cmaps = FontParser.get_supported_cmaps(font, font_path, 0) + assert cmaps == [CMap(3, 1)] + def test_get_cmap_encoding(): # It could be any format