with one click
gen-pdf
// Converts a Markdown file to a styled PDF with DevExpert branding (logo in bottom-right corner). Use when asked to generate a PDF from a Markdown document, or when any DevExpert proposal/document needs to be exported as PDF.
// Converts a Markdown file to a styled PDF with DevExpert branding (logo in bottom-right corner). Use when asked to generate a PDF from a Markdown document, or when any DevExpert proposal/document needs to be exported as PDF.
Process invoice PDFs/emails: extract vendor + date, generate normalized filename, optionally archive to Drive, and email the PDF to the right Holded inbox.
Use the private LearnWorlds CLI to inspect DevExpert Academy users, find students by email, list their enrolled courses/products, look up products, and perform safe enrollment workflows. Trigger when Antonio asks what courses a student has in LearnWorlds or academia.devexpert.io, whether someone belongs to the current or next AI Expert edition, or to use the LearnWorlds/academy CLI.
Genera la newsletter semanal de DevExpert recopilando contenido de X, YouTube, PostFlow y bookmarks. Crea borrador en Listmonk para revisión.
End-to-end YouTube publishing workflow using ordered scripts: prepare/concat video, upload draft, transcribe with Parakeet, generate copy with the calling model, optionally prepare English dubbing assets, render thumbnails, update YouTube metadata, then schedule socials (PostFlow) 15 minutes after publish.
Enforce Antonio's real Spanish social voice when drafting or rewriting X, LinkedIn, article teaser, and newsletter intro copy. Use whenever copy must sound like Antonio and avoid obvious AI writing.
Manage inbox email. Uses the inbox script and stores metadata (ids) to open or archive messages later.
| name | gen-pdf |
| description | Converts a Markdown file to a styled PDF with DevExpert branding (logo in bottom-right corner). Use when asked to generate a PDF from a Markdown document, or when any DevExpert proposal/document needs to be exported as PDF. |
Convierte cualquier fichero Markdown a PDF con un acabado visual cercano al export de Obsidian (tipografĆa y jerarquĆa neutras) y mantiene el logo de DevExpert en la esquina inferior derecha.
.mdpip install markdown-it-py[plugins] weasyprint reportlab PyPDF2
assets/devexpert-logo.png (logo horizontal completo, relativo a la carpeta de la skill)~/Documents/aipal/40-archive/Usa el siguiente script Python. Acepta input_md y output_pdf como variables:
import os
import tempfile
from pathlib import Path
from markdown_it import MarkdownIt
from PyPDF2 import PdfReader, PdfWriter
from reportlab.lib.pagesizes import A4
from reportlab.pdfgen import canvas
from weasyprint import CSS, HTML
md = MarkdownIt("commonmark", {"breaks": True, "html": True}).enable("table")
CSS_STYLES = """
@page {
size: A4;
margin: 30mm 24mm 26mm 24mm;
}
html, body {
margin: 0;
padding: 0;
color: #111111;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Helvetica, Arial, sans-serif;
font-size: 12pt;
line-height: 1.46;
}
p {
margin: 0 0 0.82em 0;
}
h1, h2, h3, h4 {
color: #111111;
font-weight: 800;
line-height: 1.12;
margin: 0;
}
h1 {
font-size: 24pt;
margin-bottom: 0.9em;
}
h2 {
font-size: 20pt;
margin-top: 1.1em;
margin-bottom: 0.74em;
}
h3 {
font-size: 16pt;
margin-top: 0.95em;
margin-bottom: 0.58em;
}
h4 {
font-size: 13pt;
margin-top: 0.8em;
margin-bottom: 0.4em;
}
strong {
color: #111111;
font-weight: 800;
}
ul, ol {
margin: 0.35em 0 0.9em 1.28em;
padding: 0;
}
li {
margin-bottom: 0.24em;
}
li::marker {
color: #a0a0a0;
}
table {
width: 100%;
border-collapse: collapse;
margin: 0.9em 0 1.1em 0;
font-size: 10.5pt;
}
th, td {
border: 1px solid #d4d4d4;
padding: 7px 10px;
text-align: left;
vertical-align: top;
}
th {
background: #f3f3f3;
color: #111111;
font-weight: 700;
}
tr:nth-child(even) td {
background: #fafafa;
}
blockquote {
border-left: 3px solid #d0d0d0;
padding-left: 12px;
color: #303030;
margin: 0.8em 0;
}
code {
font-family: "SF Mono", "SFMono-Regular", Menlo, Consolas, "Liberation Mono", monospace;
font-size: 0.9em;
background: #f5f5f5;
padding: 0.08em 0.28em;
border-radius: 4px;
}
pre code {
display: block;
white-space: pre-wrap;
padding: 0.8em;
}
a {
color: #111111;
text-decoration: underline;
text-decoration-color: #c1c1c1;
}
hr {
border: 0;
height: 0;
margin: 0;
padding: 0;
background: transparent;
page-break-after: always;
break-after: page;
}
"""
def resolve_logo_path(explicit_logo_path=None):
if explicit_logo_path:
explicit_path = Path(explicit_logo_path).expanduser()
if explicit_path.exists():
return explicit_path
candidates = []
env_logo = os.getenv("GEN_PDF_LOGO_PATH")
if env_logo:
candidates.append(Path(env_logo).expanduser())
skill_dir = os.getenv("GEN_PDF_SKILL_DIR")
if skill_dir:
candidates.append(Path(skill_dir).expanduser() / "assets" / "devexpert-logo.png")
candidates.extend(
[
Path.cwd() / "assets" / "devexpert-logo.png",
Path.home() / ".claude" / "skills" / "gen-pdf" / "assets" / "devexpert-logo.png",
]
)
for candidate in candidates:
if candidate.exists():
return candidate
return None
def gen_pdf(input_md, output_pdf, add_logo=True, logo_path=None):
input_md_path = Path(input_md).expanduser()
output_pdf_path = Path(output_pdf).expanduser()
output_pdf_path.parent.mkdir(parents=True, exist_ok=True)
content = input_md_path.read_text(encoding="utf-8")
html_content = md.render(content)
full_html = f"""<!doctype html>
<html>
<head><meta charset="utf-8" /></head>
<body>{html_content}</body>
</html>"""
HTML(string=full_html, base_url=str(input_md_path.parent)).write_pdf(
str(output_pdf_path), stylesheets=[CSS(string=CSS_STYLES)]
)
if not add_logo:
return
resolved_logo = resolve_logo_path(logo_path)
if not resolved_logo:
return
reader = PdfReader(str(output_pdf_path))
writer = PdfWriter()
with tempfile.NamedTemporaryFile(suffix=".pdf", delete=False) as temp_file:
temp_logo_pdf = temp_file.name
page_width, _ = A4
canvas_obj = canvas.Canvas(temp_logo_pdf, pagesize=A4)
canvas_obj.drawImage(
str(resolved_logo),
page_width - 128,
14,
width=110,
height=27,
mask="auto",
preserveAspectRatio=True,
)
canvas_obj.showPage()
canvas_obj.save()
logo_overlay_page = PdfReader(temp_logo_pdf).pages[0]
for page in reader.pages:
page.merge_page(logo_overlay_page)
writer.add_page(page)
with output_pdf_path.open("wb") as output_file:
writer.write(output_file)
os.unlink(temp_logo_pdf)
gen_pdf en un script temporal en /tmp/input_md = ruta al fichero Markdown y output_pdf = ruta de salidaadd_logo=True para documentos DevExpertGEN_PDF_SKILL_DIR o GEN_PDF_LOGO_PATH$TMPDIR/aipal/documents/) y responde con [[document:/ruta]]--- en el Markdown fuerzan salto de pƔgina en el PDFbreaks=True)base_url del fichero de entradaassets/devexpert-logo.png, no el icono cuadrado.