def dinero(x): return f”{sym}{x:,.2f}” ss = getSampleStyleSheet() H1 = ParagraphStyle(“H1”, parent=ss[“Title”]fontSize=18, interlineado=22, spaceAfter=2) SMALL= ParagraphStyle(“SM”, parent=ss[“Normal”]fontSize=8.5, textColor=colors.grey, interlineado=11) LBL = ParagraphStyle(“LBL”, parent=ss[“Normal”]fontSize=8.5, textColor=colors.HexColor(“#2b3a67″), spaceAfter=1, fontName=”Helvetica-Bold”) BODY = ParagraphStyle(“BODY”, parent=ss[“Normal”]fontSize=9.5, interlineado=13) DERECHA= ParagraphStyle(“R”, parent=ss[“Normal”]fontSize=16, interlineado=18, alineación=2, textColor=colors.HexColor(“#2b3a67″), fontName=”Helvetica-Bold”) historia = []
cabeza = tabla ([[
[Paragraph(d[“vendor_name”]H1), párrafo (d[“vendor_address”]PEQUEÑO)],
[Paragraph(“INVOICE”, RIGHT),
Paragraph(f”{d[‘invoice_number’]}”, ParagraphStyle(‘n’, padre=PEQUEÑO, alineación=2, tamaño de fuente=9.5))], ]], colWidths=[4.2 * inch, 2.8 * inch]) head.setStyle(TableStyle([(“VALIGN”, (0, 0), (-1, -1), “TOP”)])) historia += [head, Spacer(1, 10)]
meta_rows = [[“Invoice date”, d[“invoice_date”]”Fecha de vencimiento”, d[“due_date”]]]if d.get(“po_number”): meta_rows.append([“PO number”, d[“po_number”]”Moneda”, d[“currency_code”]]) más: meta_rows.append([“Currency”, d[“currency_code”]””, “”]) meta = Tabla(meta_rows, colWidths=[1.3 * inch, 2.2 * inch, 1.3 * inch, 2.2 * inch]) meta.setStyle(TableStyle([
(“FONTSIZE”, (0, 0), (-1, -1), 9),
(“TEXTCOLOR”, (0, 0), (0, -1), colors.HexColor(“#2b3a67”)),
(“TEXTCOLOR”, (2, 0), (2, -1), colors.HexColor(“#2b3a67”)),
(“FONTNAME”, (0, 0), (0, -1), “Helvetica-Bold”),
(“FONTNAME”, (2, 0), (2, -1), “Helvetica-Bold”),
(“BOTTOMPADDING”, (0, 0), (-1, -1), 3), (“TOPPADDING”, (0, 0), (-1, -1), 3)])) historia += [meta, Spacer(1, 12)]
factura = [Paragraph(“BILL TO”, LBL), Paragraph(d[“bill_to_name”]CUERPO), Párrafo (d[“bill_to_address”]PEQUEÑO)]if d.get(“ship_to_name”): barco = [Paragraph(“SHIP TO”, LBL), Paragraph(d[“ship_to_name”]CUERPO), Párrafo (d[“ship_to_address”]PEQUEÑO)]más: barco = [Paragraph(“SHIP TO”, LBL), Paragraph(“Same as billing address”, SMALL)]
partes = Mesa([[bill, ship]], anchos de columna =[3.5 * inch, 3.5 * inch]) fiestas.setStyle(TableStyle([(“VALIGN”, (0, 0), (-1, -1), “TOP”),
(“LEFTPADDING”, (0, 0), (-1, -1), 0)])) historia += [parties, Spacer(1, 14)]
filas = [[“Description”, “Qty”, “Unit price”, “Amount”]]para (desc, q, arriba, t) en c[“items”]: filas.append([desc, str(q), money(up), money
(“BACKGROUND”, (0, 0), (-1, 0), colors.HexColor(“#2b3a67”)),
(“TEXTCOLOR”, (0, 0), (-1, 0), colors.white),
(“FONTSIZE”, (0, 0), (-1, -1), 9.5),
(“ALIGN”, (1, 0), (-1, -1), “RIGHT”),
(“GRID”, (0, 0), (-1, -1), 0.4, colors.HexColor(“#cdd3e6”)),
(“ROWBACKGROUNDS”, (0, 1), (-1, -1), [colors.white, colors.HexColor(“#eef1f8”)]), (“LEFTPADDING”, (0, 0), (-1, -1), 8), (“TOPPADDING”, (0, 0), (-1, -1), 5), (“BOTTOMPADDING”, (0, 0), (-1, -1), 5)])) historia += [items_tbl, Spacer(1, 10)]
tot_rows = [[“Subtotal”, money(c[“subtotal”])]]si c[“discount”]: tot_rows.append([“Discount”, “-” + money(c[“discount”])]) tot_rows.append([f”Tax ({d[‘tax_rate’]*100:.1f}%)”, dinero(c[“tax”])]) tot_rows.append([“TOTAL”, money(c[“total”])]) totales = Tabla(tot_rows, colWidths=[1.6 * inch, 1.4 * inch]hAlign=”DERECHA”) totales.setStyle(TableStyle([
(“FONTSIZE”, (0, 0), (-1, -1), 10),
(“ALIGN”, (0, 0), (-1, -1), “RIGHT”),
(“LINEABOVE”, (0, -1), (-1, -1), 1.0, colors.HexColor(“#2b3a67”)),
(“FONTNAME”, (0, -1), (-1, -1), “Helvetica-Bold”),
(“TEXTCOLOR”, (0, -1), (-1, -1), colors.HexColor(“#2b3a67”)),
(“TOPPADDING”, (0, 0), (-1, -1), 3), (“BOTTOMPADDING”, (0, 0), (-1, -1), 3)])) historia += [totals, Spacer(1, 8)]
filas_pago = [[“Amount paid”, money(c[“amount_paid”])],
[“Balance due”, money(c[“balance”])]]pago = Tabla(pago_filas, colWidths=[1.6 * inch, 1.4 * inch]hAlign=”RIGHT”) due_color = colores.HexColor(“#1b7a3d”) si c[“is_paid”] demás colores.HexColor(“#7a2e2e”) pay.setStyle(TableStyle([
(“FONTSIZE”, (0, 0), (-1, -1), 10),
(“ALIGN”, (0, 0), (-1, -1), “RIGHT”),
(“FONTNAME”, (0, 1), (-1, 1), “Helvetica-Bold”),
(“TEXTCOLOR”, (0, 1), (-1, 1), due_color),
(“TOPPADDING”, (0, 0), (-1, -1), 2), (“BOTTOMPADDING”, (0, 0), (-1, -1), 2)])) estado = “PAGADO TOTALMENTE” si c[“is_paid”] otra historia de “SALDO VENCIDO” += [pay, Spacer(1, 6),
Paragraph(f”Status: {status}”, BODY), Spacer(1, 16),
Paragraph(“Notes”, LBL), Paragraph(d[“notes”]CUERPO)]SimpleDocTemplate(ruta, tamaño de página=LETRA, margen superior=0,7 * pulgada, margen inferior=0,7 * pulgada, margen izquierdo=0,8 * pulgada, margen derecho=0,8 * pulgada).build(story) print(“PASO 3/7 · Generación de archivos PDF de facturas sintéticas…”) CORPUS = []
para i, d en enumerar(DOCS): ruta = f”/content/invoice_{i}.pdf” if os.path.isdir(“/content”) else f”invoice_{i}.pdf” render_pdf(d, ruta) CORPUS.append((d, ground_truth(d), ruta)) print(f” ✓ {os.path.basename(ruta)} — {d[‘vendor_name’]} → {d[‘bill_to_name’]}”) print() si SHOW_FIRST_PAGE: intente: importar pypdfium2 como pdfium, matplotlib.pyplot como plt pg = pdfium.PdfDocument(CORPUS[0][2])[0]
img = pg.render(scale=2.0).to_pil() plt.figure(figsize=(6.4, 8.3)); plt.imshow(img); plt.axis(“off”) plt.title(“Lo que dice el ascensor: página 1 de factura_0.pdf”, fontsize=10); plt.show() excepto Excepción como e: print(“vista previa de página omitida:”, e, “\n”)