Skip to content

Commit 15bd3e8

Browse files
committed
Allow setting font path and font color by frequency
1 parent a75fc83 commit 15bd3e8

File tree

7 files changed

+57
-18
lines changed

7 files changed

+57
-18
lines changed

.pylintrc

+1-1
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ max-spelling-suggestions=2
418418
[DESIGN]
419419

420420
# Maximum number of arguments for function / method
421-
max-args=10
421+
max-args=15
422422

423423
# Maximum number of locals for function / method body
424424
max-locals=25

examples/sherlock.py

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import math
12
from pathlib import Path
23

34
from wrdcld import make_word_cloud
@@ -7,9 +8,17 @@
78
all_words = [word.strip(" \n,.!?:-&\"'[]") for word in contents.split(" ")]
89
all_words = [word for word in all_words if word]
910

10-
make_word_cloud(
11+
12+
def color_func(frequency):
13+
x = int((1 - frequency) * 200)
14+
return (x, x, x)
15+
16+
17+
img = make_word_cloud(
1118
all_words=all_words,
12-
font_color=(0, 0, 0),
19+
font_color_func=color_func,
1320
background_color=(255, 255, 255),
1421
minimum_font_size=5,
15-
).show()
22+
)
23+
24+
img.show()

wrdcld/__init__.py

+17-5
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
11
import math
22
from collections import Counter
33
from collections.abc import Callable
4+
from pathlib import Path
45

56
from .font import FontWrapper
67
from .image import ImageWrapper
78
from .main import fill_next_word
89
from .rectangle import Rectangle
10+
from .util import Color, get_repo_root
911

1012

