# Copyright 2024 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from resources.types.resource_object import ResourceObject from resources.resource_map.resource_generator import ResourceGenerator from font.fontgen import Font, MAX_GLYPHS_EXTENDED, MAX_GLYPHS from pebble_sdk_platform import pebble_platforms, maybe_import_internal from threading import Lock import re class FontResourceGenerator(ResourceGenerator): """ ResourceGenerator for the 'font' type """ type = 'font' lock = Lock() @staticmethod def definitions_from_dict(bld, definition_dict, resource_source_path): maybe_import_internal(bld.env) definitions = ResourceGenerator.definitions_from_dict(bld, definition_dict, resource_source_path) # Parse additional font specific fields for d in definitions: d.max_glyph_size = pebble_platforms[bld.env.PLATFORM_NAME]['MAX_FONT_GLYPH_SIZE'] d.character_list = definition_dict.get('characterList') d.character_regex = definition_dict.get('characterRegex') d.compatibility = definition_dict.get('compatibility') d.compress = definition_dict.get('compress') d.extended = bool(definition_dict.get('extended')) d.tracking_adjust = definition_dict.get('trackingAdjust') return definitions @classmethod def generate_object(cls, task, definition): font_data = cls.build_font_data(task.inputs[0].abspath(), definition) return ResourceObject(definition, font_data) @classmethod def build_font_data(cls, ttf_path, definition): # PBL-23964: it turns out that font generation is not thread-safe with freetype # 2.4 (and possibly later versions). To avoid running into this, we use a lock. with cls.lock: height = FontResourceGenerator._get_font_height_from_name(definition.name) is_legacy = definition.compatibility == "2.7" max_glyphs = MAX_GLYPHS_EXTENDED if definition.extended else MAX_GLYPHS font = Font(ttf_path, height, max_glyphs, definition.max_glyph_size, is_legacy) if definition.character_regex is not None: font.set_regex_filter(definition.character_regex.encode('utf8')) if definition.character_list is not None: font.set_codepoint_list(definition.character_list) if definition.compress: font.set_compression(definition.compress) if definition.tracking_adjust is not None: font.set_tracking_adjust(definition.tracking_adjust) font.build_tables() return font.bitstring() @staticmethod def _get_font_height_from_name(name): """ Search the name of the font for an integer which will be used as the pixel height of the generated font """ match = re.search('([0-9]+)', name) if match is None: if name != 'FONT_FALLBACK' and name != 'FONT_FALLBACK_INTERNAL': raise ValueError('Font {0}: no height found in name\n'.format(name)) return 14 return int(match.group(0))