feat: sync full workspace including web modules, docs, and configurations to Gitea
Optimized the root .gitignore to exclude virtual environments, node modules, and temp folders to ensure clean and lightweight version tracking. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
129
docs/render_salary_notice_png.py
Normal file
129
docs/render_salary_notice_png.py
Normal file
@@ -0,0 +1,129 @@
|
||||
#!/usr/bin/env python3
|
||||
"""将「关于暂缓下月工资发放事宜的通知」正文叠到扫描件上,输出 工资通知-暂缓下月.png"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import textwrap
|
||||
from pathlib import Path
|
||||
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
ROOT = Path(__file__).resolve().parent
|
||||
SRC_PNG = ROOT / "工资通知-原图.png"
|
||||
TXT = ROOT / "关于暂缓下月工资发放事宜的通知.txt"
|
||||
OUT = ROOT / "工资通知-暂缓下月.png"
|
||||
|
||||
SONGTI = "/System/Library/Fonts/Supplemental/Songti.ttc"
|
||||
|
||||
MARGIN_L = 72
|
||||
MARGIN_R = 72
|
||||
LINE_H = 34
|
||||
TITLE_Y = 128
|
||||
CHARS_PER_LINE = 24
|
||||
|
||||
|
||||
def load_font(size: int, bold: bool = False) -> ImageFont.FreeTypeFont:
|
||||
idx = 1 if bold else 7
|
||||
return ImageFont.truetype(SONGTI, size, encoding="utf-8", index=idx)
|
||||
|
||||
|
||||
def text_width(s: str, font: ImageFont.FreeTypeFont, draw: ImageDraw.ImageDraw) -> int:
|
||||
bbox = draw.textbbox((0, 0), s, font=font)
|
||||
return bbox[2] - bbox[0]
|
||||
|
||||
|
||||
def wrap_line_to_chars(s: str, width: int) -> list[str]:
|
||||
s = s.strip()
|
||||
if not s:
|
||||
return []
|
||||
return textwrap.wrap(s, width=width, break_long_words=False, break_on_hyphens=False)
|
||||
|
||||
|
||||
def draw_body_paragraph(
|
||||
draw: ImageDraw.ImageDraw,
|
||||
y: int,
|
||||
text: str,
|
||||
font: ImageFont.FreeTypeFont,
|
||||
x0: int,
|
||||
line_h: int,
|
||||
first_indent: bool,
|
||||
) -> int:
|
||||
max_chars = CHARS_PER_LINE - (2 if first_indent else 0)
|
||||
parts = wrap_line_to_chars(text, max_chars)
|
||||
for part in parts:
|
||||
line = (" " + part) if first_indent else part
|
||||
draw.text((x0, y), line, font=font, fill=(30, 30, 30))
|
||||
y += line_h
|
||||
return y
|
||||
|
||||
|
||||
def main() -> None:
|
||||
im = Image.open(SRC_PNG).convert("RGB")
|
||||
draw = ImageDraw.Draw(im)
|
||||
w, _h = im.size
|
||||
paper = (255, 255, 255)
|
||||
|
||||
# 覆盖标题与正文,下缘略低于原「正文末行」留白,整块至公章上沿(公章约自 y≥708)
|
||||
draw.rectangle([32, 114, w - 34, 706], fill=paper)
|
||||
# y=706–716 狭长带:盖住正文与公章之间的浅灰水印,仅涂左侧留白,不压住公章顶边圆弧
|
||||
draw.rectangle([36, 706, 446, 716], fill=paper)
|
||||
# 清空左下角旧署名/日期笔迹(不覆盖右下角公章 x≈453–596)
|
||||
draw.rectangle([120, 716, 446, 832], fill=paper)
|
||||
|
||||
raw = TXT.read_text(encoding="utf-8")
|
||||
lines = [ln.rstrip() for ln in raw.splitlines()]
|
||||
title = lines[0].strip()
|
||||
i = 1
|
||||
while i < len(lines) and not lines[i].strip():
|
||||
i += 1
|
||||
|
||||
font_title = load_font(26, bold=True)
|
||||
font_body = load_font(22, bold=False)
|
||||
x0 = MARGIN_L
|
||||
max_px = w - MARGIN_L - MARGIN_R
|
||||
|
||||
y = TITLE_Y
|
||||
tw = text_width(title, font_title, draw)
|
||||
draw.text(((w - tw) // 2, y), title, font=font_title, fill=(30, 30, 30))
|
||||
y += 54
|
||||
|
||||
while i < len(lines):
|
||||
line = lines[i].strip()
|
||||
i += 1
|
||||
if not line:
|
||||
y += LINE_H // 2
|
||||
continue
|
||||
|
||||
if line == "全体员工:":
|
||||
draw.text((x0, y), line, font=font_body, fill=(30, 30, 30))
|
||||
y += LINE_H
|
||||
continue
|
||||
|
||||
if line == "特此通知。":
|
||||
twl = text_width(line, font_body, draw)
|
||||
draw.text((x0 + max_px - twl, y), line, font=font_body, fill=(30, 30, 30))
|
||||
y += LINE_H * 2
|
||||
continue
|
||||
|
||||
if line.startswith("浙江羚牛"):
|
||||
twl = text_width(line, font_body, draw)
|
||||
draw.text((x0 + max_px - twl, y), line, font=font_body, fill=(30, 30, 30))
|
||||
y += LINE_H
|
||||
continue
|
||||
|
||||
if re.match(r"^\d{4}年\d{1,2}月\d{1,2}日$", line):
|
||||
twl = text_width(line, font_body, draw)
|
||||
draw.text((x0 + max_px - twl, y), line, font=font_body, fill=(30, 30, 30))
|
||||
y += LINE_H
|
||||
continue
|
||||
|
||||
first_indent = not bool(re.match(r"^[一二三四五六七八九十]+、", line))
|
||||
y = draw_body_paragraph(draw, y, line, font_body, x0, LINE_H, first_indent)
|
||||
|
||||
im.save(OUT, format="PNG", optimize=True)
|
||||
print(f"写入 {OUT}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user