1113
# pylint: disable=unused-argument
1214
def make_word_cloud(
1315
all_words: list[str],
1416
width: int = 500,
1517
height: int = 500,
16-
font_color: tuple[int, int, int] = (255, 255, 0),
17-
background_color: tuple[int, int, int] = (73, 109, 137),
18+
font_path: Path = None,
19+
font_color: Color = (255, 255, 0),
20+
font_color_func: Callable[[float], Color] = None,
21+
background_color: Color = (73, 109, 137),
1822
minimum_font_size: int = 1,
1923
maximum_font_size: int = 100,
2024
word_padding: int = 0, # TODO
@@ -28,24 +32,32 @@ def make_word_cloud(
2832
assert (
2933
0 < minimum_font_size < maximum_font_size
3034
), "Invalid font sizes, must be positive (in pixels)"
35+
assert (
36+
font_color is not None or font_color_func is not None
37+
), "Must specify a fixed font color or function"
3138

3239
# Create a new image and font
40+
font_path = font_path or (get_repo_root() / "fonts" / "OpenSans-Regular.ttf")
41+
font_color_func = font_color_func or (lambda _: font_color)
3342
image = ImageWrapper(width, height, background_color)
34-
font = FontWrapper(color=font_color, size=maximum_font_size)
43+
font = FontWrapper(
44+
path=font_path, color_func=font_color_func, size=maximum_font_size
45+
)
3546

3647
# Handle data
3748
word_counts = Counter(all_words)
3849
_, first_count = word_counts.most_common(1)[0]
3950

4051
available_rectangles = [Rectangle(width=width, height=height, x=0, y=0)]
4152
for word, count in word_counts.most_common():
42-
required_font_size = maximum_font_size * scaling_func(count / first_count)
53+
frequency = count / first_count
54+
required_font_size = maximum_font_size * scaling_func(frequency)
4355

4456
if required_font_size < minimum_font_size:
4557
break
4658

4759
available_rectangles = fill_next_word(
48-
word, available_rectangles, image, font[required_font_size]
60+
word, available_rectangles, image, font[required_font_size], frequency
4961
)
5062

5163
return image.img

wrdcld/font.py

+13-5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from collections.abc import Callable
12
from dataclasses import dataclass, replace
23
from functools import lru_cache
34
from pathlib import Path
@@ -6,14 +7,17 @@
67

78
from .image import ImageWrapper
89
from .rectangle import Rectangle
9-
from .util import get_repo_root
10+
from .util import Color
1011

1112

1213
@dataclass(frozen=True)
1314
class FontWrapper:
14-
path: Path = get_repo_root() / "fonts" / "OpenSans-Regular.ttf"
15+
color_func: Callable[[float], Color]
16+
path: Path
1517
size: int = 1
16-
color: tuple[int, int, int] = (255, 255, 0)
18+
19+
def color(self, frequency: float) -> Color:
20+
return self.color_func(frequency)
1721

1822
@lru_cache(maxsize=1024)
1923
def get(self):
@@ -39,6 +43,7 @@ def draw_text(
3943
rectangle: Rectangle,
4044
word: str,
4145
font: FontWrapper,
46+
frequency: float,
4247
rotate=False,
4348
):
4449
"""
@@ -52,7 +57,10 @@ def draw_text(
5257
text_draw = ImageDraw.Draw(text_image)
5358

5459
text_draw.text(
55-
(-text_bbox.x, -text_bbox.y), word, font=font.get(), fill=font.color
60+
(-text_bbox.x, -text_bbox.y),
61+
word,
62+
font=font.get(),
63+
fill=font.color(frequency),
5664
)
5765
rotated_text_image = text_image.rotate(90, expand=True)
5866
image.img.paste(rotated_text_image, rectangle.xy)
@@ -62,5 +70,5 @@ def draw_text(
6270
(rectangle.x - text_bbox.x, rectangle.y - text_bbox.y),
6371
word,
6472
font=font.get(),
65-
fill=font.color,
73+
fill=font.color(frequency),
6674
)

wrdcld/image.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
from PIL import Image, ImageDraw
22

3+
from .util import Color
4+
35

46
class ImageWrapper:
5-
def __init__(self, width: int, height: int, background_color: tuple[int, int, int]):
7+
def __init__(self, width: int, height: int, background_color: Color):
68
self.width = width
79
self.height = height
810
self.background_color = background_color

wrdcld/main.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ def _fill(
1616
word_length: int,
1717
word: str,
1818
font: FontWrapper,
19+
frequency: float,
1920
rotate: bool = False,
2021
):
2122
word_height = font.size
@@ -47,12 +48,12 @@ def _fill(
4748
height=word_length,
4849
)
4950

50-
draw_text(image, text_rectangle, word, font, rotate=rotate)
51+
draw_text(image, text_rectangle, word, font, frequency, rotate=rotate)
5152

5253
return text_rectangle
5354

5455

55-
def fill_next_word(word, available_rectangles, image, font):
56+
def fill_next_word(word, available_rectangles, image, font, frequency):
5657
word_length = font.get_length_of_word(word)
5758

5859
suitable_horizontal_rectangles = [
@@ -96,7 +97,9 @@ def fill_next_word(word, available_rectangles, image, font):
9697
if option == "horizontal":
9798
available_rectangles.remove(horizontal_option)
9899
chosen_rectangle = horizontal_option
99-
text_rectangle = _fill(chosen_rectangle, image, word_length, word, font)
100+
text_rectangle = _fill(
101+
chosen_rectangle, image, word_length, word, font, frequency
102+
)
100103

101104
else:
102105
available_rectangles.remove(vertical_option)
@@ -107,6 +110,7 @@ def fill_next_word(word, available_rectangles, image, font):
107110
word_length,
108111
word,
109112
font,
113+
frequency,
110114
rotate=True,
111115
)
112116

wrdcld/util.py

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1+
from typing import TypeAlias
12
from pathlib import Path
23

34

5+
Color: TypeAlias = tuple[int, int, int]
6+
7+
48
def get_repo_root():
59
return Path(__file__).parent.parent

0 commit comments

Comments
 (0)