feat: Agente-Marketing initial commit

This commit is contained in:
2026-07-03 12:23:34 -04:00
commit 293522436a
52 changed files with 13522 additions and 0 deletions
+757
View File
@@ -0,0 +1,757 @@
"""
Prisa Yachts LLC — Bilingual PDF Brochure Generator
Generates 3 professional PDFs using ReportLab.
"""
import os
from reportlab.lib.pagesizes import letter, landscape
from reportlab.lib.units import inch
from reportlab.lib.colors import Color, HexColor, white, black
from reportlab.pdfgen import canvas
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus import Paragraph
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT, TA_JUSTIFY
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from PIL import Image as PILImage
import textwrap
# ── Brand constants ────────────────────────────────────────────────────────────
NAVY = HexColor('#1B3A6B')
GOLD = HexColor('#C9A84C')
WHITE = HexColor('#FFFFFF')
LIGHT_NAVY = HexColor('#2A5298')
LIGHT_GRAY = HexColor('#F5F5F5')
DARK_GRAY = HexColor('#333333')
LOGO_PATH = r"D:\Prisa Yachts LLC\WhatsApp Image 2026-03-11 at 22.24.00.jpeg"
OUTPUT_DIR = r"D:\Proyectos Software\Agente Marketing\prisa-yachts\brochures"
TAGLINE = "Safe Command ◇ Luxury Maintenance and Care"
WEBSITE = "prisayachts.com"
AREA = "Stuart to Jacksonville, FL"
# ── Helper: draw logo or text placeholder ─────────────────────────────────────
def draw_logo(c, x, y, width, height, centered=True):
"""Draw the logo image; fall back to a styled text block if unavailable."""
if os.path.exists(LOGO_PATH):
try:
if centered:
c.drawImage(LOGO_PATH, x - width / 2, y - height,
width=width, height=height,
preserveAspectRatio=True, mask='auto')
else:
c.drawImage(LOGO_PATH, x, y - height,
width=width, height=height,
preserveAspectRatio=True, mask='auto')
return
except Exception:
pass
# Fallback placeholder
bx = x - width / 2 if centered else x
by = y - height
c.setFillColor(NAVY)
c.rect(bx, by, width, height, fill=1, stroke=0)
c.setFillColor(GOLD)
c.setFont("Helvetica-Bold", 10)
c.drawCentredString(bx + width / 2, by + height / 2 - 4, "PRISA YACHTS LLC")
c.setFont("Helvetica", 7)
c.setFillColor(WHITE)
c.drawCentredString(bx + width / 2, by + height / 2 - 16, "prisayachts.com")
def draw_gold_line(c, x1, y, x2, thickness=1.0):
c.setStrokeColor(GOLD)
c.setLineWidth(thickness)
c.line(x1, y, x2, y)
def draw_gold_divider(c, x, y, width, thickness=1.0):
draw_gold_line(c, x, y, x + width, thickness)
# ── Text wrapping helper ───────────────────────────────────────────────────────
def draw_wrapped_text(c, text, x, y, max_width, font_name, font_size,
line_height, color=None, align='left'):
"""Draw text wrapping within max_width, returns final y position."""
if color:
c.setFillColor(color)
c.setFont(font_name, font_size)
words = text.split()
lines = []
current = []
for word in words:
test_line = ' '.join(current + [word])
if c.stringWidth(test_line, font_name, font_size) <= max_width:
current.append(word)
else:
if current:
lines.append(' '.join(current))
current = [word]
if current:
lines.append(' '.join(current))
for line in lines:
if align == 'center':
c.drawCentredString(x + max_width / 2, y, line)
elif align == 'right':
c.drawRightString(x + max_width, y, line)
else:
c.drawString(x, y, line)
y -= line_height
return y
def draw_section_header(c, text, x, y, width, bg_color=NAVY, text_color=GOLD,
font_size=11, padding=5):
"""Draw a colored section header bar."""
bar_h = font_size + padding * 2
c.setFillColor(bg_color)
c.rect(x, y - bar_h, width, bar_h, fill=1, stroke=0)
c.setFillColor(text_color)
c.setFont("Helvetica-Bold", font_size)
c.drawString(x + padding + 2, y - bar_h + padding, text)
return y - bar_h
def draw_bullet_item(c, text, x, y, max_width, font_name="Helvetica",
font_size=8.5, line_height=12, bullet_color=GOLD,
text_color=DARK_GRAY):
"""Draw a gold diamond bullet followed by wrapped text."""
bullet = ""
indent = 14
c.setFont("Helvetica-Bold", font_size)
c.setFillColor(bullet_color)
c.drawString(x, y, "")
y = draw_wrapped_text(c, text, x + indent, y,
max_width - indent, font_name, font_size,
line_height, color=text_color)
return y - 2
# ══════════════════════════════════════════════════════════════════════════════
# BROCHURE 1 — General Services (landscape letter, 3 columns)
# ══════════════════════════════════════════════════════════════════════════════
def generate_brochure1():
path = os.path.join(OUTPUT_DIR, "PrisaYachts_General_Services.pdf")
pw, ph = landscape(letter) # 792 x 612 pts
c = canvas.Canvas(path, pagesize=landscape(letter))
c.setTitle("Prisa Yachts LLC — Professional Marine Services")
c.setAuthor("Prisa Yachts LLC")
c.setSubject("General Marine Services Brochure EN/ES")
margin = 0.35 * inch
col_gap = 0.18 * inch
usable_w = pw - 2 * margin
left_col_w = usable_w * 0.24
center_col_w = usable_w * 0.38
right_col_w = usable_w - left_col_w - center_col_w - 2 * col_gap
left_x = margin
center_x = left_x + left_col_w + col_gap
right_x = center_x + center_col_w + col_gap
top_y = ph - margin
bottom_y = margin
# ── Full background white ──
c.setFillColor(WHITE)
c.rect(0, 0, pw, ph, fill=1, stroke=0)
# ── LEFT COLUMN — navy background ────────────────────────────────────────
col_h = ph - 2 * margin
c.setFillColor(NAVY)
c.rect(left_x, bottom_y, left_col_w, col_h, fill=1, stroke=0)
# Logo
logo_y = top_y - 0.15 * inch
logo_w = left_col_w - 0.3 * inch
logo_h = 1.0 * inch
draw_logo(c, left_x + left_col_w / 2, logo_y, logo_w, logo_h, centered=True)
cur_y = logo_y - logo_h - 0.1 * inch
# Company name
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 12)
c.drawCentredString(left_x + left_col_w / 2, cur_y, "Prisa Yachts LLC")
cur_y -= 0.22 * inch
# Tagline (gold italic, wrapped)
c.setFillColor(GOLD)
c.setFont("Helvetica-Oblique", 7.5)
tagline_parts = ["Safe Command ◇", "Luxury Maintenance and Care"]
for part in tagline_parts:
c.drawCentredString(left_x + left_col_w / 2, cur_y, part)
cur_y -= 0.16 * inch
cur_y -= 0.05 * inch
# Service area
c.setFillColor(WHITE)
c.setFont("Helvetica", 7.5)
c.drawCentredString(left_x + left_col_w / 2, cur_y, AREA)
cur_y -= 0.18 * inch
# Gold divider
pad = 0.15 * inch
draw_gold_divider(c, left_x + pad, cur_y, left_col_w - 2 * pad, 1.5)
cur_y -= 0.2 * inch
# Contact blocks
contacts = [
("Alberto", "(954) 655-4084", "technical@prisayachts.com"),
("Federico", "(754) 209-3375", "management@prisayachts.com"),
]
for name, phone, email in contacts:
c.setFillColor(GOLD)
c.setFont("Helvetica-Bold", 8.5)
c.drawCentredString(left_x + left_col_w / 2, cur_y, name)
cur_y -= 0.15 * inch
c.setFillColor(WHITE)
c.setFont("Helvetica", 7.5)
c.drawCentredString(left_x + left_col_w / 2, cur_y, phone)
cur_y -= 0.14 * inch
c.setFont("Helvetica", 6.8)
c.drawCentredString(left_x + left_col_w / 2, cur_y, email)
cur_y -= 0.2 * inch
# Gold divider
draw_gold_divider(c, left_x + pad, cur_y, left_col_w - 2 * pad, 1.0)
cur_y -= 0.18 * inch
# Website
c.setFillColor(GOLD)
c.setFont("Helvetica-Bold", 8)
c.drawCentredString(left_x + left_col_w / 2, cur_y, WEBSITE)
# ── CENTER COLUMN — English services ─────────────────────────────────────
# Column background (very light)
c.setFillColor(WHITE)
c.rect(center_x, bottom_y, center_col_w, col_h, fill=1, stroke=0)
# Thin navy left border
c.setFillColor(NAVY)
c.rect(center_x, bottom_y, 3, col_h, fill=1, stroke=0)
cur_y = top_y - 0.15 * inch
# Header
c.setFillColor(NAVY)
c.setFont("Helvetica-Bold", 14)
c.drawString(center_x + 0.15 * inch, cur_y, "Professional Marine Services")
cur_y -= 0.12 * inch
draw_gold_divider(c, center_x + 0.15 * inch, cur_y,
center_col_w - 0.2 * inch, 2.0)
cur_y -= 0.22 * inch
en_services = [
"Marine Electrical (ABYC E-11 Certified)",
"Lithium Battery Bank Design & Installation",
"Shore Power & Inverter/Charger Systems",
"Solar & Alternator Upgrades",
"Full Rewiring (Tinned Wire, Labeled Circuits)",
"Teak Deck Recovery & Restoration",
"Gelcoat Polishing & Oxidation Removal",
"Washdown System Installation",
"Anchor Windlass & Bilge Systems",
"NMEA 2000 / 0183 Integration",
"Pre-Season Inspection & Annual Service",
]
bx = center_x + 0.15 * inch
bw = center_col_w - 0.25 * inch
for svc in en_services:
cur_y = draw_bullet_item(c, svc, bx, cur_y, bw,
font_size=8.5, line_height=11.5)
cur_y -= 1
# Footer band
footer_h = 0.55 * inch
footer_y = bottom_y
c.setFillColor(LIGHT_GRAY)
c.rect(center_x, footer_y, center_col_w, footer_h, fill=1, stroke=0)
draw_gold_divider(c, center_x, footer_y + footer_h,
center_col_w, 1.5)
c.setFillColor(NAVY)
c.setFont("Helvetica-Oblique", 7)
footer_text = ("All electrical work performed to ABYC E-11 standards. "
"Bilingual service EN/ES.")
c.drawString(center_x + 0.1 * inch, footer_y + 0.28 * inch, footer_text)
# ── RIGHT COLUMN — Spanish services ──────────────────────────────────────
c.setFillColor(WHITE)
c.rect(right_x, bottom_y, right_col_w, col_h, fill=1, stroke=0)
# Thin gold left border
c.setFillColor(GOLD)
c.rect(right_x, bottom_y, 3, col_h, fill=1, stroke=0)
cur_y = top_y - 0.15 * inch
c.setFillColor(NAVY)
c.setFont("Helvetica-Bold", 14)
c.drawString(right_x + 0.15 * inch, cur_y, "Servicios Marinos Profesionales")
cur_y -= 0.12 * inch
draw_gold_divider(c, right_x + 0.15 * inch, cur_y,
right_col_w - 0.2 * inch, 2.0)
cur_y -= 0.22 * inch
es_services = [
"Instalaciones Eléctricas Marinas (Estándar ABYC E-11)",
"Diseño e Instalación de Banco de Baterías de Litio",
"Sistemas de Alimentación en Puerto e Inversor/Cargador",
"Mejoras Solar y de Alternador",
"Recableado Completo (Cable Estañado, Circuitos Etiquetados)",
"Recuperación y Restauración de Teca",
"Pulido de Gelcoat y Eliminación de Oxidación",
"Instalación de Sistema Washdown",
"Sistemas de Molinete y Achique",
"Integración NMEA 2000 / 0183",
"Inspección Pre-Temporada y Servicio Anual",
]
bx2 = right_x + 0.15 * inch
bw2 = right_col_w - 0.25 * inch
for svc in es_services:
cur_y = draw_bullet_item(c, svc, bx2, cur_y, bw2,
font_size=8.5, line_height=11.5)
cur_y -= 1
# Footer band
c.setFillColor(LIGHT_GRAY)
c.rect(right_x, footer_y, right_col_w, footer_h, fill=1, stroke=0)
draw_gold_divider(c, right_x, footer_y + footer_h, right_col_w, 1.5)
c.setFillColor(NAVY)
c.setFont("Helvetica-Oblique", 7)
es_footer = ("Todo trabajo eléctrico realizado según estándar ABYC E-11. "
"Servicio bilingüe EN/ES.")
c.drawString(right_x + 0.1 * inch, footer_y + 0.28 * inch, es_footer)
c.save()
print(f" [OK] {path}")
return path
# ══════════════════════════════════════════════════════════════════════════════
# BROCHURE 2 — Marine Electrical (portrait letter, 2 columns)
# ══════════════════════════════════════════════════════════════════════════════
def generate_brochure2():
path = os.path.join(OUTPUT_DIR, "PrisaYachts_Electrical.pdf")
pw, ph = letter # 612 x 792 pts
c = canvas.Canvas(path, pagesize=letter)
c.setTitle("Prisa Yachts LLC — Marine Electrical Services")
c.setAuthor("Prisa Yachts LLC")
c.setSubject("Marine Electrical Brochure EN/ES")
margin = 0.4 * inch
col_gap = 0.2 * inch
usable_w = pw - 2 * margin
col_w = (usable_w - col_gap) / 2
# ── Header band ───────────────────────────────────────────────────────────
header_h = 1.05 * inch
header_y = ph - header_h
c.setFillColor(NAVY)
c.rect(0, header_y, pw, header_h, fill=1, stroke=0)
# Gold bottom border on header
c.setStrokeColor(GOLD)
c.setLineWidth(2.5)
c.line(0, header_y, pw, header_y)
# Logo in header (left side)
draw_logo(c, margin, ph - 0.08 * inch, 1.5 * inch, 0.85 * inch, centered=False)
# Title center
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 18)
c.drawCentredString(pw / 2, ph - 0.42 * inch, "Marine Electrical Services")
# ABYC badge right
c.setFillColor(GOLD)
c.setFont("Helvetica-Bold", 9.5)
c.drawRightString(pw - margin, ph - 0.38 * inch, "ABYC E-11 Certified")
c.setFillColor(WHITE)
c.setFont("Helvetica", 8)
c.drawRightString(pw - margin, ph - 0.55 * inch, "Stuart to Jacksonville, FL")
# ── Body columns ─────────────────────────────────────────────────────────
body_top = header_y - 0.22 * inch
footer_h = 0.55 * inch
body_bottom = margin + footer_h + 0.1 * inch
left_x = margin
right_x = margin + col_w + col_gap
# Vertical gold divider between columns
mid_x = margin + col_w + col_gap / 2
c.setStrokeColor(GOLD)
c.setLineWidth(1.0)
c.line(mid_x, body_bottom, mid_x, body_top)
def draw_electrical_column(start_x, cur_y, lang='en'):
cw = col_w - 0.05 * inch
line_sm = 10.5
line_md = 12
if lang == 'en':
c.setFillColor(NAVY)
c.setFont("Helvetica-Bold", 10.5)
c.drawString(start_x, cur_y, "Expert Marine Electrical — Stuart to Jacksonville")
cur_y -= 0.18 * inch
draw_gold_divider(c, start_x, cur_y, cw, 1.5)
cur_y -= 0.18 * inch
sections = [
("WHY ABYC CERTIFIED WORK MATTERS",
"Marine electrical failures are the #1 cause of boat fires. Every "
"installation we do meets or exceeds ABYC E-11 standards: correct wire "
"gauge, tinned marine-grade wire, properly sized overcurrent protection, "
"and labeled circuits from stem to stern."),
("FULL REWIRING",
"Complete vessel rewiring using marine-grade tinned wire. Correct gauge "
"per ABYC E-11. Every circuit labeled and documented."),
("LITHIUM BATTERY BANKS",
"Custom LiFePO4 battery bank design and installation. BMS integration, "
"proper charge profiles, and full system documentation."),
("SOLAR & SHORE POWER",
"Solar panel installation with MPPT charge controllers. Shore power "
"systems, isolation transformers, and inverter/charger integration."),
("SYSTEMS & ELECTRONICS",
"NMEA 2000 backbone design and installation. Bilge pumps, washdown "
"systems, anchor windlass, lighting upgrades."),
("DIAGNOSTICS & INSPECTION",
"Pre-purchase electrical inspection. Annual safety audit. "
"Load testing and fault finding."),
]
icons = ["", "", "\U0001f50b", "☀️", "\U0001f50c", "\U0001f50d"]
else:
c.setFillColor(NAVY)
c.setFont("Helvetica-Bold", 10.5)
c.drawString(start_x, cur_y,
"Instalaciones Eléctricas Marinas — Stuart a Jacksonville")
cur_y -= 0.18 * inch
draw_gold_divider(c, start_x, cur_y, cw, 1.5)
cur_y -= 0.18 * inch
sections = [
("POR QUÉ IMPORTA EL ESTÁNDAR ABYC",
"Los fallos eléctricos son la causa número 1 de incendios en embarcaciones. "
"Cada instalación que realizamos cumple o supera el estándar ABYC E-11: "
"calibre de cable correcto, cable marino estañado, protección contra "
"sobrecorriente correctamente dimensionada, y circuitos etiquetados de proa a popa."),
("RECABLEADO COMPLETO",
"Recableado total de la embarcación con cable estañado de grado marino. "
"Calibre correcto según ABYC E-11. Cada circuito etiquetado y documentado."),
("BANCO DE BATERÍAS DE LITIO",
"Diseño e instalación personalizada de banco LiFePO4. Integración de BMS, "
"perfiles de carga correctos y documentación completa del sistema."),
("SOLAR Y ALIMENTACIÓN EN PUERTO",
"Instalación de paneles solares con controladores MPPT. Sistemas de shore power, "
"transformadores de aislamiento e integración inversor/cargador."),
("SISTEMAS Y ELECTRÓNICA",
"Diseño e instalación de backbone NMEA 2000. Bombas de achique, sistemas "
"washdown, molinete de ancla, mejoras de iluminación."),
("DIAGNÓSTICO E INSPECCIÓN",
"Inspección eléctrica previa a compra. Auditoría de seguridad anual. "
"Pruebas de carga y localización de fallos."),
]
icons = ["", "", "\U0001f50b", "☀️", "\U0001f50c", "\U0001f50d"]
for i, (title, body) in enumerate(sections):
if i == 0:
# First section: intro paragraph, no icon
c.setFillColor(NAVY)
c.setFont("Helvetica-Bold", 8.5)
c.drawString(start_x, cur_y, title)
cur_y -= 0.14 * inch
cur_y = draw_wrapped_text(c, body, start_x, cur_y, cw,
"Helvetica", 8, line_sm, color=DARK_GRAY)
cur_y -= 0.12 * inch
else:
# Service sections with colored mini-header
bar_h = 14
c.setFillColor(NAVY)
c.rect(start_x, cur_y - bar_h + 3, cw, bar_h, fill=1, stroke=0)
c.setFillColor(GOLD)
c.setFont("Helvetica-Bold", 8.5)
c.drawString(start_x + 4, cur_y - bar_h + 6, title)
cur_y -= bar_h + 2
cur_y = draw_wrapped_text(c, body, start_x + 4, cur_y, cw - 4,
"Helvetica", 7.8, line_sm, color=DARK_GRAY)
cur_y -= 0.1 * inch
# Quote
cur_y -= 0.05 * inch
draw_gold_divider(c, start_x, cur_y, cw, 1.0)
cur_y -= 0.16 * inch
if lang == 'en':
quote = '"Every wire we touch, we own. No shortcuts."'
else:
quote = '"Cada cable que tocamos, lo respaldamos. Sin atajos."'
c.setFillColor(NAVY)
c.setFont("Helvetica-BoldOblique", 8)
cur_y = draw_wrapped_text(c, quote, start_x, cur_y, cw,
"Helvetica-BoldOblique", 8, 11, color=NAVY)
return cur_y
draw_electrical_column(left_x, body_top, 'en')
draw_electrical_column(right_x, body_top, 'es')
# ── Footer ────────────────────────────────────────────────────────────────
c.setFillColor(NAVY)
c.rect(0, 0, pw, margin + footer_h, fill=1, stroke=0)
c.setStrokeColor(GOLD)
c.setLineWidth(1.5)
c.line(0, margin + footer_h, pw, margin + footer_h)
c.setFillColor(WHITE)
c.setFont("Helvetica", 8)
c.drawString(margin, margin + footer_h - 0.18 * inch,
"Alberto | (954) 655-4084 | technical@prisayachts.com")
c.setFillColor(GOLD)
c.setFont("Helvetica-Bold", 8.5)
c.drawRightString(pw - margin, margin + footer_h - 0.18 * inch, WEBSITE)
c.setFillColor(WHITE)
c.setFont("Helvetica", 7)
c.drawCentredString(pw / 2, margin + 0.15 * inch, AREA)
c.save()
print(f" [OK] {path}")
return path
# ══════════════════════════════════════════════════════════════════════════════
# BROCHURE 3 — Teak & Detailing (portrait letter, 2 columns)
# ══════════════════════════════════════════════════════════════════════════════
def generate_brochure3():
path = os.path.join(OUTPUT_DIR, "PrisaYachts_Teak_Detailing.pdf")
pw, ph = letter
c = canvas.Canvas(path, pagesize=letter)
c.setTitle("Prisa Yachts LLC — Teak Restoration & Marine Detailing")
c.setAuthor("Prisa Yachts LLC")
c.setSubject("Teak & Detailing Brochure EN/ES")
margin = 0.4 * inch
col_gap = 0.2 * inch
usable_w = pw - 2 * margin
col_w = (usable_w - col_gap) / 2
# ── Header band ───────────────────────────────────────────────────────────
header_h = 1.05 * inch
header_y = ph - header_h
c.setFillColor(NAVY)
c.rect(0, header_y, pw, header_h, fill=1, stroke=0)
c.setStrokeColor(GOLD)
c.setLineWidth(2.5)
c.line(0, header_y, pw, header_y)
draw_logo(c, margin, ph - 0.08 * inch, 1.5 * inch, 0.85 * inch, centered=False)
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 16)
c.drawCentredString(pw / 2, ph - 0.4 * inch, "Teak Restoration & Marine Detailing")
c.setFillColor(GOLD)
c.setFont("Helvetica-Bold", 9)
c.drawRightString(pw - margin, ph - 0.38 * inch, "Stuart to Jacksonville")
c.setFillColor(WHITE)
c.setFont("Helvetica", 8)
c.drawRightString(pw - margin, ph - 0.55 * inch, "Florida's Saltwater Environment")
# ── Body ─────────────────────────────────────────────────────────────────
body_top = header_y - 0.22 * inch
footer_h = 0.55 * inch
body_bottom = margin + footer_h + 0.1 * inch
left_x = margin
right_x = margin + col_w + col_gap
# Vertical gold divider
mid_x = margin + col_w + col_gap / 2
c.setStrokeColor(GOLD)
c.setLineWidth(1.0)
c.line(mid_x, body_bottom, mid_x, body_top)
def draw_teak_column(start_x, cur_y, lang='en'):
cw = col_w - 0.05 * inch
line_sm = 10.5
if lang == 'en':
subtitle = "Teak & Detailing Specialists — Floridas Saltwater Environment"
h2 = "IS YOUR TEAK WORTH SAVING?"
p1 = ("Gray teak isnt always dead teak. Before we quote any job, we do an honest "
"hands-on assessment — and well tell you straight if recovery makes "
"financial sense or if replacement is the smarter move. Most teak we see in "
"Florida is recoverable. It just needs the right process.")
steps_title = "THE RECOVERY PROCESS"
steps = [
("Step 1 — CLEAN",
"Two-part oxalic acid cleaner. Part A removes oxidation and gray surface "
"cells. Part B neutralizes pH. Clean canvas only — never cover problems."),
("Step 2 — SAND",
"80-grit to open the grain. 120-grit to smooth. Always with the grain. "
"Never cross-grain — water traps are invisible until they rot your plank from inside."),
("Step 3 — OIL",
"Penetrating marine teak oil, thin coats, wiped before skinning. "
"Feeds the wood from inside. Conditions, doesnt coat."),
("Step 4 — SEAL",
"UV-blocking teak sealer on all horizontal surfaces. Florida sun reoxidizes "
"bare teak in one season. Proper sealing buys 1218 months."),
]
det_title = "DETAILING SERVICES"
det_items = [
"Gelcoat oxidation removal (light & heavy cut)",
"Machine polish & wax — DA and rotary",
"Hull cleaning & brightwork",
"Caulking inspection & reseam",
"Full exterior detail packages",
]
quote = '"Weve restored teak that owners wanted to scrap. Send us a photo before you decide."'
else:
subtitle = "Especialistas en Teca y Detailing — Ambiente Marino de Florida"
h2 = "¿VALE LA PENA SALVAR TU TECA?"
p1 = ("La teca gris no siempre está muerta. Antes de cotizar cualquier trabajo, "
"hacemos una evaluación honesta — y te diremos directamente si la "
"recuperación tiene sentido económico o si el reemplazo es la decisión "
"más inteligente. La mayoría de la teca que vemos en Florida es recuperable. "
"Solo necesita el proceso correcto.")
steps_title = "EL PROCESO DE RECUPERACIÓN"
steps = [
("Paso 1 — LIMPIAR",
"Limpiador de ácido oxálico en dos partes. La parte A elimina la oxidación "
"y las células grises. La parte B neutraliza el pH. Solo superficie limpia — "
"nunca cubrir problemas."),
("Paso 2 — LIJAR",
"Lija 80 para abrir el grano. Lija 120 para alisar. Siempre a favor de la veta. "
"Nunca transversal — las trampas de agua son invisibles hasta que pudren la tabla desde adentro."),
("Paso 3 — ACEITAR",
"Aceite marino penetrante de teca, capas finas, limpiando antes de que forme película. "
"Nutre la madera desde adentro. Acondiciona, no recubre."),
("Paso 4 — SELLAR",
"Sellador UV en todas las superficies horizontales. El sol de Florida reoxida la teca "
"sin protección en una temporada. El sellado correcto da 12 a 18 meses."),
]
det_title = "SERVICIOS DE DETAILING"
det_items = [
"Eliminación de oxidación de gelcoat (corte suave y profundo)",
"Pulido a máquina y encerado — DA y rotativa",
"Limpieza de casco y herrajes brillantes",
"Inspección de sellado y recalafateo",
"Paquetes completos de detailing exterior",
]
quote = '"Hemos restaurado teca que los dueños querían desechar. Mándanos una foto antes de decidir."'
# Subtitle
c.setFillColor(NAVY)
c.setFont("Helvetica-Bold", 9.5)
cur_y = draw_wrapped_text(c, subtitle, start_x, cur_y, cw,
"Helvetica-Bold", 9.5, 12, color=NAVY)
cur_y -= 0.1 * inch
draw_gold_divider(c, start_x, cur_y, cw, 1.5)
cur_y -= 0.16 * inch
# H2
c.setFillColor(NAVY)
c.setFont("Helvetica-Bold", 8.5)
c.drawString(start_x, cur_y, h2)
cur_y -= 0.14 * inch
# Intro paragraph
cur_y = draw_wrapped_text(c, p1, start_x, cur_y, cw,
"Helvetica", 7.8, line_sm, color=DARK_GRAY)
cur_y -= 0.15 * inch
# Recovery process header
bar_h = 14
c.setFillColor(NAVY)
c.rect(start_x, cur_y - bar_h + 3, cw, bar_h, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 8.5)
c.drawString(start_x + 4, cur_y - bar_h + 6, steps_title)
cur_y -= bar_h + 6
# Steps
for step_title, step_body in steps:
c.setFillColor(GOLD)
c.setFont("Helvetica-Bold", 8)
c.drawString(start_x + 4, cur_y, step_title)
cur_y -= 0.13 * inch
cur_y = draw_wrapped_text(c, step_body, start_x + 10, cur_y, cw - 10,
"Helvetica", 7.5, 10.5, color=DARK_GRAY)
cur_y -= 0.08 * inch
cur_y -= 0.05 * inch
# Detailing services header
c.setFillColor(NAVY)
c.rect(start_x, cur_y - bar_h + 3, cw, bar_h, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 8.5)
c.drawString(start_x + 4, cur_y - bar_h + 6, det_title)
cur_y -= bar_h + 6
for item in det_items:
cur_y = draw_bullet_item(c, item, start_x + 4, cur_y,
cw - 4, font_size=7.8, line_height=10.5)
cur_y -= 1
# Quote
cur_y -= 0.08 * inch
draw_gold_divider(c, start_x, cur_y, cw, 1.0)
cur_y -= 0.14 * inch
cur_y = draw_wrapped_text(c, quote, start_x, cur_y, cw,
"Helvetica-BoldOblique", 7.8, 11, color=NAVY)
return cur_y
draw_teak_column(left_x, body_top, 'en')
draw_teak_column(right_x, body_top, 'es')
# ── Footer ────────────────────────────────────────────────────────────────
c.setFillColor(NAVY)
c.rect(0, 0, pw, margin + footer_h, fill=1, stroke=0)
c.setStrokeColor(GOLD)
c.setLineWidth(1.5)
c.line(0, margin + footer_h, pw, margin + footer_h)
c.setFillColor(WHITE)
c.setFont("Helvetica", 8)
c.drawString(margin, margin + footer_h - 0.18 * inch,
"Federico | (754) 209-3375 | management@prisayachts.com")
c.setFillColor(GOLD)
c.setFont("Helvetica-Bold", 8.5)
c.drawRightString(pw - margin, margin + footer_h - 0.18 * inch, WEBSITE)
c.setFillColor(WHITE)
c.setFont("Helvetica", 7)
c.drawCentredString(pw / 2, margin + 0.15 * inch, AREA)
c.save()
print(f" [OK] {path}")
return path
# ── Main ───────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
import sys
print("Prisa Yachts LLC — Brochure Generator")
print(f"Output: {OUTPUT_DIR}\n")
files = []
try:
files.append(generate_brochure1())
except Exception as e:
print(f" [ERROR] Brochure 1: {e}", file=sys.stderr)
try:
files.append(generate_brochure2())
except Exception as e:
print(f" [ERROR] Brochure 2: {e}", file=sys.stderr)
try:
files.append(generate_brochure3())
except Exception as e:
print(f" [ERROR] Brochure 3: {e}", file=sys.stderr)
print("\nFile sizes:")
for f in files:
size = os.path.getsize(f)
print(f" {os.path.basename(f)}: {size:,} bytes ({size/1024:.1f} KB)")