Skill flagged — suspicious patterns detected

ClawHub Security flagged this skill as suspicious. Review the scan results before using.

Pdf Studio

v1.0.4

Professional PDF document generator. Use when user needs to create reports, invoices, certificates, portfolios, or any publication-ready PDF. Supports images...

0· 120·0 current·0 all-time

Install

OpenClaw Prompt Flow

Install with OpenClaw

Best for remote or guided setup. Copy the exact prompt, then paste it into OpenClaw for tobewin/pdf-studio.

Previewing Install & Setup.
Prompt PreviewInstall & Setup
Install the skill "Pdf Studio" (tobewin/pdf-studio) from ClawHub.
Skill page: https://clawhub.ai/tobewin/pdf-studio
Keep the work scoped to this skill only.
After install, inspect the skill metadata and help me finish setup.
Required binaries: python3
Use only the metadata you can verify from ClawHub; do not invent missing requirements.
Ask before making any broader environment changes.

Command Line

CLI Commands

Use the direct CLI path if you want to install manually and keep every step visible.

OpenClaw CLI

Bare skill slug

openclaw skills install pdf-studio

ClawHub CLI

Package manager switcher

npx clawhub@latest install pdf-studio
Security Scan
VirusTotalVirusTotal
Suspicious
View report →
OpenClawOpenClaw
Benign
high confidence
Purpose & Capability
Name/description (PDF generator) aligns with required binary (python3) and the SKILL.md which includes Python templates and recommends installing fpdf2 and pillow. The declared capabilities (images, tables, fonts, templates) are consistent with the libraries referenced.
Instruction Scope
SKILL.md contains prompts to collect document parameters from the user and a Python script template that generates PDFs. The instructions ask the agent to install libraries and run a Python heredoc to produce files. There are no instructions to harvest unrelated system files, access credentials, or send data to external endpoints.
Install Mechanism
This is an instruction-only skill (no install spec). The SKILL.md recommends running `pip install fpdf2 pillow` at runtime — a normal choice for Python PDF generation. Installing third-party PyPI packages is expected but carries the usual supply-chain risk (review packages or run in a sandbox if you distrust the source).
Credentials
The skill declares no required environment variables, no credential access, and no config paths. That is proportionate for a document-generation skill which only needs local Python and libraries. The instructions likewise do not reference hidden env vars or credentials.
Persistence & Privilege
always is false and the skill does not request permanent presence or modify other skills or global agent configuration. Normal autonomous invocation is allowed (platform default) and does not by itself raise additional concern.
Assessment
This skill appears coherent and implements a PDF generator by running Python code and installing common libraries (fpdf2, pillow). Before using it: - Review the entire SKILL.md and template files (they're included) to confirm there are no unexpected network endpoints or commands you disagree with. The provided files appear local and template-focused. - Understand that following the instructions will install PyPI packages; if you don't trust the source, run the install and generation in an isolated environment (virtualenv, container, or sandbox) to limit risk. - The skill will read any image/files you provide and write PDF output to disk; avoid pointing it at sensitive directories you don't want written to or read. - If you need auditability, manually inspect the generated Python snippet for any commands that execute shell commands or reach out to external URLs (none are obvious in the provided excerpts). Overall, it's consistent with its stated purpose, but exercise the normal caution when running third-party Python code and installing packages from PyPI.

Like a lobster shell, security has layers — review code before you run it.

Runtime requirements

📑 Clawdis
Binspython3
latestvk971xeewjnjb9swqkpy64geerh83hq0c
120downloads
0stars
5versions
Updated 1mo ago
v1.0.4
MIT-0

PDF Studio

Professional PDF document generator that creates publication-ready PDFs with perfect typography and layout.

Features

  • 📑 Print Quality: 300 DPI, CMYK support, bleed marks
  • 🎨 Professional Layout: Multi-column, headers/footers, page numbers
  • 🖼️ Image Support: Embedded images, captions, watermarks
  • 📊 Tables & Charts: Formatted tables, bar charts, pie charts
  • 📐 Typography: Professional fonts, kerning, ligatures
  • 🌍 Multi-Language: Chinese, English, Japanese, etc.
  • Cross-Platform: Windows, macOS, Linux
  • 📱 PDF/A Compliant: Archival quality

Trigger Conditions

  • "生成PDF" / "Generate PDF"
  • "帮我做一份报告" / "Create a report"
  • "制作发票" / "Create an invoice"
  • "生成证书" / "Generate certificate"
  • "制作作品集" / "Create portfolio"
  • "pdf-studio"

Document Types

Business (商务)

  • 年度报告
  • 商业计划书
  • 发票/账单
  • 合同/协议
  • 产品手册

Academic (学术)

  • 论文排版
  • 学位证书
  • 研究报告
  • 会议论文

Government (政府)

  • 公文
  • 公告
  • 证书
  • 表格

Personal (个人)

  • 简历
  • 作品集
  • 证书
  • 邀请函

Step 1: Understand Requirements

请提供以下信息:

文档类型:(报告/发票/证书/简历/其他)
纸张大小:(A4/Letter/A3/自定义)
方向:(纵向/横向)
页边距:(默认/自定义)
字体要求:(默认/指定字体)
语言:(中文/英文)
特殊要求:(水印/页眉页脚/目录等)

Step 2: Generate PDF

Using FPDF2 (Recommended for simplicity)

# Install dependencies
pip install fpdf2 pillow

Python Script Template (with Unicode Support)

python3 << 'PYEOF'
import os
from fpdf import FPDF
from datetime import datetime

class PDFGenerator:
    def __init__(self, config):
        self.config = config
        self.html_content = []
        self.css_content = []
        self.font_config = FontConfiguration()
        
    def add_page_setup(self, size='A4', orientation='portrait', margins=None):
        """Setup page dimensions"""
        if margins is None:
            margins = {'top': '2cm', 'right': '2cm', 'bottom': '2cm', 'left': '2cm'}
        
        page_size = {
            'A4': '210mm 297mm',
            'Letter': '216mm 279mm',
            'A3': '297mm 420mm'
        }.get(size, size)
        
        self.css_content.append(f'''
        @page {{
            size: {page_size} {orientation};
            margin: {margins['top']} {margins['right']} {margins['bottom']} {margins['left']};
            
            @top-center {{
                content: "{self.config.get('header_text', '')}";
                font-size: 9pt;
                color: #666;
            }}
            
            @bottom-center {{
                content: "Page " counter(page) " of " counter(pages);
                font-size: 9pt;
                color: #666;
            }}
            
            @bottom-right {{
                content: "{self.config.get('footer_text', '')}";
                font-size: 9pt;
                color: #666;
            }}
        }}
        ''')
        
    def add_base_styles(self, font_family='Noto Serif SC', font_size='12pt'):
        """Add base CSS styles"""
        self.css_content.append(f'''
        body {{
            font-family: '{font_family}', 'Noto Serif', serif;
            font-size: {font_size};
            line-height: 1.8;
            color: #333;
        }}
        
        h1 {{
            font-family: '{font_family.replace('Serif', 'Sans')}', 'Noto Sans', sans-serif;
            font-size: 24pt;
            color: #1a365d;
            border-bottom: 3px solid #3182ce;
            padding-bottom: 0.5em;
            margin-top: 1.5em;
            page-break-after: avoid;
        }}
        
        h2 {{
            font-family: '{font_family.replace('Serif', 'Sans')}', 'Noto Sans', sans-serif;
            font-size: 18pt;
            color: #2c5282;
            border-left: 4px solid #3182ce;
            padding-left: 1em;
            margin-top: 1.2em;
            page-break-after: avoid;
        }}
        
        h3 {{
            font-family: '{font_family.replace('Serif', 'Sans')}', 'Noto Sans', sans-serif;
            font-size: 14pt;
            color: #3182ce;
            margin-top: 1em;
            page-break-after: avoid;
        }}
        
        p {{
            text-align: justify;
            text-indent: 2em;
            margin: 0.5em 0;
        }}
        
        table {{
            width: 100%;
            border-collapse: collapse;
            margin: 1em 0;
            font-size: 10pt;
            page-break-inside: avoid;
        }}
        
        th {{
            background-color: #3182ce;
            color: white;
            padding: 0.8em;
            text-align: left;
            font-weight: 600;
        }}
        
        td {{
            border: 1px solid #e2e8f0;
            padding: 0.6em 0.8em;
        }}
        
        tr:nth-child(even) {{
            background-color: #f7fafc;
        }}
        
        .cover {{
            text-align: center;
            padding-top: 30%;
            page-break-after: always;
        }}
        
        .cover h1 {{
            border: none;
            font-size: 28pt;
        }}
        
        .cover .subtitle {{
            font-size: 14pt;
            color: #4a5568;
            margin-top: 1em;
        }}
        
        .cover .author {{
            font-size: 12pt;
            margin-top: 2em;
        }}
        
        .cover .date {{
            font-size: 12pt;
            color: #718096;
            margin-top: 1em;
        }}
        
        .toc {{
            page-break-after: always;
        }}
        
        .toc a {{
            color: #3182ce;
            text-decoration: none;
        }}
        
        .figure {{
            text-align: center;
            margin: 1em 0;
            page-break-inside: avoid;
        }}
        
        .figure img {{
            max-width: 100%;
            height: auto;
        }}
        
        .figure-caption {{
            font-size: 10pt;
            color: #718096;
            margin-top: 0.5em;
        }}
        
        .code {{
            background-color: #2d3748;
            color: #e2e8f0;
            padding: 1em;
            border-radius: 5px;
            font-family: 'Fira Code', 'Consolas', monospace;
            font-size: 9pt;
            white-space: pre-wrap;
            page-break-inside: avoid;
        }}
        
        .highlight {{
            background-color: #ebf8ff;
            border: 1px solid #90cdf4;
            border-radius: 5px;
            padding: 1em;
            margin: 1em 0;
        }}
        
        .warning {{
            background-color: #fffaf0;
            border: 1px solid #f6ad55;
            border-radius: 5px;
            padding: 1em;
            margin: 1em 0;
        }}
        
        blockquote {{
            border-left: 4px solid #718096;
            background-color: #f7fafc;
            padding: 1em;
            margin: 1em 0;
            font-style: italic;
        }}
        
        ul, ol {{
            margin: 0.5em 0;
            padding-left: 2em;
        }}
        
        li {{
            margin-bottom: 0.3em;
        }}
        
        strong {{
            color: #1a365d;
        }}
        
        .page-break {{
            page-break-before: always;
        }}
        ''')
    
    def add_cover(self, title, subtitle='', author='', date=None):
        """Add cover page"""
        if date is None:
            date = datetime.now().strftime('%Y年%m月%d日')
        
        self.html_content.append(f'''
        <div class="cover">
            <h1>{title}</h1>
            {f'<div class="subtitle">{subtitle}</div>' if subtitle else ''}
            {f'<div class="author">{author}</div>' if author else ''}
            <div class="date">{date}</div>
        </div>
        ''')
    
    def add_toc(self, title='目录'):
        """Add table of contents placeholder"""
        self.html_content.append(f'''
        <div class="toc">
            <h1>{title}</h1>
            <p style="font-style: italic; color: #718096;">
                (在支持目录生成的PDF阅读器中自动生成)
            </p>
        </div>
        ''')
    
    def add_heading(self, text, level=1):
        """Add heading"""
        self.html_content.append(f'<h{level}>{text}</h{level}>')
    
    def add_paragraph(self, text, indent=True):
        """Add paragraph"""
        if indent:
            self.html_content.append(f'<p>{text}</p>')
        else:
            self.html_content.append(f'<p style="text-indent: 0;">{text}</p>')
    
    def add_table(self, headers, rows, caption=None):
        """Add formatted table"""
        table_html = '<table>'
        
        # Header
        table_html += '<thead><tr>'
        for header in headers:
            table_html += f'<th>{header}</th>'
        table_html += '</tr></thead>'
        
        # Body
        table_html += '<tbody>'
        for row in rows:
            table_html += '<tr>'
            for cell in row:
                table_html += f'<td>{cell}</td>'
            table_html += '</tr>'
        table_html += '</tbody></table>'
        
        if caption:
            table_html = f'<div class="figure"><div class="figure-caption">{caption}</div>{table_html}</div>'
        
        self.html_content.append(table_html)
    
    def add_image(self, image_path, caption=None, width='80%'):
        """Add image with caption"""
        # Convert to data URI for embedding
        import base64
        with open(image_path, 'rb') as f:
            img_data = base64.b64encode(f.read()).decode()
        
        ext = os.path.splitext(image_path)[1].lower()
        mime = {
            '.png': 'image/png',
            '.jpg': 'image/jpeg',
            '.jpeg': 'image/jpeg',
            '.gif': 'image/gif',
            '.svg': 'image/svg+xml'
        }.get(ext, 'image/png')
        
        img_html = f'<div class="figure">'
        img_html += f'<img src="data:{mime};base64,{img_data}" style="max-width: {width};">'
        if caption:
            img_html += f'<div class="figure-caption">{caption}</div>'
        img_html += '</div>'
        
        self.html_content.append(img_html)
    
    def add_code_block(self, code, language=''):
        """Add code block"""
        self.html_content.append(f'<div class="code">{code}</div>')
    
    def add_blockquote(self, text):
        """Add blockquote"""
        self.html_content.append(f'<blockquote>{text}</blockquote>')
    
    def add_list(self, items, ordered=False):
        """Add list"""
        tag = 'ol' if ordered else 'ul'
        list_html = f'<{tag}>'
        for item in items:
            list_html += f'<li>{item}</li>'
        list_html += f'</{tag}>'
        self.html_content.append(list_html)
    
    def add_page_break(self):
        """Add page break"""
        self.html_content.append('<div class="page-break"></div>')
    
    def render(self):
        """Render HTML and CSS"""
        html = f'''
        <!DOCTYPE html>
        <html>
        <head>
            <meta charset="UTF-8">
            <style>
            {''.join(self.css_content)}
            </style>
        </head>
        <body>
            {''.join(self.html_content)}
        </body>
        </html>
        '''
        return html
    
    def save(self, output_path):
        """Save to PDF"""
        html_content = self.render()
        css_content = '\n'.join(self.css_content)
        
        html = HTML(string=html_content)
        css = CSS(string=css_content, font_config=self.font_config)
        
        html.write_pdf(output_path, stylesheets=[css], font_config=self.font_config)
        
        return output_path

# 示例使用
def create_sample_pdf():
    config = {
        'header_text': 'Sample Report',
        'footer_text': 'Confidential'
    }
    
    pdf = PDFGenerator(config)
    pdf.add_page_setup('A4', 'portrait')
    pdf.add_base_styles('Noto Serif SC', '12pt')
    
    # 封面
    pdf.add_cover(
        '2026年AI行业发展趋势报告',
        '从大模型到智能体时代的全面变革',
        'AI研究院'
    )
    
    # 目录
    pdf.add_toc()
    
    # 内容
    pdf.add_heading('一、市场概况', 1)
    pdf.add_paragraph('2026年,全球人工智能市场规模突破3.2万亿美元,年复合增长率达到47%。')
    pdf.add_paragraph('企业AI采用率从2025年的72%提升至89%,标志着AI技术正式进入全面普及阶段。')
    
    # 表格
    pdf.add_heading('二、核心数据', 1)
    pdf.add_table(
        ['指标', '2025年', '2026年', '增长率'],
        [
            ['全球市场规模', '$2.1万亿', '$3.2万亿', '52%'],
            ['企业采用率', '72%', '89%', '24%'],
            ['风险投资', '$790亿', '$1,280亿', '62%']
        ],
        caption='表2-1:AI市场核心指标'
    )
    
    # 列表
    pdf.add_heading('三、技术趋势', 1)
    pdf.add_list([
        '大模型能力持续提升',
        'Agent技术走向成熟',
        '开源生态蓬勃发展'
    ])
    
    return pdf

# 生成示例PDF
output_dir = os.environ.get('OPENCLAW_WORKSPACE', os.getcwd())
output_path = os.path.join(output_dir, 'sample-report.pdf')

pdf = create_sample_pdf()
pdf.save(output_path)

print(f"✅ PDF已生成:{output_path}")
PYEOF

Typography Settings (字体设置)

Cross-Platform Fonts

FONTS = {
    'chinese': {
        'serif': 'Noto Serif SC',
        'sans': 'Noto Sans SC',
        'mono': 'Noto Sans Mono CJK SC'
    },
    'english': {
        'serif': 'Liberation Serif',
        'sans': 'Liberation Sans',
        'mono': 'Fira Code'
    },
    'japanese': {
        'serif': 'Noto Serif JP',
        'sans': 'Noto Sans JP',
        'mono': 'Noto Sans Mono CJK JP'
    }
}

Font Features

body {
    font-feature-settings: "liga" 1, "kern" 1;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

Page Layout (页面布局)

Multi-Column Layout

.two-column {
    column-count: 2;
    column-gap: 2em;
    column-rule: 1px solid #e2e8f0;
}

Margin Presets

MARGINS = {
    'normal': {'top': '2.54cm', 'right': '2.54cm', 'bottom': '2.54cm', 'left': '2.54cm'},
    'narrow': {'top': '1.27cm', 'right': '1.27cm', 'bottom': '1.27cm', 'left': '1.27cm'},
    'wide': {'top': '2.54cm', 'right': '5.08cm', 'bottom': '2.54cm', 'left': '5.08cm'}
}

Error Handling

字体缺失       → 使用开源字体回退
图片格式不支持 → 转换为PNG/JPEG
内存不足       → 分页处理大文档
生成失败       → 输出HTML备选

Security Notes (安全说明)

  • ✅ No network calls or external endpoints
  • ✅ No credentials or API keys required
  • ✅ Local file processing only
  • ✅ Open source dependencies (fpdf2, pillow from PyPI)
  • ✅ No data uploaded to external servers
  • ⚠️ Requires python3 and pip install
  • ⚠️ Review code before execution

Notes

  • 使用FPDF2确保跨平台一致性
  • 所有字体可嵌入PDF中
  • 支持PDF/A归档标准
  • 图片自动压缩优化
  • 支持水印和页眉页脚

Comments

Loading comments...