From 5898fe3ff99bdf59cc77c5054034d3988f04239d Mon Sep 17 00:00:00 2001 From: SkyEye_FAST Date: Sat, 21 Sep 2024 22:17:47 +0800 Subject: [PATCH] Optimization --- README.rst | 21 ++++++++++++++++++++ pyproject.toml | 4 ++-- unifont_utils/base.py | 32 +++++++++++++++++------------- unifont_utils/converter.py | 27 +++++++++++++++++-------- unifont_utils/files.py | 40 +++++++++++++++++++------------------- unifont_utils/glyphs.py | 1 - 6 files changed, 80 insertions(+), 45 deletions(-) create mode 100644 README.rst diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..10d6c95 --- /dev/null +++ b/README.rst @@ -0,0 +1,21 @@ +Unifont Utils +============= + +This project provides a set of tools for working with the Unifont font. + +Installation +------------ + +Install the package from PyPI using the following command: + +.. code-block:: shell + + pip install unifont_utils + +Feedback +-------- + +Please feel free to raise issues for any problems encountered or +feature suggestions. + +Pull requests are welcome. diff --git a/pyproject.toml b/pyproject.toml index 15c7f97..3c07422 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,10 @@ [tool.poetry] name = "unifont_utils" -version = "0.1.1" +version = "0.1.2" description = "Unifont Utilities, but rewritten in Python." authors = ["SkyEye_FAST "] license = "Apache-2.0" -readme = ["README.md", "README_zh.md"] +readme = "README.rst" repository = "https://github.com/SkyEye-FAST/unifont_utils" keywords = ["unifont"] classifiers = [ diff --git a/unifont_utils/base.py b/unifont_utils/base.py index efe621e..070b6c2 100644 --- a/unifont_utils/base.py +++ b/unifont_utils/base.py @@ -10,11 +10,11 @@ CodePoints: TypeAlias = Union[str, Tuple[CodePoint, CodePoint]] -def validate_code_point(code_point: Union[str, int]) -> str: +def validate_code_point(code_point: CodePoint) -> str: """Validate a code point string and return its normalized form. Args: - code_point (str): The code point string to validate. + code_point (CodePoint): The code point string to validate. Returns: str: The normalized code point if valid. @@ -26,18 +26,17 @@ def validate_code_point(code_point: Union[str, int]) -> str: if not isinstance(code_point, (str, int)): raise ValueError("Invalid code point type. Must be a string or integer.") if isinstance(code_point, int): - code_point = hex(code_point)[2:].upper() + code_point = hex(code_point)[2:] if not code_point.isalnum() or len(code_point) >= 7: - raise ValueError("Invalid code point.") + raise ValueError(f"Invalid code point: {code_point}.") code_point = code_point.upper() - if not all(c in "0123456789ABCDEF" for c in code_point): - raise ValueError("Invalid character in code point.") + for c in code_point: + if c not in "0123456789ABCDEF": + raise ValueError(f"Invalid character in code point: {c}.") - if len(code_point) >= 5: - return code_point.zfill(6) - return code_point.zfill(4) + return code_point.zfill(6 if len(code_point) > 4 else 4) def validate_code_points(code_points: CodePoints) -> List[str]: @@ -64,10 +63,14 @@ def validate_code_points(code_points: CodePoints) -> List[str]: code_points_list = code_points.split(",") else: if len(code_points) != 2: - raise ValueError("The tuple must contain exactly two elements (begin, end).") + raise ValueError( + "The tuple must contain exactly two elements (begin, end)." + ) begin, end = code_points if not isinstance(begin, (str, int)) or not isinstance(end, (str, int)): - raise TypeError("The begin and end code points must be strings or integers.") + raise TypeError( + "The begin and end code points must be strings or integers." + ) begin, end = validate_code_point(begin), validate_code_point(end) code_points_list = range(int(begin, 16), int(end, 16) + 1) code_points_list = [hex(i)[2:].zfill(4) for i in code_points_list] @@ -96,10 +99,11 @@ def validate_hex_str(hex_str: Optional[str]) -> Optional[str]: f"Invalid .hex string length: {hex_str} (length: {len(hex_str)})." ) - if not all(c in "0123456789ABCDEF" for c in hex_str): - raise ValueError("Invalid character in .hex string.") + for c in hex_str: + if c not in "0123456789ABCDEF": + raise ValueError(f"Invalid character in .hex string: {c}.") - return hex_str.upper() if hex_str else "" + return hex_str.upper() def validate_file_path(file_path: FilePath) -> Path: diff --git a/unifont_utils/converter.py b/unifont_utils/converter.py index 18fd872..c39581f 100644 --- a/unifont_utils/converter.py +++ b/unifont_utils/converter.py @@ -105,7 +105,6 @@ def print_glyph( Defaults to `False`. If `True`, the hexadecimal string of each line will be displayed on the left. - display_bin (bool, optional): Whether to display the binary strings. Defaults to `False`. @@ -122,24 +121,33 @@ def print_glyph( else ("\033[0;37;47m ", "\033[0;37;40m ", "\033[0m") ) - black_and_white = black_and_white or self.black_and_white + black_and_white = ( + black_and_white if black_and_white is not None else self.black_and_white + ) if black_and_white: white_block, black_block = black_block, white_block + hex_length = self.width // 4 if display_hex else None + for i in range(self.height): row = "".join( white_block if self.data[i * self.width + j] else black_block for j in range(self.width) ) - if display_hex: - length = self.width // 4 - hex_slice = self.hex_str[i * length : (i + 1) * length] + + if display_hex or display_bin: + prefix = [] + if display_hex: + hex_slice = self.hex_str[i * hex_length : (i + 1) * hex_length] + prefix.append(hex_slice) if display_bin: bin_slice = "".join( str(self.data[i * self.width + j]) for j in range(self.width) ) - row = f"{bin_slice}\t{row}" - row = f"{hex_slice}\t{row}" + prefix.append(bin_slice) + + row = "\t".join(prefix) + "\t" + row + print(row + new_line) @@ -198,7 +206,10 @@ def __init__(self, hex_str: str, black_and_white: bool = True) -> None: self.hex_str = validate_hex_str(hex_str) self.width, self.height = (16, 16) if len(hex_str) == 64 else (8, 16) super().__init__( - self._to_img_data(), self.hex_str, (self.width, self.height), black_and_white + self._to_img_data(), + self.hex_str, + (self.width, self.height), + black_and_white, ) def _to_img_data(self) -> List[int]: diff --git a/unifont_utils/files.py b/unifont_utils/files.py index 04a8487..988863d 100644 --- a/unifont_utils/files.py +++ b/unifont_utils/files.py @@ -16,8 +16,6 @@ def load_hex_file(file_path: FilePath) -> GlyphSet: Args: file_path (FilePath): The path to the `.hex` file. - If a string is provided, it will be converted to a Path object. - Returns: GlyphSet: Obtained glyphs. """ @@ -25,16 +23,22 @@ def load_hex_file(file_path: FilePath) -> GlyphSet: start_time = time.time() file_path = validate_file_path(file_path) + if not file_path.exists(): + raise FileNotFoundError(f"File not found: {file_path}") + glyphs = GlyphSet() with file_path.open("r", encoding="utf-8") as f: for line in f: + if ":" not in line: + raise ValueError(f"Invalid line in file: {line}") code_point, hex_str = line.strip().split(":") glyphs.add_glyph((code_point, hex_str)) elapsed_time = time.time() - start_time print( - f'Loaded {len(glyphs)} glyphs from "{file_path.name}". Time elapsed: {elapsed_time:.2f} s.' + f'Loaded {len(glyphs)} glyphs from "{file_path.name}".' + f"Time elapsed: {elapsed_time:.2f} s." ) return glyphs @@ -45,8 +49,6 @@ def save_hex_file(glyphs: GlyphSet, file_path: FilePath) -> None: Args: glyphs (GlyphSet): The glyphs to save. file_path (FilePath): The path to the `.hex` file. - - If a string is provided, it will be converted to a Path object. """ start_time = time.time() @@ -58,7 +60,8 @@ def save_hex_file(glyphs: GlyphSet, file_path: FilePath) -> None: elapsed_time = time.time() - start_time print( - f'Saved {len(glyphs)} glyphs to "{file_path.name}". Time elapsed: {elapsed_time:.2f} s.' + f'Saved {len(glyphs)} glyphs to "{file_path.name}".' + f"Time elapsed: {elapsed_time:.2f} s." ) @@ -74,8 +77,6 @@ def save_unicode_page( Args: glyphs (GlyphSet): The glyphs to save. file_path (FilePath): The path to the Unicode page file. - - If a string is provided, it will be converted to a Path object. start (CodePoint, optional): The starting Unicode code point. Defaults to "4E00". """ @@ -83,15 +84,14 @@ def save_unicode_page( glyphs.sort_glyphs() file_path = validate_file_path(file_path) - if isinstance(start, str): - start = int(start, 16) + start = int(start, 16) if isinstance(start, str) else start img = Img.new("RGBA", (256, 256)) - x, y, count = 0, 0, 0 + position = 0 for code_point, glyph in glyphs.glyphs.items(): - code_point = int(code_point, 16) - if code_point < start: + if int(code_point, 16) < start: continue + if glyph.hex_str: converter = HexConverter(glyph.hex_str) glyph_img = Img.new("RGBA", (16, 16)) @@ -100,19 +100,19 @@ def save_unicode_page( for pixel in converter.data ] glyph_img.putdata(rgba_data) + + x = (position % 16) * 16 + y = (position // 16) * 16 img.paste(glyph_img, (x, y)) - count += 1 - x += 16 - if x >= 256: - x = 0 - y += 16 - if y >= 256: + position += 1 + if position >= 256: break img.save(file_path) elapsed_time = time.time() - start_time print( - f'Saved {count} glyphs to "{file_path.name}". Time elapsed: {elapsed_time:.2f} s.' + f'Saved {position} glyphs to "{file_path.name}".' + f" Time elapsed: {elapsed_time:.2f} s." ) diff --git a/unifont_utils/glyphs.py b/unifont_utils/glyphs.py index e292652..dafa4a3 100644 --- a/unifont_utils/glyphs.py +++ b/unifont_utils/glyphs.py @@ -60,7 +60,6 @@ def print_glyph( Defaults to `False`. If `True`, the hexadecimal string of each line will be displayed on the left. - display_bin (bool, optional): Whether to display the binary strings. Defaults to `False`.