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:
61class CharAndFont:
62    chars: Sequence[str]
63    font_variant: FontVariant
CharAndFont( chars: Sequence[str], font_variant: vkit.engine.font.type.FontVariant)
2def __init__(self, chars, font_variant):
3    self.chars = chars
4    self.font_variant = font_variant

Method generated by attrs for class CharAndFont.

 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 get_type_name(cls) -> str:
75    @classmethod
76    def get_type_name(cls) -> str:
77        return 'default'
@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)