Skip to content

Commit

Permalink
Optimization
Browse files Browse the repository at this point in the history
  • Loading branch information
SkyEye-FAST committed Sep 21, 2024
1 parent e57f440 commit 5898fe3
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 45 deletions.
21 changes: 21 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
@@ -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.
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>"]
license = "Apache-2.0"
readme = ["README.md", "README_zh.md"]
readme = "README.rst"
repository = "https://github.com/SkyEye-FAST/unifont_utils"
keywords = ["unifont"]
classifiers = [
Expand Down
32 changes: 18 additions & 14 deletions unifont_utils/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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]:
Expand All @@ -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]
Expand Down Expand Up @@ -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:
Expand Down
27 changes: 19 additions & 8 deletions unifont_utils/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand All @@ -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)


Expand Down Expand Up @@ -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]:
Expand Down
40 changes: 20 additions & 20 deletions unifont_utils/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,29 @@ 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.
"""

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

Expand All @@ -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()
Expand All @@ -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."
)


Expand All @@ -74,24 +77,21 @@ 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".
"""

start_time = time.time()

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))
Expand All @@ -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."
)
1 change: 0 additions & 1 deletion unifont_utils/glyphs.py
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down

0 comments on commit 5898fe3

Please sign in to comment.