测试一下

SKILL效率
效率 未标注权限 许可证不明确
安装这个技能

Generate PDFs from multiple input sources — HTTP URLs (with login/filter state), raw HTML strings/files, Markdown documents, or images. Use when a user asks to "export/download/save X as PDF", "turn the page/report/markdown into PDF", "screenshot a dashboard to PDF", or any equivalent phrasing. Handles Chinese fonts, ECharts/canvas/SVG charts, authenticated pages, and multi-image composition.

一句话选型结论

按资料字段整理

测试一下是效率类的 Agent Skill,安装方式未标注,权限级别未标注,许可证不明确,有可直接复制的安装命令。技能本质是给模型读的说明书,效果取决于模型与客户端;装完先用一个小任务验证,再放进正式流程。

分类效率 安装方式未标注 权限级别未标注权限 许可证许可证不明确
累计安装 未标注 本周 未标注
GitHub 星标 未标注 仓库 未标注
下载量 未标注 技能市场下载数
权限级别 未标注权限 安装前请确认风险范围
许可证 许可证不明确 未说明

安装

未标注
在支持 Skills 的客户端里执行,装完问一句「你有哪些技能」验证
skillhub install user_8b1ded60/test-wj
  1. 1
    确认客户端支持

    确认你的客户端支持 Skills(Claude、Codex、Cursor 等),并升级到较新的版本。

  2. 2
    拿到技能文件

    执行上面的命令安装;也可以把仓库里的 SKILL.md 手动放进客户端的 skills 目录。

  3. 3
    刷新技能列表

    重新打开客户端,或刷新技能列表,让新放进来的技能被加载。

  4. 4
    验证是否生效

    问一句「你有哪些技能」,确认列表里出现它,再拿一个小任务试跑。

技能档案

效率
字段来自技能仓库与公开索引,中文说明由本站标注
作者 / 维护方user_8b1ded60仓库 未标注 · 版本 未标注
宿主客户端未标注技能可被这些客户端加载
安装方式未标注安装包 community
权限级别未标注权限安装前请确认风险范围
许可证未说明许可证不明确,商用前请自行确认条款
仓库可用性available技能仓库当前是否可访问
最近核验未标注上游最后核验时间 · 数据更新 1791677508
正文体量11.6 KBSKILL.md 原文大小,越大说明说明书写得越细

典型使用场景

3 类
按分类整理的常见用法,具体以 SKILL.md 为准
整理待办与日程

把零散任务收进清单,排出优先级。

批量处理重复任务

把固定动作做成模板,重复执行。

把流程写成清单

把口头流程整理成可复用的步骤。

选择建议

装之前先看这几条
按条目字段给出的判断,不替你决定用不用

值得选的地方

  • 有可直接复制的安装命令,不用自己翻文档找入口。

先想清楚的地方

  • 没有标注权限级别,实际能做什么要装好后确认。
  • 许可证不明确,商用或二次分发前先到仓库确认条款。
  • 没有做站内核验,内容以作者仓库为准。
  • 技能只是说明书,实际效果取决于模型与客户端,装完先用小任务试一遍。

更适合

  • 需要在效率场景里给模型补一套做法的人。
  • 愿意先读一遍 SKILL.md、确认它会让模型做什么的人。

不太适合

  • 对许可证要求很严、必须明确可商用的人。
  • 只想装完就自动变强、不打算看说明书的人。

同类对比

6 个同类
同分类里挑最接近的几条,字段以各自详情页为准
技能权限级别安装方式累计安装宿主客户端安装命令
测试一下当前 未标注权限 未标注 未标注 未标注 有
Nano PDF 文档 联网访问 ClawHub 市场 424.9 万 OpenClaw 有
自动更新程序 浏览器控制 ClawHub 市场 296.4 万 OpenClaw 有
Gemini 助手 联网访问 ClawHub 市场 254.7 万 OpenClaw 有
word DOCX 文档 联网访问 ClawHub 市场 239.6 万 OpenClaw 有
Excel XLSX 前端 联网访问 ClawHub 市场 227.0 万 OpenClaw 有

