# cost_calculator.py import os from PyPDF2 import PdfReader from pdf2image import convert_from_path import numpy as np UPLOAD_FOLDER = "data/uploads" ALLOWED_EXTENSIONS = {"pdf"} def allowed_file(filename: str) -> bool: return "." in filename and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS def points_to_meters(points: float) -> float: return (points / 72.0) * 0.0254 def get_rate_black() -> float: return float(os.environ.get("RATE_PER_M2_BLACK", "4.0")) def get_rate_color() -> float: return float(os.environ.get("RATE_PER_M2_COLOR", "5.0")) def analyze_pdf(path, compute_ink=True): reader = PdfReader(path) pages_info = [] total_area_black = total_area_color = 0.0 total_cost_black = total_cost_color = 0.0 for i, page in enumerate(reader.pages): box = page.mediabox width_m = points_to_meters(float(box.width)) height_m = points_to_meters(float(box.height)) area = width_m * height_m ink_pct = None is_color = False if compute_ink: try: images = convert_from_path(path, first_page=i + 1, last_page=i + 1, dpi=150) img = images[0].convert("RGB") arr = np.array(img) # ink pixels: any channel < 240 ink_mask = np.any(arr < 240, axis=2) ink_pct = float(np.count_nonzero(ink_mask)) / (arr.shape[0] * arr.shape[1]) * 100.0 # simple color detection: if RGB channels differ significantly avg_rgb = arr.mean(axis=(0, 1)) if np.ptp(avg_rgb) > 30: is_color = True except Exception as e: print(f"Page {i+1} ink/color calc failed: {e}") ink_pct = None if is_color: rate = get_rate_color() total_area_color += area total_cost_color += area * rate else: rate = get_rate_black() total_area_black += area total_cost_black += area * rate pages_info.append({ "page": i + 1, "width_m": width_m, "height_m": height_m, "area_m2": area, "ink_pct": ink_pct, "is_color": is_color, "cost": round(area * rate, 2) }) return { "filename": os.path.basename(path), "pages": pages_info, "total_area_black": total_area_black, "total_area_color": total_area_color, "total_cost_black": round(total_cost_black, 2), "total_cost_color": round(total_cost_color, 2), "grand_total": round(total_cost_black + total_cost_color, 2) }