Move to real LaTeX rendering
parent
6222ba5dac
commit
2677e41b04
|
|
@ -219,6 +219,71 @@ def render_latex_text(text, width_px, bg_color=(255, 255, 255, 255), max_lines=N
|
|||
final_img.alpha_composite(img)
|
||||
return final_img
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
import subprocess
|
||||
import PIL.ImageOps
|
||||
|
||||
|
||||
def render_real_latex_text(text, width_px, bg_color=(255, 255, 255, 255), max_lines=None, fontsize=19):
|
||||
dpi = 100
|
||||
width_in = width_px / dpi
|
||||
line_spacing = int(fontsize * 1.2)
|
||||
|
||||
# Use the 'standalone' class with 'varwidth' to auto-crop height while restricting width
|
||||
latex_template = f"""\\documentclass[varwidth={width_in}in,margin=0.2cm]{{standalone}}
|
||||
\\usepackage[utf8]{{inputenc}}
|
||||
\\usepackage[T1]{{fontenc}}
|
||||
\\usepackage{{lmodern}} % Enables arbitrary font scaling
|
||||
\\usepackage{{amsmath, amssymb}}
|
||||
%\\usepackage{{anyfontsize}} % replaces by lmodern
|
||||
\\begin{{document}}
|
||||
\\fontsize{{{fontsize}}}{{{line_spacing}}}\\selectfont
|
||||
{text}
|
||||
\\end{{document}}
|
||||
"""
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
tex_path = os.path.join(temp_dir, 'text.tex')
|
||||
pdf_path = os.path.join(temp_dir, 'text.pdf')
|
||||
|
||||
with open(tex_path, 'w', encoding='utf-8') as f:
|
||||
f.write(latex_template)
|
||||
|
||||
# Compile to PDF
|
||||
result = subprocess.run(
|
||||
['pdflatex', '-interaction=nonstopmode', 'text.tex'],
|
||||
cwd=temp_dir,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL
|
||||
)
|
||||
|
||||
if not os.path.exists(pdf_path):
|
||||
raise RuntimeError("LaTeX compilation failed. Check your LaTeX syntax.")
|
||||
|
||||
# Convert PDF to grayscale (ignoring pdf2image's broken transparency)
|
||||
images = convert_from_path(pdf_path, dpi=dpi)
|
||||
gray_img = images[0].convert("L")
|
||||
|
||||
# 1. Invert grayscale to create an alpha mask (white bg = 0, black text = 255)
|
||||
alpha_mask = PIL.ImageOps.invert(gray_img)
|
||||
|
||||
# 2. Create a transparent image with black text using the mask
|
||||
text_img = Image.new("RGBA", gray_img.size, (0, 0, 0, 255))
|
||||
text_img.putalpha(alpha_mask)
|
||||
|
||||
# 3. Create the requested background and composite the text over it
|
||||
final_img = Image.new("RGBA", text_img.size, bg_color)
|
||||
final_img.alpha_composite(text_img)
|
||||
|
||||
# (Optional) Truncate image height if max_lines is strictly enforced
|
||||
if max_lines:
|
||||
max_height_px = int((fontsize * 1.2 / 72.0) * dpi * max_lines) # Points to pixels
|
||||
if final_img.height > max_height_px:
|
||||
final_img = final_img.crop((0, 0, final_img.width, max_height_px))
|
||||
|
||||
return final_img
|
||||
|
||||
import io
|
||||
from PIL import Image
|
||||
import matplotlib.pyplot as plt
|
||||
|
|
@ -376,7 +441,7 @@ def compose_label_image(base_img, label, result, hmin,
|
|||
|
||||
# Render Text
|
||||
txt_img = render_fn(fb['text'], width_px=ANNOT_WIDTH,
|
||||
bg_color=(255, 200, 200, 180), max_lines=3)
|
||||
bg_color=(255, 200, 200, 180), max_lines=None)
|
||||
|
||||
# Calculate Position
|
||||
center_y = (target_ymin + target_ymax) / 2
|
||||
|
|
@ -458,7 +523,8 @@ def process_student(student_id, labels_data, root_dir, all_labels, overwrite):
|
|||
d_notes[label] = str(score)
|
||||
|
||||
final_img, _ = compose_label_image(base_img, label, result, coordinates[0],
|
||||
with_empty=True)
|
||||
with_empty=True,
|
||||
render_fn=render_real_latex_text)
|
||||
# 7. Save Image
|
||||
save_path = os.path.join(output_dir, f"{label}.jpg")
|
||||
final_img.save(save_path)
|
||||
|
|
|
|||
|
|
@ -39,8 +39,9 @@ def draw_checkbox(draw, x, y, size=BOX_SIZE, label=None, fill="white"):
|
|||
|
||||
def safe_render_latex(*args, **kwargs):
|
||||
"""Thread-safe wrapper for latex rendering."""
|
||||
with LATEX_LOCK:
|
||||
return annotating.render_latex_text(*args, **kwargs)
|
||||
# with LATEX_LOCK:
|
||||
# return annotating.render_latex_text(*args, **kwargs)
|
||||
return annotating.render_real_latex_text(*args, **kwargs)
|
||||
|
||||
class CheckboxRenderer:
|
||||
def __init__(self, label_name):
|
||||
|
|
|
|||
|
|
@ -226,6 +226,7 @@ def apply_actions_and_regenerate(root_dir, data, student_id, actions, notes_laye
|
|||
|
||||
# --- 2. Process Images (Cut notes, Regenerate, Concatenate) ---
|
||||
concat_list = []
|
||||
concat_list_F = []
|
||||
d_notes = dict.fromkeys(all_labels, "")
|
||||
|
||||
# Iterate over images defined in bnote.json to maintain order/geometry
|
||||
|
|
@ -235,7 +236,8 @@ def apply_actions_and_regenerate(root_dir, data, student_id, actions, notes_laye
|
|||
|
||||
# Update scores dict
|
||||
content = labels_data[label]
|
||||
d_notes[label] = str(content['result'].get('score', 0))
|
||||
result = content['result']
|
||||
d_notes[label] = str(result.get('score', 0))
|
||||
|
||||
# A. Cut Manual Notes
|
||||
hmin, hmax = img_info["hmin"], img_info["hmax"]
|
||||
|
|
@ -272,6 +274,14 @@ def apply_actions_and_regenerate(root_dir, data, student_id, actions, notes_laye
|
|||
|
||||
concat_list.append(final_img)
|
||||
|
||||
perfect_no_comment = True
|
||||
if float(d_notes[label]) != 4.0:
|
||||
perfect_no_comment = False
|
||||
if len(result.get('feedback', [])) != 0:
|
||||
perfect_no_comment = False
|
||||
if not perfect_no_comment:
|
||||
concat_list_F.append(final_img)
|
||||
|
||||
# --- 3. Save Final Outputs ---
|
||||
with open(score_path, "w") as f:
|
||||
json.dump(d_notes, f, indent=4)
|
||||
|
|
@ -289,6 +299,18 @@ def apply_actions_and_regenerate(root_dir, data, student_id, actions, notes_laye
|
|||
|
||||
full_img.save(os.path.join(output_dir, "Concat.jpg"))
|
||||
print(f" Saved regenerated Concat.jpg")
|
||||
if concat_list_F:
|
||||
max_w = max(i.width for i in concat_list_F)
|
||||
total_h = sum(i.height for i in concat_list_F)
|
||||
full_img = Image.new("RGB", (max_w, total_h), "white")
|
||||
|
||||
y = 0
|
||||
for img in concat_list_F:
|
||||
full_img.paste(img, (0, y))
|
||||
y += img.height
|
||||
|
||||
full_img.save(os.path.join(output_dir, "Concat_F.jpg"))
|
||||
print(f" Saved regenerated Concat_F.jpg")
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
|
|
|||
|
|
@ -65,13 +65,15 @@ def apply_actions_and_regenerate_grouped(root_dir, data, student_id, actions, la
|
|||
|
||||
# --- 2. Process Images (Regenerate & Concatenate) ---
|
||||
concat_list = []
|
||||
concat_list_F = []
|
||||
d_notes = dict.fromkeys(all_labels, "")
|
||||
|
||||
# Iterate over all labels naturally to assemble a complete student profile
|
||||
sorted_labels = sorted(labels_data.items(), key=lambda x: natural_key(x[0]))
|
||||
|
||||
for label, content in sorted_labels:
|
||||
d_notes[label] = str(content['result'].get('score', 0))
|
||||
result = content['result']
|
||||
d_notes[label] = str(result.get('score', 0))
|
||||
|
||||
pdf_path = os.path.join(root_dir, f"Copie{student_id}", f"{label}.pdf")
|
||||
if not os.path.exists(pdf_path): continue
|
||||
|
|
@ -102,6 +104,16 @@ def apply_actions_and_regenerate_grouped(root_dir, data, student_id, actions, la
|
|||
|
||||
concat_list.append(final_img)
|
||||
|
||||
perfect_no_comment = True
|
||||
if float(d_notes[label]) != 4.0:
|
||||
perfect_no_comment = False
|
||||
else:
|
||||
if len(result.get('feedback', [])) != 0:
|
||||
perfect_no_comment = False
|
||||
if not perfect_no_comment:
|
||||
concat_list_F.append(final_img)
|
||||
|
||||
|
||||
# --- 3. Save Final Outputs ---
|
||||
with open(score_path, "w") as f:
|
||||
json.dump(d_notes, f, indent=4)
|
||||
|
|
@ -119,6 +131,18 @@ def apply_actions_and_regenerate_grouped(root_dir, data, student_id, actions, la
|
|||
|
||||
full_img.save(os.path.join(output_dir, "Concat.jpg"))
|
||||
print(f" Saved regenerated Concat.jpg")
|
||||
if concat_list_F:
|
||||
max_w = max(i.width for i in concat_list_F)
|
||||
total_h = sum(i.height for i in concat_list_F)
|
||||
full_img = Image.new("RGB", (max_w, total_h), "white")
|
||||
|
||||
y = 0
|
||||
for img in concat_list_F:
|
||||
full_img.paste(img, (0, y))
|
||||
y += img.height
|
||||
|
||||
full_img.save(os.path.join(output_dir, "Concat_F.jpg"))
|
||||
print(f" Saved regenerated Concat_F.jpg")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
Loading…
Reference in New Issue