SKILL.md 全文

11.6 KB
模型实际读到的说明书原文,来自技能仓库

PDF Generator

A practical guide for turning four common input types into PDF files: HTTP URLs, HTML (string or file), Markdown, and Images. Covers the rendering trade-offs and gives working Python recipes.

When to use this skill

Trigger whenever the user wants a PDF produced from content that is not already a PDF. Typical phrasings:

  • "Export the overview page / dashboard as PDF"
  • "Download this report as PDF"
  • "Generate a PDF from this Markdown / README"
  • "Turn these screenshots into a single PDF"
  • "给我一个 PDF 下载 / 导出当前页面为 PDF"

Decision flow — pick the right backend

Input Needs JS / charts? Recommended backend Why
HTTP URL (dashboards, ECharts, auth required) Yes Playwright + full-page screenshot → PDF Real browser, honors session cookies, no measurement pitfalls
HTTP URL (static HTML only) No WeasyPrint via HTML(url=...) Pure Python, faster, text selectable
HTML string / file (print styles, CSS grid) No WeasyPrint Honors @page, CJK fonts, page breaks, text selectable
HTML with JS rendering / charts Yes Playwright + page.pdf() or screenshot JS executes before capture
Markdown No markdown → HTML → WeasyPrint Lightweight; control styling via CSS
Images (PNG/JPG) N/A Pillow .save(pdf) or img2pdf img2pdf is lossless for JPEGs; Pillow is simpler

The "whitespace / compression" trap for URL → PDF

Two failure modes keep showing up:

  1. A4 compression — default page.pdf() uses A4 (~793 px wide). Desktop dashboards (1440 px) get crammed. Fix: render with explicit desktop viewport and set PDF width to match.
  2. Bottom whitespace — setting explicit PDF height and measuring scrollHeight is fragile (async Tailwind JIT, flex min-height, ECharts async redraw all distort it). Fix: use page.screenshot(full_page=True) and wrap the PNG into a same-size single-page PDF via Pillow. Chromium handles the height internally — zero whitespace by construction.

This is the canonical recipe for dashboards with ECharts/canvas charts.

Installation

# Playwright (for URL and JS-HTML paths)
pip install playwright pillow
playwright install chromium
# On RHEL/CentOS/Alibaba Linux, Chromium system libs:
sudo yum install -y atk at-spi2-atk cups-libs libXcomposite libXrandr libXi libXtst

# WeasyPrint (for static HTML and Markdown)
pip install weasyprint markdown
# system fonts for CJK:
sudo yum install -y google-noto-sans-cjk-fonts  # or equivalent

# Image-only path
pip install pillow img2pdf

Recipe 1 — HTTP URL → PDF (authenticated, JS-rendered)

Use when the page has login, filters, ECharts, or any client-side rendering. See scripts/url_to_pdf.py for the full runnable version.

from io import BytesIO
from urllib.parse import urlparse
from playwright.sync_api import sync_playwright
from PIL import Image

def url_to_pdf(url, session_cookie=None, cookie_domain=None,
               viewport_width=1440, wait_ms=1500):
    """Render a URL to a single-page PDF via full-page screenshot.

    session_cookie: {'name': 'session', 'value': '...'} to reuse login
    """
    PRINT_CSS = """
      nav, footer, .no-print { display: none !important; }
      body { background: #fff !important; }
    """
    parsed = urlparse(url)
    domain = cookie_domain or parsed.hostname or "localhost"

    with sync_playwright() as pw:
        browser = pw.chromium.launch(args=["--no-sandbox", "--disable-dev-shm-usage"])
        ctx = browser.new_context(
            viewport={"width": viewport_width, "height": 900},
            device_scale_factor=2,
        )
        if session_cookie:
            ctx.add_cookies([{
                "name": session_cookie["name"], "value": session_cookie["value"],
                "domain": domain, "path": "/",
                "httpOnly": True, "sameSite": "Lax",
            }])
        page = ctx.new_page()
        page.emulate_media(media="screen")
        page.goto(url, wait_until="networkidle", timeout=45000)
        page.wait_for_timeout(wait_ms)
        page.add_style_tag(content=PRINT_CSS)
        page.evaluate("() => window.dispatchEvent(new Event('resize'))")
        page.wait_for_timeout(500)
        png = page.screenshot(full_page=True, type="png")
        browser.close()

    img = Image.open(BytesIO(png))
    if img.mode in ("RGBA", "LA", "P"):
        bg = Image.new("RGB", img.size, (255, 255, 255))
        bg.paste(img, mask=img.split()[-1] if img.mode == "RGBA" else None)
        img = bg
    out = BytesIO()
    img.save(out, format="PDF", resolution=144.0)
    out.seek(0)
    return out

