Configuration
Example configuration. Covers supports multiple covers to be configured. Here is an example from my blog where we have a template sized for dev.to and one sized for open graph. Each image takes it's own configuration.
[[markata.covers]]
name='-dev'
template = "static/cover-template.png"
font = "./static/JosefinSans-Regular.ttf"
text_font = "./static/JosefinSans-Regular.ttf"
font_color = "rgb(185,155,165)"
text_font_color = "rgb(255,255,255)"
text_key = 'description'
padding = [0, 40, 100, 300]
text_padding = [0,0]
[[markata.covers]]
name=''
template = "static/og-template.png"
font = "./static/JosefinSans-Regular.ttf"
font_color = "rgb(255,255,255)"
text_font = "./static/JosefinSans-Regular.ttf"
text_font_color = "rgb(200,200,200)"
text_key = 'description'
padding = [10, 10, 100, 300]
text_padding = [0,0]
_load_font function
None
_load_font source
def _load_font(path: Path, size: int) -> ImageFont.FreeTypeFont:
return ImageFont.truetype(path, size=size)
get_font function
None
get_font source
def get_font(
path: Path,
draw: ImageDraw.Draw,
title: str,
size: int = 250,
max_size: tuple = (800, 220),
) -> ImageFont.FreeTypeFont:
title = title or ""
font = _load_font(path, size)
current_size = draw.textsize(title, font=font)
if current_size[0] > max_size[0] or current_size[1] > max_size[1]:
return get_font(path, draw, title, size - 10, max_size=max_size)
return font
PaddingError class
None
PaddingError source
class PaddingError(BaseException):
def __init__(
self,
msg: str = "",
) -> None:
super().__init__(
"Padding must be an iterable of length 1, 2, 3, or 4.\n" + msg,
)
draw_text function
None
draw_text source
def draw_text(
image: Image,
font_path: Optional[Path],
text: str,
color: Union[str, None],
padding: Tuple[int, ...],
) -> None:
text = text or ""
draw = ImageDraw.Draw(image)
padding = resolve_padding(padding)
width = image.size[0]
height = image.size[1]
bounding_box = [padding[0], padding[1], width - padding[0], height - padding[1]]
bounding_box = [padding[0], padding[1], width - padding[2], height - padding[3]]
max_size = (bounding_box[2] - bounding_box[0], bounding_box[3] - bounding_box[1])
x1, y1, x2, y2 = bounding_box
if font_path:
font = get_font(font_path, draw, text, max_size=max_size)
else:
font = None
w, h = draw.textsize(text, font=font)
x = (x2 - x1 - w) / 2 + x1
y = (y2 - y1 - h) / 2 + y1
draw.text((x, y), text, fill=color, font=font, align="center")
resolve_padding function
Convert padding to a len 4 tuple
resolve_padding source
def resolve_padding(padding: Tuple[int, ...]) -> Tuple[int, ...]:
"""Convert padding to a len 4 tuple"""
if len(padding) == 4:
return padding
if len(padding) == 3:
return (*padding, padding[1])
if len(padding) == 2:
return padding * 2
if len(padding) == 1:
return padding * 4
raise PaddingError(f"recieved padding: {padding}")
make_cover function
None
make_cover source
def make_cover(
title: str,
color: str,
output_path: Path,
template_path: Path,
font_path: Optional[Path],
padding: Tuple[int, ...],
text_font: Path,
text: str = None,
text_font_color: str = None,
text_padding: Tuple[int, ...] = None,
resizes: List[int] = None,
) -> None:
if output_path.exists():
return
if template_path:
image = Image.open(template_path)
else:
image = Image.new("RGB", (800, 450))
draw_text(image, font_path, title, color, padding)
if text is not None:
if text_padding is None:
text_padding = (
image.size[1] - image.size[1] / 5,
image.size[0] / 5,
image.size[1] - image.size[1] / 10,
)
draw_text(image, text_font, text, text_font_color, text_padding)
output_path.parent.mkdir(exist_ok=True)
image.save(output_path)
ratio = image.size[1] / image.size[0]
covers = []
if resizes:
for width in resizes:
re_img = image.resize((width, int(width * ratio)), Image.ANTIALIAS)
filename = (
f"{output_path.stem}_{width}x{int(width*ratio)}{output_path.suffix}"
)
covers.append(filename)
filepath = Path(output_path.parent / filename)
re_img.save(filepath)
save function
None
save source
def save(markata: "Markata") -> None:
futures = []
if "covers" not in markata.config.keys():
return
for article in markata.iter_articles("making covers"):
for cover in markata.config["covers"]:
try:
padding = cover["padding"]
except KeyError:
padding = (
200,
100,
)
try:
text_padding = cover["text_padding"]
except KeyError:
text_padding = (
200,
100,
)
if "text_key" in cover:
try:
text = article.metadata[cover["text_key"]]
except AttributeError:
text = article[cover["text_key"]]
try:
text = text.replace("\n", "")
from more_itertools import chunked
text = "\n".join(["".join(c) for c in chunked(text, 60)])
except AttributeError:
# text is likely None
pass
text_font = cover["text_font"]
text_font_color = cover["text_font_color"]
else:
text = None
text_font = None
text_font_color = None
try:
title = article.metadata["title"]
except AttributeError:
title = article["title"]
futures.append(
make_cover(
title=title,
color=cover["font_color"],
output_path=Path(markata.config["output_dir"])
/ (article["slug"] + cover["name"] + ".png"),
template_path=cover.get("template", None),
font_path=cover.get("font", None),
padding=padding,
text_font=text_font,
text=text,
text_font_color=text_font_color,
text_padding=text_padding,
resizes=cover.get("resizes"),
)
)
progress = Progress(
BarColumn(bar_width=None), transient=True, console=markata.console
)
task_id = progress.add_task("loading markdown")
progress.update(task_id, total=len(futures))
with progress:
while not all([f.done() for f in futures]):
time.sleep(0.1)
progress.update(task_id, total=len([f for f in futures if f.done()]))
[f.result() for f in futures]
init method
None
init source
def __init__(
self,
msg: str = "",
) -> None:
super().__init__(
"Padding must be an iterable of length 1, 2, 3, or 4.\n" + msg,
)