vkit.engine.char_and_font_sampler
1# Copyright 2022 vkit-x Administrator. All Rights Reserved. 2# 3# This project (vkit-x/vkit) is dual-licensed under commercial and SSPL licenses. 4# 5# The commercial license gives you the full rights to create and distribute software 6# on your own terms without any SSPL license obligations. For more information, 7# please see the "LICENSE_COMMERCIAL.txt" file. 8# 9# This project is also available under Server Side Public License (SSPL). 10# The SSPL licensing is ideal for use cases such as open source projects with 11# SSPL distribution, student/academic purposes, hobby projects, internal research 12# projects without external distribution, or other projects where all SSPL 13# obligations can be met. For more information, please see the "LICENSE_SSPL.txt" file. 14from typing import Sequence, Optional 15import math 16import logging 17 18import attrs 19from numpy.random import Generator as RandomGenerator 20 21from vkit.utility import rng_choice 22from vkit.element import LexiconCollection 23from ..interface import ( 24 NoneTypeEngineInitConfig, 25 Engine, 26 EngineExecutorFactory, 27 EngineExecutorAggregator, 28) 29from vkit.engine.font.type import ( 30 FontCollection, 31 FontVariant, 32 FontEngineRunConfigGlyphSequence, 33) 34from vkit.engine.char_sampler.type import CharSamplerEngineRunConfig 35 36logger = logging.getLogger(__name__) 37 38 39@attrs.define 40class CharAndFontSamplerEngineRunConfig: 41 height: int 42 width: int 43 glyph_sequence: FontEngineRunConfigGlyphSequence = \ 44 FontEngineRunConfigGlyphSequence.HORI_DEFAULT 45 num_chars_factor: float = 1.1 46 num_chars: Optional[int] = None 47 48 49@attrs.define 50class CharAndFontSamplerEngineInitResource: 51 lexicon_collection: LexiconCollection 52 font_collection: FontCollection 53 char_sampler_engine_executor_aggregator: EngineExecutorAggregator[ 54 CharSamplerEngineRunConfig, 55 Sequence[str], 56 ] # yapf: disable 57 58 59@attrs.define 60class CharAndFont: 61 chars: Sequence[str] 62 font_variant: FontVariant 63 64 65class CharAndFontSamplerEngine( 66 Engine[ 67 NoneTypeEngineInitConfig, 68 CharAndFontSamplerEngineInitResource, 69 CharAndFontSamplerEngineRunConfig, 70 Optional[CharAndFont], 71 ] 72): # yapf: disable 73 74 @classmethod 75 def get_type_name(cls) -> str: 76 return 'default' 77 78 def __init__( 79 self, 80 init_config: NoneTypeEngineInitConfig, 81 init_resource: Optional[CharAndFontSamplerEngineInitResource] = None, 82 ): 83 super().__init__(init_config, init_resource) 84 85 assert init_resource 86 self.font_collection = init_resource.font_collection 87 self.lexicon_collection = init_resource.lexicon_collection 88 self.char_sampler_engine_executor_aggregator = init_resource.char_sampler_engine_executor_aggregator 89 90 @classmethod 91 def estimate_num_chars(cls, run_config: CharAndFontSamplerEngineRunConfig): 92 if run_config.num_chars: 93 return run_config.num_chars 94 95 if run_config.glyph_sequence == FontEngineRunConfigGlyphSequence.HORI_DEFAULT: 96 num_chars = run_config.width / run_config.height 97 elif run_config.glyph_sequence == FontEngineRunConfigGlyphSequence.VERT_DEFAULT: 98 num_chars = run_config.height / run_config.width 99 else: 100 raise NotImplementedError() 101 102 num_chars *= run_config.num_chars_factor 103 return math.ceil(num_chars) 104 105 def run( 106 self, 107 run_config: CharAndFontSamplerEngineRunConfig, 108 rng: Optional[RandomGenerator] = None, 109 ) -> Optional[CharAndFont]: 110 assert rng is not None 111 112 # Sample chars. 113 num_chars = self.estimate_num_chars(run_config) 114 chars = self.char_sampler_engine_executor_aggregator.run( 115 CharSamplerEngineRunConfig( 116 num_chars=num_chars, 117 enable_aggregator_mode=True, 118 ), 119 rng, 120 ) 121 logger.debug(f'chars={chars}') 122 123 # Sample font variant. 124 font_metas = self.font_collection.filter_font_metas(chars) 125 if not font_metas: 126 logger.warning(f'Cannot sample font_metas for chars={chars}') 127 return None 128 129 font_meta = rng_choice(rng, font_metas) 130 variant_idx = int(rng.integers(0, font_meta.num_font_variants)) 131 font_variant = font_meta.get_font_variant(variant_idx) 132 133 return CharAndFont(chars=chars, font_variant=font_variant) 134 135 136char_and_font_sampler_engine_executor_factory = EngineExecutorFactory(CharAndFontSamplerEngine)
class
CharAndFontSamplerEngineRunConfig:
41class CharAndFontSamplerEngineRunConfig: 42 height: int 43 width: int 44 glyph_sequence: FontEngineRunConfigGlyphSequence = \ 45 FontEngineRunConfigGlyphSequence.HORI_DEFAULT 46 num_chars_factor: float = 1.1 47 num_chars: Optional[int] = None
CharAndFontSamplerEngineRunConfig( height: int, width: int, glyph_sequence: vkit.engine.font.type.FontEngineRunConfigGlyphSequence = <FontEngineRunConfigGlyphSequence.HORI_DEFAULT: 'hori_default'>, num_chars_factor: float = 1.1, num_chars: Union[int, NoneType] = None)
2def __init__(self, height, width, glyph_sequence=attr_dict['glyph_sequence'].default, num_chars_factor=attr_dict['num_chars_factor'].default, num_chars=attr_dict['num_chars'].default): 3 self.height = height 4 self.width = width 5 self.glyph_sequence = glyph_sequence 6 self.num_chars_factor = num_chars_factor 7 self.num_chars = num_chars
Method generated by attrs for class CharAndFontSamplerEngineRunConfig.
class
CharAndFontSamplerEngineInitResource:
51class CharAndFontSamplerEngineInitResource: 52 lexicon_collection: LexiconCollection 53 font_collection: FontCollection 54 char_sampler_engine_executor_aggregator: EngineExecutorAggregator[ 55 CharSamplerEngineRunConfig, 56 Sequence[str], 57 ] # yapf: disable
CharAndFontSamplerEngineInitResource( lexicon_collection: vkit.element.lexicon.LexiconCollection, font_collection: vkit.engine.font.type.FontCollection, char_sampler_engine_executor_aggregator: vkit.engine.interface.EngineExecutorAggregator[vkit.engine.char_sampler.type.CharSamplerEngineRunConfig, typing.Sequence[str]])
2def __init__(self, lexicon_collection, font_collection, char_sampler_engine_executor_aggregator): 3 self.lexicon_collection = lexicon_collection 4 self.font_collection = font_collection 5 self.char_sampler_engine_executor_aggregator = char_sampler_engine_executor_aggregator
Method generated by attrs for class CharAndFontSamplerEngineInitResource.
class
CharAndFont:
CharAndFont( chars: Sequence[str], font_variant: vkit.engine.font.type.FontVariant)
Method generated by attrs for class CharAndFont.
class
CharAndFontSamplerEngine(vkit.engine.interface.Engine[vkit.engine.interface.NoneTypeEngineInitConfig, vkit.engine.char_and_font_sampler.CharAndFontSamplerEngineInitResource, vkit.engine.char_and_font_sampler.CharAndFontSamplerEngineRunConfig, typing.Union[vkit.engine.char_and_font_sampler.CharAndFont, NoneType]]):
66class CharAndFontSamplerEngine( 67 Engine[ 68 NoneTypeEngineInitConfig, 69 CharAndFontSamplerEngineInitResource, 70 CharAndFontSamplerEngineRunConfig, 71 Optional[CharAndFont], 72 ] 73): # yapf: disable 74 75 @classmethod 76 def get_type_name(cls) -> str: 77 return 'default' 78 79 def __init__( 80 self, 81 init_config: NoneTypeEngineInitConfig, 82 init_resource: Optional[CharAndFontSamplerEngineInitResource] = None, 83 ): 84 super().__init__(init_config, init_resource) 85 86 assert init_resource 87 self.font_collection = init_resource.font_collection 88 self.lexicon_collection = init_resource.lexicon_collection 89 self.char_sampler_engine_executor_aggregator = init_resource.char_sampler_engine_executor_aggregator 90 91 @classmethod 92 def estimate_num_chars(cls, run_config: CharAndFontSamplerEngineRunConfig): 93 if run_config.num_chars: 94 return run_config.num_chars 95 96 if run_config.glyph_sequence == FontEngineRunConfigGlyphSequence.HORI_DEFAULT: 97 num_chars = run_config.width / run_config.height 98 elif run_config.glyph_sequence == FontEngineRunConfigGlyphSequence.VERT_DEFAULT: 99 num_chars = run_config.height / run_config.width 100 else: 101 raise NotImplementedError() 102 103 num_chars *= run_config.num_chars_factor 104 return math.ceil(num_chars) 105 106 def run( 107 self, 108 run_config: CharAndFontSamplerEngineRunConfig, 109 rng: Optional[RandomGenerator] = None, 110 ) -> Optional[CharAndFont]: 111 assert rng is not None 112 113 # Sample chars. 114 num_chars = self.estimate_num_chars(run_config) 115 chars = self.char_sampler_engine_executor_aggregator.run( 116 CharSamplerEngineRunConfig( 117 num_chars=num_chars, 118 enable_aggregator_mode=True, 119 ), 120 rng, 121 ) 122 logger.debug(f'chars={chars}') 123 124 # Sample font variant. 125 font_metas = self.font_collection.filter_font_metas(chars) 126 if not font_metas: 127 logger.warning(f'Cannot sample font_metas for chars={chars}') 128 return None 129 130 font_meta = rng_choice(rng, font_metas) 131 variant_idx = int(rng.integers(0, font_meta.num_font_variants)) 132 font_variant = font_meta.get_font_variant(variant_idx) 133 134 return CharAndFont(chars=chars, font_variant=font_variant)
Abstract base class for generic types.
A generic type is typically declared by inheriting from this class parameterized with one or more type variables. For example, a generic mapping type might be defined as::
class Mapping(Generic[KT, VT]): def __getitem__(self, key: KT) -> VT: ... # Etc.
This class can then be used as follows::
def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT: try: return mapping[key] except KeyError: return default
CharAndFontSamplerEngine( init_config: vkit.engine.interface.NoneTypeEngineInitConfig, init_resource: Union[vkit.engine.char_and_font_sampler.CharAndFontSamplerEngineInitResource, NoneType] = None)
79 def __init__( 80 self, 81 init_config: NoneTypeEngineInitConfig, 82 init_resource: Optional[CharAndFontSamplerEngineInitResource] = None, 83 ): 84 super().__init__(init_config, init_resource) 85 86 assert init_resource 87 self.font_collection = init_resource.font_collection 88 self.lexicon_collection = init_resource.lexicon_collection 89 self.char_sampler_engine_executor_aggregator = init_resource.char_sampler_engine_executor_aggregator
@classmethod
def
estimate_num_chars( cls, run_config: vkit.engine.char_and_font_sampler.CharAndFontSamplerEngineRunConfig):
91 @classmethod 92 def estimate_num_chars(cls, run_config: CharAndFontSamplerEngineRunConfig): 93 if run_config.num_chars: 94 return run_config.num_chars 95 96 if run_config.glyph_sequence == FontEngineRunConfigGlyphSequence.HORI_DEFAULT: 97 num_chars = run_config.width / run_config.height 98 elif run_config.glyph_sequence == FontEngineRunConfigGlyphSequence.VERT_DEFAULT: 99 num_chars = run_config.height / run_config.width 100 else: 101 raise NotImplementedError() 102 103 num_chars *= run_config.num_chars_factor 104 return math.ceil(num_chars)
def
run( self, run_config: vkit.engine.char_and_font_sampler.CharAndFontSamplerEngineRunConfig, rng: Union[numpy.random._generator.Generator, NoneType] = None) -> Union[vkit.engine.char_and_font_sampler.CharAndFont, NoneType]:
106 def run( 107 self, 108 run_config: CharAndFontSamplerEngineRunConfig, 109 rng: Optional[RandomGenerator] = None, 110 ) -> Optional[CharAndFont]: 111 assert rng is not None 112 113 # Sample chars. 114 num_chars = self.estimate_num_chars(run_config) 115 chars = self.char_sampler_engine_executor_aggregator.run( 116 CharSamplerEngineRunConfig( 117 num_chars=num_chars, 118 enable_aggregator_mode=True, 119 ), 120 rng, 121 ) 122 logger.debug(f'chars={chars}') 123 124 # Sample font variant. 125 font_metas = self.font_collection.filter_font_metas(chars) 126 if not font_metas: 127 logger.warning(f'Cannot sample font_metas for chars={chars}') 128 return None 129 130 font_meta = rng_choice(rng, font_metas) 131 variant_idx = int(rng.integers(0, font_meta.num_font_variants)) 132 font_variant = font_meta.get_font_variant(variant_idx) 133 134 return CharAndFont(chars=chars, font_variant=font_variant)