Key knobs:

  • viewport_width=1440 — match the design width; don't use A4 for dashboards
  • device_scale_factor=2 — high-DPI output
  • emulate_media('screen') — prevent @media print from collapsing layout
  • networkidle + wait_ms + a resize event — gives ECharts time to settle

If you must have multi-page paginated PDF (text selectable, printable), swap screenshot for page.pdf(format='A4', print_background=True) and accept the width constraints.

Recipe 2 — HTML string/file → PDF (no JS needed)

WeasyPrint reads HTML+CSS and paginates per @page rules. Great for reports. Full script: scripts/html_to_pdf.py.

from io import BytesIO
from weasyprint import HTML, CSS

def html_to_pdf(html_str, base_url=None, extra_css=None):
    css_list = [CSS(string=PDF_BASE_CSS)]
    if extra_css:
        css_list.append(CSS(string=extra_css))
    buf = BytesIO()
    HTML(string=html_str, base_url=base_url).write_pdf(buf, stylesheets=css_list)
    buf.seek(0)
    return buf

PDF_BASE_CSS = """
  @page { size: A4; margin: 18mm 16mm;
          @bottom-center { content: counter(page) " / " counter(pages);
                           font-size: 9pt; color: #94a3b8; } }
  body { font-family: "Noto Sans CJK SC", "Microsoft YaHei", sans-serif;
         font-size: 11pt; color: #1e293b; line-height: 1.6; }
  h1 { color: #6366F1; border-bottom: 2px solid #6366F1; padding-bottom: 6px; }
  table { border-collapse: collapse; width: 100%; page-break-inside: auto; }
  tr { page-break-inside: avoid; }
  th, td { border: 1px solid #e2e8f0; padding: 6px 8px; }
  th { background: #f1f5f9; }
  img { max-width: 100%; }
"""

For local files / inline images, pass base_url=os.path.dirname(os.path.abspath(path)) so relative paths resolve. For remote images, embed as base64 data URIs to avoid network during rendering.

Recipe 3 — Markdown → PDF

Convert MD → HTML, then reuse Recipe 2. Full script: scripts/markdown_to_pdf.py.

import markdown
from weasyprint import HTML, CSS

def markdown_to_pdf(md_text, title="Document"):
    html_body = markdown.markdown(
        md_text,
        extensions=["tables", "fenced_code", "toc", "sane_lists"],
    )
    html_doc = f"""<!DOCTYPE html><html><head><meta charset="utf-8">
      <title>{title}</title></head><body>
      <h1>{title}</h1>{html_body}</body></html>"""
    from io import BytesIO
    buf = BytesIO()
    HTML(string=html_doc).write_pdf(buf, stylesheets=[CSS(string=PDF_BASE_CSS)])
    buf.seek(0)
    return buf

Tables, code blocks, nested lists all work. Syntax highlighting requires the codehilite extension + Pygments CSS.

Recipe 4 — Images → PDF

Single image or multi-image collage. scripts/images_to_pdf.py.

from io import BytesIO
from PIL import Image

def images_to_pdf(paths):
    """Multiple images → multi-page PDF. Each image is one page."""
    imgs = [Image.open(p).convert("RGB") for p in paths]
    buf = BytesIO()
    imgs[0].save(buf, format="PDF", save_all=True, append_images=imgs[1:],
                 resolution=144.0)
    buf.seek(0)
    return buf

For pixel-perfect JPEG retention (no recompression), prefer img2pdf:

import img2pdf
with open("out.pdf", "wb") as f:
    f.write(img2pdf.convert(["a.jpg", "b.jpg"]))

Serving PDFs from Flask

End-to-end pattern for a "download this page as PDF" button in a Flask app:

from flask import Blueprint, request, send_file, current_app, abort
from urllib.parse import urljoin, urlparse

bp = Blueprint("export", __name__)
ALLOWED_PREFIXES = ("/", "/report/")

@bp.route("/export/pdf")
def export_pdf():
    path = request.args.get("path", "").strip()
    if not path.startswith("/") or "://" in path:
        abort(400)
    if not any(path == p or path.startswith(p) for p in ALLOWED_PREFIXES):
        abort(403)
    target_url = urljoin(request.host_url, path.lstrip("/"))
    cookie_name = current_app.config.get("SESSION_COOKIE_NAME", "session")
    cookie_value = request.cookies.get(cookie_name)
    if not cookie_value:
        abort(401)
    pdf_buf = url_to_pdf(
        target_url,
        session_cookie={"name": cookie_name, "value": cookie_value},
        cookie_domain=urlparse(request.host_url).hostname,
    )
    return send_file(pdf_buf, as_attachment=True,
                     download_name="report.pdf", mimetype="application/pdf")

Front-end: fetch with credentials: 'same-origin', wrap response as a Blob, trigger <a download> — gives you a progress/error UI instead of a silent iframe redirect.

Troubleshooting

  • Content compressed in PDF — you're using A4 portrait with a wide desktop layout. Fix: use viewport_width=1440 + PDF width matching viewport, or switch to the screenshot-based recipe.
  • Blank page / white bottom — measuring scrollHeight and passing it as PDF height. Switch to screenshot(full_page=True) + Pillow PDF wrap.
  • Chinese text rendered as boxes — no CJK font installed on the rendering machine. Install Noto Sans CJK SC and reference it in font-family.
  • libatk-1.0.so.0: No such file or directory — Chromium system deps missing. On RHEL family: sudo yum install -y atk at-spi2-atk cups-libs libXcomposite libXrandr libXi libXtst.
  • Auth page returned instead of content — the session cookie wasn't injected or its domain doesn't match the target host. Log the cookies Playwright sees: ctx.cookies().
  • ECharts tooltip or animation appears mid-frame — wait_for_timeout too short, or animations enabled. Bump wait, or disable animations with echartsInstance.setOption({animation:false}) before capture.
  • PDF huge (~10MB+) — screenshot resolution too high. Drop device_scale_factor to 1, or compress the PNG before embedding.

File map

  • scripts/url_to_pdf.py — Recipe 1, runnable with --url argument
  • scripts/html_to_pdf.py — Recipe 2
  • scripts/markdown_to_pdf.py — Recipe 3
  • scripts/images_to_pdf.py — Recipe 4
  • scripts/flask_export_blueprint.py — drop-in Flask blueprint

常见问题

5 问
答案由本站依据条目字段整理
测试一下怎么安装?

在支持 Skills 的客户端里执行:skillhub install user_8b1ded60/test-wj。安装方式为未标注。装完刷新技能列表或重开客户端。

测试一下支持哪些客户端?

资料没有标注宿主客户端,但只要客户端支持 Skills(Claude、Codex、Cursor 等),一般都能加载。

测试一下权限高吗,安全吗?

权限级别为未标注权限。安装前先看它会读取或写入哪些内容。

测试一下收费吗?

本站不代收费用,也没有标注具体价格。是否收费取决于技能本身和所用的模型或客户端,正式使用前按作者说明确认。

测试一下用什么许可证?

许可证标注为 未说明(许可证不明确)。商用或二次分发前请到仓库确认条款原文。

热门专题

按场景挑技能
全部专题 →

技能内容以作者仓库为准;分类、权限级别与许可证状态的中文说明为中文整理,最近核验 未标注。