[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6HXS0GS9VTJMX8HE8PRHAZE":3,"skill-same:01M6HXS0GS9VTJMX8HE8PRHAZE":62},{"skillNo":4,"name":5,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":22,"publishedAt":29,"linkOnly":30,"markdownContent":31,"markdownBody":32,"frontMatter":33,"refName":34,"commitSha":35,"skillDirectory":36,"forksCount":37,"repositoryHtmlUrl":38,"repositoryHomepageUrl":6,"ownerHtmlUrl":39,"ownerBlogUrl":40,"ownerDescription":6,"updatedAt":41,"install":42,"resources":55},"01M6HXS0GS9VTJMX8HE8PRHAZE","pdf-verification-cli-enhanced",null,"HKUDS\u002FAgentPilot","HKUDS","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F118165258?v=4","01M6HW8VRNZP64G0P4MVJW2KKQ","✨Data Intelligence Lab@HKU✨","01M6HW8VWZVHBRHYH3T330PFWV",7993,"MIT","https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot\u002Fblob\u002Fmain\u002Fopenspace\u002Flegacy\u002Fv1\u002Fgdpval_bench\u002Fskills\u002Fpdf-verification-cli-enhanced\u002FSKILL.md",[17],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000002","文档处理","SCENARIO","使用场景",[23,24,25,26,27,28],"PDF","cli","verification","pdf verification cli enhanced","pdf verification cli","pdf verification","2026-11-10 10:12:30",false,"---\nname: pdf-report-workflow-cli\ndescription: Complete PDF workflow: verify, extract content, assemble reports, and generate output PDFs using command-line tools\n---\n\n# PDF Report Generation with Command-Line Tools\n\nWhen working with PDFs in minimal\u002Fcontainerized environments where Python libraries may be unavailable, this skill provides a complete end-to-end workflow: verify source PDFs, extract content, assemble structured reports, and generate final PDF output.\n\n## When to Use This Skill\n\n- Need to create new PDF reports from existing PDF sources\n- Need to verify, extract, and combine PDF content\n- Working in environments without Python PDF libraries\n- Building document assembly pipelines in CI\u002FCD or containers\n\n## Complete Workflow Overview\n\n```\n[Source PDFs] → [Verify] → [Extract] → [Assemble] → [Generate Output PDF]\n```\n\n## Phase 1: Tool Availability Check\n\n```bash\n# Core extraction tools (from poppler-utils)\nwhich pdfinfo && echo \"✓ pdfinfo available\" || echo \"✗ pdfinfo missing\"\nwhich pdftotext && echo \"✓ pdftotext available\" || echo \"✗ pdftotext missing\"\n\n# PDF generation tools (choose based on availability)\nwhich pdftk && echo \"✓ pdftk available (PDF merging)\" || true\nwhich wkhtmltopdf && echo \"✓ wkhtmltopdf available (HTML→PDF)\" || true\nwhich pandoc && echo \"✓ pandoc available (document conversion)\" || true\nwhich enscript && echo \"✓ enscript available (text→PS)\" || true\nwhich ps2pdf && echo \"✓ ps2pdf available (PS→PDF)\" || true\n```\n\n### Install Required Tools\n\n```bash\n# Debian\u002FUbuntu - Core tools\napt-get update && apt-get install -y poppler-utils\n\n# Optional: PDF generation tools (choose based on needs)\napt-get install -y pdftk          # PDF merging\u002Fmanipulation\napt-get install -y wkhtmltopdf    # HTML to PDF\napt-get install -y pandoc         # Document conversion\napt-get install -y enscript ghostscript  # Text to PDF pipeline\n\n# RHEL\u002FCentOS\u002FFedora\nyum install -y poppler-utils pdftk ghostscript\n# or\ndnf install -y poppler-utils pdftk ghostscript\n\n# macOS (with Homebrew)\nbrew install poppler pdftk ghostscript\nbrew install --cask wkhtmltopdf  # GUI app, includes CLI\n```\n\n## Phase 2: Verify Source PDFs\n\n### Check Page Count and Metadata\n\n```bash\n# Verify each source PDF exists and is readable\nfor pdf in source1.pdf source2.pdf source3.pdf; do\n    if [ ! -f \"$pdf\" ]; then\n        echo \"ERROR: $pdf not found\"\n        exit 1\n    fi\n    \n    pages=$(pdfinfo \"$pdf\" | grep Pages | awk '{print $2}')\n    echo \"$pdf: $pages pages\"\ndone\n```\n\n### Validate Content Presence\n\n```bash\n# Check that required sections exist in source documents\nREQUIRED_TERMS=(\"checklist\" \"summary\" \"references\")\n\nfor term in \"${REQUIRED_TERMS[@]}\"; do\n    if pdftotext source.pdf - | grep -qi \"$term\"; then\n        echo \"✓ Found: $term\"\n    else\n        echo \"⚠ Missing: $term\"\n    fi\ndone\n```\n\n## Phase 3: Extract Content from Source PDFs\n\n### Extract Text to Temporary Files\n\n```bash\n# Create working directory\nWORK_DIR=$(mktemp -d)\necho \"Working directory: $WORK_DIR\"\n\n# Extract text from each source PDF\nfor i in source1.pdf source2.pdf source3.pdf; do\n    base=$(basename \"$i\" .pdf)\n    pdftotext -layout \"$i\" \"$WORK_DIR\u002F${base}.txt\"\n    echo \"Extracted: $i → ${base}.txt\"\ndone\n\n# Also extract metadata for reference\npdfinfo source1.pdf > \"$WORK_DIR\u002Fsource1_metadata.txt\"\n```\n\n### Extract Specific Sections (Optional)\n\n```bash\n# Extract only specific page ranges\npdftotext -f 1 -l 3 source1.pdf \"$WORK_DIR\u002Fsource1_pages1-3.txt\"\n\n# Extract and filter for specific content\npdftotext source1.pdf - | grep -A 10 \"Summary\" > \"$WORK_DIR\u002Fsummary_section.txt\"\n```\n\n## Phase 4: Assemble Report Content\n\n### Create Structured Report (Markdown Format)\n\n```bash\nREPORT_MD=\"$WORK_DIR\u002Freport.md\"\n\ncat > \"$REPORT_MD\" \u003C\u003C 'REPORT_HEADER'\n# New Case Creation Report\n\n**Generated:** $(date '+%Y-%m-%d %H:%M:%S')\n\n---\n\n## Executive Summary\n\nREPORT_HEADER\n\n# Add content from extracted sources\necho \"## Case Details\" >> \"$REPORT_MD\"\necho \"\" >> \"$REPORT_MD\"\ncat \"$WORK_DIR\u002Fsource1.txt\" >> \"$REPORT_MD\"\necho \"\" >> \"$REPORT_MD\"\n\necho \"## Supporting Documentation\" >> \"$REPORT_MD\"\necho \"\" >> \"$REPORT_MD\"\ncat \"$WORK_DIR\u002Fsource2.txt\" >> \"$REPORT_MD\"\necho \"\" >> \"$REPORT_MD\"\n\necho \"## References\" >> \"$REPORT_MD\"\necho \"\" >> \"$REPORT_MD\"\ncat \"$WORK_DIR\u002Fsource3.txt\" >> \"$REPORT_MD\"\n\necho \"Report assembled: $REPORT_MD\"\n```\n\n### Create HTML Report (Alternative for Better Formatting)\n\n```bash\nREPORT_HTML=\"$WORK_DIR\u002Freport.html\"\n\ncat > \"$REPORT_HTML\" \u003C\u003C 'HTML_HEADER'\n\u003C!DOCTYPE html>\n\u003Chtml>\n\u003Chead>\n    \u003Cmeta charset=\"UTF-8\">\n    \u003Ctitle>Case Creation Report\u003C\u002Ftitle>\n    \u003Cstyle>\n        body { font-family: Arial, sans-serif; margin: 40px; }\n        h1 { color: #333; border-bottom: 2px solid #333; }\n        h2 { color: #666; }\n        .section { margin: 20px 0; }\n        .metadata { font-size: 0.9em; color: #888; }\n    \u003C\u002Fstyle>\n\u003C\u002Fhead>\n\u003Cbody>\n\u003Ch1>New Case Creation Report\u003C\u002Fh1>\n\u003Cp class=\"metadata\">Generated: HTML_HEADER\n\ndate '+%Y-%m-%d %H:%M:%S' >> \"$REPORT_HTML\"\n\ncat >> \"$REPORT_HTML\" \u003C\u003C 'HTML_MIDDLE'\n\u003C\u002Fp>\n\n\u003Cdiv class=\"section\">\n\u003Ch2>Case Details\u003C\u002Fh2>\nHTML_MIDDLE\n\n# Convert text to HTML paragraphs (simple approach)\nsed 's\u002F^\u002F\u003Cp>\u002F; s\u002F$\u002F\u003C\\\u002Fp>\u002F' \"$WORK_DIR\u002Fsource1.txt\" >> \"$REPORT_HTML\"\n\ncat >> \"$REPORT_HTML\" \u003C\u003C 'HTML_END'\n\u003C\u002Fdiv>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\nHTML_END\n\necho \"HTML report created: $REPORT_HTML\"\n```\n\n## Phase 5: Generate Final PDF Output\n\n### Option A: Using pandoc (Recommended if available)\n\n```bash\n# Markdown to PDF\npandoc \"$REPORT_MD\" -o final_report.pdf \\\n    --pdf-engine=xelatex \\\n    -V geometry:margin=1in\n\n# Or HTML to PDF\npandoc \"$REPORT_HTML\" -o final_report.pdf \\\n    --pdf-engine=webkit2png  # or wkhtmltopdf\n```\n\n### Option B: Using wkhtmltopdf (HTML to PDF)\n\n```bash\nwkhtmltopdf \\\n    --page-size A4 \\\n    --margin-top 20mm \\\n    --margin-bottom 20mm \\\n    --margin-left 15mm \\\n    --margin-right 15mm \\\n    \"$REPORT_HTML\" \\\n    final_report.pdf\n```\n\n### Option C: Using enscript + ps2pdf (Text to PDF)\n\n```bash\n# Text to PostScript, then to PDF\nenscript \\\n    --media=A4 \\\n    --font=Courier10 \\\n    --margins=20:20:20:20 \\\n    -o \"$WORK_DIR\u002Freport.ps\" \\\n    \"$REPORT_MD\"\n\n# PostScript to PDF\nps2pdf \"$WORK_DIR\u002Freport.ps\" final_report.pdf\n```\n\n### Option D: Merge Existing PDFs with pdftk\n\n```bash\n# If you have multiple PDFs to combine (not convert)\npdftk source1.pdf source2.pdf source3.pdf cat output combined_report.pdf\n\n# Add bookmarks\u002Foutline\npdftk source1.pdf dump_data > \"$WORK_DIR\u002Fbookmarks.txt\"\n# Edit bookmarks.txt, then:\npdftk source1.pdf update_info \"$WORK_DIR\u002Fbookmarks.txt\" output final_report.pdf\n```\n\n## Complete End-to-End Example\n\n```bash\n#!\u002Fbin\u002Fbash\n# pdf-report-generator.sh - Complete PDF report generation workflow\n\nset -e\n\n# Configuration\nSOURCE_PDFS=(\"case_guide.pdf\" \"case_summary.pdf\" \"test_results.pdf\")\nOUTPUT_PDF=\"new_case_report.pdf\"\nWORK_DIR=$(mktemp -d)\n\necho \"=== PDF Report Generation Workflow ===\"\necho \"Working directory: $WORK_DIR\"\n\n# Phase 1: Verify tools\necho -e \"\\n[Phase 1] Checking tools...\"\nfor tool in pdfinfo pdftotext; do\n    if ! command -v \"$tool\" &> \u002Fdev\u002Fnull; then\n        echo \"ERROR: $tool not found. Install poppler-utils.\"\n        exit 1\n    fi\ndone\n\n# Phase 2: Verify source PDFs\necho -e \"\\n[Phase 2] Verifying source PDFs...\"\nfor pdf in \"${SOURCE_PDFS[@]}\"; do\n    if [ ! -f \"$pdf\" ]; then\n        echo \"ERROR: Source PDF not found: $pdf\"\n        exit 1\n    fi\n    pages=$(pdfinfo \"$pdf\" | grep Pages | awk '{print $2}')\n    echo \"✓ $pdf: $pages pages\"\ndone\n\n# Phase 3: Extract content\necho -e \"\\n[Phase 3] Extracting content...\"\nfor i in \"${!SOURCE_PDFS[@]}\"; do\n    pdf=\"${SOURCE_PDFS[$i]}\"\n    base=$(basename \"$pdf\" .pdf)\n    pdftotext -layout \"$pdf\" \"$WORK_DIR\u002F${base}.txt\"\n    echo \"✓ Extracted: $pdf\"\ndone\n\n# Phase 4: Assemble report\necho -e \"\\n[Phase 4] Assembling report...\"\ncat > \"$WORK_DIR\u002Freport.md\" \u003C\u003C EOF\n# New Case Creation Report\n\n**Generated:** $(date '+%Y-%m-%d %H:%M:%S')\n\n---\n\nEOF\n\nsection_num=1\nfor pdf in \"${SOURCE_PDFS[@]}\"; do\n    base=$(basename \"$pdf\" .pdf)\n    cat >> \"$WORK_DIR\u002Freport.md\" \u003C\u003C EOF\n\n## Section $section_num: ${base\u002F\u002F_\u002F }\n\nEOF\n    cat \"$WORK_DIR\u002F${base}.txt\" >> \"$WORK_DIR\u002Freport.md\"\n    ((section_num++))\ndone\n\necho \"✓ Report assembled: $WORK_DIR\u002Freport.md\"\n\n# Phase 5: Generate PDF\necho -e \"\\n[Phase 5] Generating PDF...\"\nif command -v pandoc &> \u002Fdev\u002Fnull; then\n    pandoc \"$WORK_DIR\u002Freport.md\" -o \"$OUTPUT_PDF\" --pdf-engine=xelatex\n    echo \"✓ Generated with pandoc: $OUTPUT_PDF\"\nelif command -v enscript &> \u002Fdev\u002Fnull; then\n    enscript -o \"$WORK_DIR\u002Freport.ps\" \"$WORK_DIR\u002Freport.md\"\n    ps2pdf \"$WORK_DIR\u002Freport.ps\" \"$OUTPUT_PDF\"\n    echo \"✓ Generated with enscript+ps2pdf: $OUTPUT_PDF\"\nelse\n    echo \"⚠ No PDF generator available. Report saved as Markdown: $WORK_DIR\u002Freport.md\"\n    cp \"$WORK_DIR\u002Freport.md\" \".\u002F${OUTPUT_PDF%.pdf}.md\"\nfi\n\n# Cleanup\necho -e \"\\n[Cleanup]\"\necho \"Working files preserved in: $WORK_DIR\"\necho \"=== Complete ===\"\n```\n\n## Python Integration Example\n\n```python\nimport subprocess\nimport tempfile\nimport os\nfrom pathlib import Path\n\nclass PDFReportGenerator:\n    \"\"\"Complete PDF workflow: verify, extract, assemble, generate\"\"\"\n    \n    def __init__(self, work_dir=None):\n        self.work_dir = Path(work_dir) if work_dir else Path(tempfile.mkdtemp())\n        self.extracted_files = []\n    \n    def verify_pdf(self, pdf_path):\n        \"\"\"Verify PDF exists and get metadata\"\"\"\n        result = subprocess.run(\n            ['pdfinfo', str(pdf_path)],\n            capture_output=True, text=True\n        )\n        if result.returncode != 0:\n            raise ValueError(f\"Cannot read PDF: {pdf_path}\")\n        \n        metadata = {}\n        for line in result.stdout.split('\\n'):\n            if ':' in line:\n                key, val = line.split(':', 1)\n                metadata[key.strip()] = val.strip()\n        return metadata\n    \n    def extract_pdf(self, pdf_path, output_name=None):\n        \"\"\"Extract text from PDF\"\"\"\n        if output_name is None:\n            output_name = Path(pdf_path).stem + '.txt'\n        \n        output_path = self.work_dir \u002F output_name\n        subprocess.run(\n            ['pdftotext', '-layout', str(pdf_path), str(output_path)],\n            check=True\n        )\n        self.extracted_files.append(output_path)\n        return output_path.read_text()\n    \n    def assemble_report(self, sections, output_md=None):\n        \"\"\"Assemble extracted content into markdown report\"\"\"\n        if output_md is None:\n            output_md = self.work_dir \u002F 'report.md'\n        \n        with open(output_md, 'w') as f:\n            f.write(\"# Generated Report\\n\\n\")\n            f.write(f\"**Created:** {subprocess.check_output(['date']).decode().strip()}\\n\\n---\\n\\n\")\n            \n            for i, (title, content) in enumerate(sections, 1):\n                f.write(f\"## Section {i}: {title}\\n\\n{content}\\n\\n\")\n        \n        return output_md\n    \n    def generate_pdf(self, input_file, output_pdf=None):\n        \"\"\"Generate PDF from markdown or HTML\"\"\"\n        if output_pdf is None:\n            output_pdf = self.work_dir \u002F 'report.pdf'\n        \n        # Try pandoc first\n        if self._command_exists('pandoc'):\n            subprocess.run(\n                ['pandoc', str(input_file), '-o', str(output_pdf), '--pdf-engine=xelatex'],\n                check=True\n            )\n        # Fall back to enscript + ps2pdf\n        elif self._command_exists('enscript'):\n            ps_file = self.work_dir \u002F 'report.ps'\n            subprocess.run(['enscript', '-o', str(ps_file), str(input_file)], check=True)\n            subprocess.run(['ps2pdf', str(ps_file), str(output_pdf)], check=True)\n        else:\n            raise RuntimeError(\"No PDF generator available (need pandoc or enscript)\")\n        \n        return output_pdf\n    \n    def _command_exists(self, cmd):\n        return subprocess.run(['which', cmd], capture_output=True).returncode == 0\n    \n    def full_workflow(self, source_pdfs, output_pdf):\n        \"\"\"Complete workflow: verify → extract → assemble → generate\"\"\"\n        # Verify and extract\n        sections = []\n        for pdf in source_pdfs:\n            meta = self.verify_pdf(pdf)\n            content = self.extract_pdf(pdf)\n            sections.append((Path(pdf).stem, content))\n        \n        # Assemble and generate\n        report_md = self.assemble_report(sections)\n        self.generate_pdf(report_md, output_pdf)\n        \n        return Path(output_pdf)\n\n# Usage example\ngenerator = PDFReportGenerator()\nfinal_pdf = generator.full_workflow(\n    source_pdfs=['guide.pdf', 'summary.pdf', 'results.pdf'],\n    output_pdf='final_report.pdf'\n)\nprint(f\"Report generated: {final_pdf}\")\n```\n\n## Troubleshooting\n\n**No PDF generation tools available**\n- Minimum: Use `enscript` + `ps2pdf` (usually available with ghostscript)\n- Alternative: Generate HTML report and let browser print to PDF\n- Last resort: Output as Markdown for manual conversion\n\n**pdftotext returns empty\u002Fgarbled output**\n- PDF may be image-based (scanned) - requires OCR tools like `tesseract`\n- PDF may be encrypted - check with `pdfinfo` for \"Encrypted\" field\n- Try `pdftotext -layout` or `pdftotext -raw` for different extraction modes\n\n**pandoc fails with missing LaTeX**\n- Install minimal LaTeX: `apt-get install texlive-latex-base`\n- Or use `--pdf-engine=wkhtmltopdf` instead of xelatex\n- Or fall back to enscript method\n\n**Report formatting is poor**\n- Use HTML intermediate format for better styling control\n- Adjust pandoc variables: `-V geometry:margin=1in`\n- Add CSS when using wkhtmltopdf\n\n## Best Practices\n\n1. **Verify before processing** - Always check source PDFs exist and are readable before starting the workflow\n2. **Use temporary directories** - Keep intermediate files isolated with `mktemp -d`\n3. **Preserve extraction context** - Save extracted text with clear naming that maps back to sources\n4. **Choose generation tool wisely** - pandoc for best quality, enscript for minimum dependencies\n5. **Handle failures gracefully** - Check tool availability at runtime, provide fallbacks\n6. **Document the workflow** - Include generation timestamp and source list in output\n\n## Tool Comparison\n\n| Tool | Purpose | Pros | Cons |\n|------|---------|------|------|\n| pdfinfo | Metadata extraction | Fast, reliable | Metadata only |\n| pdftotext | Text extraction | Preserves structure | Struggles with complex layouts |\n| pandoc | Document conversion | Best quality, flexible | Requires LaTeX for PDF |\n| wkhtmltopdf | HTML→PDF | Great styling support | Larger dependency |\n| enscript | Text→PS | Minimal dependencies | Basic formatting |\n| pdftk | PDF manipulation | Powerful merging | Not for content generation |\n*** End Files\n","# PDF Report Generation with Command-Line Tools\n\nWhen working with PDFs in minimal\u002Fcontainerized environments where Python libraries may be unavailable, this skill provides a complete end-to-end workflow: verify source PDFs, extract content, assemble structured reports, and generate final PDF output.\n\n## When to Use This Skill\n\n- Need to create new PDF reports from existing PDF sources\n- Need to verify, extract, and combine PDF content\n- Working in environments without Python PDF libraries\n- Building document assembly pipelines in CI\u002FCD or containers\n\n## Complete Workflow Overview\n\n```\n[Source PDFs] → [Verify] → [Extract] → [Assemble] → [Generate Output PDF]\n```\n\n## Phase 1: Tool Availability Check\n\n```bash\n# Core extraction tools (from poppler-utils)\nwhich pdfinfo && echo \"✓ pdfinfo available\" || echo \"✗ pdfinfo missing\"\nwhich pdftotext && echo \"✓ pdftotext available\" || echo \"✗ pdftotext missing\"\n\n# PDF generation tools (choose based on availability)\nwhich pdftk && echo \"✓ pdftk available (PDF merging)\" || true\nwhich wkhtmltopdf && echo \"✓ wkhtmltopdf available (HTML→PDF)\" || true\nwhich pandoc && echo \"✓ pandoc available (document conversion)\" || true\nwhich enscript && echo \"✓ enscript available (text→PS)\" || true\nwhich ps2pdf && echo \"✓ ps2pdf available (PS→PDF)\" || true\n```\n\n### Install Required Tools\n\n```bash\n# Debian\u002FUbuntu - Core tools\napt-get update && apt-get install -y poppler-utils\n\n# Optional: PDF generation tools (choose based on needs)\napt-get install -y pdftk          # PDF merging\u002Fmanipulation\napt-get install -y wkhtmltopdf    # HTML to PDF\napt-get install -y pandoc         # Document conversion\napt-get install -y enscript ghostscript  # Text to PDF pipeline\n\n# RHEL\u002FCentOS\u002FFedora\nyum install -y poppler-utils pdftk ghostscript\n# or\ndnf install -y poppler-utils pdftk ghostscript\n\n# macOS (with Homebrew)\nbrew install poppler pdftk ghostscript\nbrew install --cask wkhtmltopdf  # GUI app, includes CLI\n```\n\n## Phase 2: Verify Source PDFs\n\n### Check Page Count and Metadata\n\n```bash\n# Verify each source PDF exists and is readable\nfor pdf in source1.pdf source2.pdf source3.pdf; do\n    if [ ! -f \"$pdf\" ]; then\n        echo \"ERROR: $pdf not found\"\n        exit 1\n    fi\n    \n    pages=$(pdfinfo \"$pdf\" | grep Pages | awk '{print $2}')\n    echo \"$pdf: $pages pages\"\ndone\n```\n\n### Validate Content Presence\n\n```bash\n# Check that required sections exist in source documents\nREQUIRED_TERMS=(\"checklist\" \"summary\" \"references\")\n\nfor term in \"${REQUIRED_TERMS[@]}\"; do\n    if pdftotext source.pdf - | grep -qi \"$term\"; then\n        echo \"✓ Found: $term\"\n    else\n        echo \"⚠ Missing: $term\"\n    fi\ndone\n```\n\n## Phase 3: Extract Content from Source PDFs\n\n### Extract Text to Temporary Files\n\n```bash\n# Create working directory\nWORK_DIR=$(mktemp -d)\necho \"Working directory: $WORK_DIR\"\n\n# Extract text from each source PDF\nfor i in source1.pdf source2.pdf source3.pdf; do\n    base=$(basename \"$i\" .pdf)\n    pdftotext -layout \"$i\" \"$WORK_DIR\u002F${base}.txt\"\n    echo \"Extracted: $i → ${base}.txt\"\ndone\n\n# Also extract metadata for reference\npdfinfo source1.pdf > \"$WORK_DIR\u002Fsource1_metadata.txt\"\n```\n\n### Extract Specific Sections (Optional)\n\n```bash\n# Extract only specific page ranges\npdftotext -f 1 -l 3 source1.pdf \"$WORK_DIR\u002Fsource1_pages1-3.txt\"\n\n# Extract and filter for specific content\npdftotext source1.pdf - | grep -A 10 \"Summary\" > \"$WORK_DIR\u002Fsummary_section.txt\"\n```\n\n## Phase 4: Assemble Report Content\n\n### Create Structured Report (Markdown Format)\n\n```bash\nREPORT_MD=\"$WORK_DIR\u002Freport.md\"\n\ncat > \"$REPORT_MD\" \u003C\u003C 'REPORT_HEADER'\n# New Case Creation Report\n\n**Generated:** $(date '+%Y-%m-%d %H:%M:%S')\n\n---\n\n## Executive Summary\n\nREPORT_HEADER\n\n# Add content from extracted sources\necho \"## Case Details\" >> \"$REPORT_MD\"\necho \"\" >> \"$REPORT_MD\"\ncat \"$WORK_DIR\u002Fsource1.txt\" >> \"$REPORT_MD\"\necho \"\" >> \"$REPORT_MD\"\n\necho \"## Supporting Documentation\" >> \"$REPORT_MD\"\necho \"\" >> \"$REPORT_MD\"\ncat \"$WORK_DIR\u002Fsource2.txt\" >> \"$REPORT_MD\"\necho \"\" >> \"$REPORT_MD\"\n\necho \"## References\" >> \"$REPORT_MD\"\necho \"\" >> \"$REPORT_MD\"\ncat \"$WORK_DIR\u002Fsource3.txt\" >> \"$REPORT_MD\"\n\necho \"Report assembled: $REPORT_MD\"\n```\n\n### Create HTML Report (Alternative for Better Formatting)\n\n```bash\nREPORT_HTML=\"$WORK_DIR\u002Freport.html\"\n\ncat > \"$REPORT_HTML\" \u003C\u003C 'HTML_HEADER'\n\u003C!DOCTYPE html>\n\u003Chtml>\n\u003Chead>\n    \u003Cmeta charset=\"UTF-8\">\n    \u003Ctitle>Case Creation Report\u003C\u002Ftitle>\n    \u003Cstyle>\n        body { font-family: Arial, sans-serif; margin: 40px; }\n        h1 { color: #333; border-bottom: 2px solid #333; }\n        h2 { color: #666; }\n        .section { margin: 20px 0; }\n        .metadata { font-size: 0.9em; color: #888; }\n    \u003C\u002Fstyle>\n\u003C\u002Fhead>\n\u003Cbody>\n\u003Ch1>New Case Creation Report\u003C\u002Fh1>\n\u003Cp class=\"metadata\">Generated: HTML_HEADER\n\ndate '+%Y-%m-%d %H:%M:%S' >> \"$REPORT_HTML\"\n\ncat >> \"$REPORT_HTML\" \u003C\u003C 'HTML_MIDDLE'\n\u003C\u002Fp>\n\n\u003Cdiv class=\"section\">\n\u003Ch2>Case Details\u003C\u002Fh2>\nHTML_MIDDLE\n\n# Convert text to HTML paragraphs (simple approach)\nsed 's\u002F^\u002F\u003Cp>\u002F; s\u002F$\u002F\u003C\\\u002Fp>\u002F' \"$WORK_DIR\u002Fsource1.txt\" >> \"$REPORT_HTML\"\n\ncat >> \"$REPORT_HTML\" \u003C\u003C 'HTML_END'\n\u003C\u002Fdiv>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\nHTML_END\n\necho \"HTML report created: $REPORT_HTML\"\n```\n\n## Phase 5: Generate Final PDF Output\n\n### Option A: Using pandoc (Recommended if available)\n\n```bash\n# Markdown to PDF\npandoc \"$REPORT_MD\" -o final_report.pdf \\\n    --pdf-engine=xelatex \\\n    -V geometry:margin=1in\n\n# Or HTML to PDF\npandoc \"$REPORT_HTML\" -o final_report.pdf \\\n    --pdf-engine=webkit2png  # or wkhtmltopdf\n```\n\n### Option B: Using wkhtmltopdf (HTML to PDF)\n\n```bash\nwkhtmltopdf \\\n    --page-size A4 \\\n    --margin-top 20mm \\\n    --margin-bottom 20mm \\\n    --margin-left 15mm \\\n    --margin-right 15mm \\\n    \"$REPORT_HTML\" \\\n    final_report.pdf\n```\n\n### Option C: Using enscript + ps2pdf (Text to PDF)\n\n```bash\n# Text to PostScript, then to PDF\nenscript \\\n    --media=A4 \\\n    --font=Courier10 \\\n    --margins=20:20:20:20 \\\n    -o \"$WORK_DIR\u002Freport.ps\" \\\n    \"$REPORT_MD\"\n\n# PostScript to PDF\nps2pdf \"$WORK_DIR\u002Freport.ps\" final_report.pdf\n```\n\n### Option D: Merge Existing PDFs with pdftk\n\n```bash\n# If you have multiple PDFs to combine (not convert)\npdftk source1.pdf source2.pdf source3.pdf cat output combined_report.pdf\n\n# Add bookmarks\u002Foutline\npdftk source1.pdf dump_data > \"$WORK_DIR\u002Fbookmarks.txt\"\n# Edit bookmarks.txt, then:\npdftk source1.pdf update_info \"$WORK_DIR\u002Fbookmarks.txt\" output final_report.pdf\n```\n\n## Complete End-to-End Example\n\n```bash\n#!\u002Fbin\u002Fbash\n# pdf-report-generator.sh - Complete PDF report generation workflow\n\nset -e\n\n# Configuration\nSOURCE_PDFS=(\"case_guide.pdf\" \"case_summary.pdf\" \"test_results.pdf\")\nOUTPUT_PDF=\"new_case_report.pdf\"\nWORK_DIR=$(mktemp -d)\n\necho \"=== PDF Report Generation Workflow ===\"\necho \"Working directory: $WORK_DIR\"\n\n# Phase 1: Verify tools\necho -e \"\\n[Phase 1] Checking tools...\"\nfor tool in pdfinfo pdftotext; do\n    if ! command -v \"$tool\" &> \u002Fdev\u002Fnull; then\n        echo \"ERROR: $tool not found. Install poppler-utils.\"\n        exit 1\n    fi\ndone\n\n# Phase 2: Verify source PDFs\necho -e \"\\n[Phase 2] Verifying source PDFs...\"\nfor pdf in \"${SOURCE_PDFS[@]}\"; do\n    if [ ! -f \"$pdf\" ]; then\n        echo \"ERROR: Source PDF not found: $pdf\"\n        exit 1\n    fi\n    pages=$(pdfinfo \"$pdf\" | grep Pages | awk '{print $2}')\n    echo \"✓ $pdf: $pages pages\"\ndone\n\n# Phase 3: Extract content\necho -e \"\\n[Phase 3] Extracting content...\"\nfor i in \"${!SOURCE_PDFS[@]}\"; do\n    pdf=\"${SOURCE_PDFS[$i]}\"\n    base=$(basename \"$pdf\" .pdf)\n    pdftotext -layout \"$pdf\" \"$WORK_DIR\u002F${base}.txt\"\n    echo \"✓ Extracted: $pdf\"\ndone\n\n# Phase 4: Assemble report\necho -e \"\\n[Phase 4] Assembling report...\"\ncat > \"$WORK_DIR\u002Freport.md\" \u003C\u003C EOF\n# New Case Creation Report\n\n**Generated:** $(date '+%Y-%m-%d %H:%M:%S')\n\n---\n\nEOF\n\nsection_num=1\nfor pdf in \"${SOURCE_PDFS[@]}\"; do\n    base=$(basename \"$pdf\" .pdf)\n    cat >> \"$WORK_DIR\u002Freport.md\" \u003C\u003C EOF\n\n## Section $section_num: ${base\u002F\u002F_\u002F }\n\nEOF\n    cat \"$WORK_DIR\u002F${base}.txt\" >> \"$WORK_DIR\u002Freport.md\"\n    ((section_num++))\ndone\n\necho \"✓ Report assembled: $WORK_DIR\u002Freport.md\"\n\n# Phase 5: Generate PDF\necho -e \"\\n[Phase 5] Generating PDF...\"\nif command -v pandoc &> \u002Fdev\u002Fnull; then\n    pandoc \"$WORK_DIR\u002Freport.md\" -o \"$OUTPUT_PDF\" --pdf-engine=xelatex\n    echo \"✓ Generated with pandoc: $OUTPUT_PDF\"\nelif command -v enscript &> \u002Fdev\u002Fnull; then\n    enscript -o \"$WORK_DIR\u002Freport.ps\" \"$WORK_DIR\u002Freport.md\"\n    ps2pdf \"$WORK_DIR\u002Freport.ps\" \"$OUTPUT_PDF\"\n    echo \"✓ Generated with enscript+ps2pdf: $OUTPUT_PDF\"\nelse\n    echo \"⚠ No PDF generator available. Report saved as Markdown: $WORK_DIR\u002Freport.md\"\n    cp \"$WORK_DIR\u002Freport.md\" \".\u002F${OUTPUT_PDF%.pdf}.md\"\nfi\n\n# Cleanup\necho -e \"\\n[Cleanup]\"\necho \"Working files preserved in: $WORK_DIR\"\necho \"=== Complete ===\"\n```\n\n## Python Integration Example\n\n```python\nimport subprocess\nimport tempfile\nimport os\nfrom pathlib import Path\n\nclass PDFReportGenerator:\n    \"\"\"Complete PDF workflow: verify, extract, assemble, generate\"\"\"\n    \n    def __init__(self, work_dir=None):\n        self.work_dir = Path(work_dir) if work_dir else Path(tempfile.mkdtemp())\n        self.extracted_files = []\n    \n    def verify_pdf(self, pdf_path):\n        \"\"\"Verify PDF exists and get metadata\"\"\"\n        result = subprocess.run(\n            ['pdfinfo', str(pdf_path)],\n            capture_output=True, text=True\n        )\n        if result.returncode != 0:\n            raise ValueError(f\"Cannot read PDF: {pdf_path}\")\n        \n        metadata = {}\n        for line in result.stdout.split('\\n'):\n            if ':' in line:\n                key, val = line.split(':', 1)\n                metadata[key.strip()] = val.strip()\n        return metadata\n    \n    def extract_pdf(self, pdf_path, output_name=None):\n        \"\"\"Extract text from PDF\"\"\"\n        if output_name is None:\n            output_name = Path(pdf_path).stem + '.txt'\n        \n        output_path = self.work_dir \u002F output_name\n        subprocess.run(\n            ['pdftotext', '-layout', str(pdf_path), str(output_path)],\n            check=True\n        )\n        self.extracted_files.append(output_path)\n        return output_path.read_text()\n    \n    def assemble_report(self, sections, output_md=None):\n        \"\"\"Assemble extracted content into markdown report\"\"\"\n        if output_md is None:\n            output_md = self.work_dir \u002F 'report.md'\n        \n        with open(output_md, 'w') as f:\n            f.write(\"# Generated Report\\n\\n\")\n            f.write(f\"**Created:** {subprocess.check_output(['date']).decode().strip()}\\n\\n---\\n\\n\")\n            \n            for i, (title, content) in enumerate(sections, 1):\n                f.write(f\"## Section {i}: {title}\\n\\n{content}\\n\\n\")\n        \n        return output_md\n    \n    def generate_pdf(self, input_file, output_pdf=None):\n        \"\"\"Generate PDF from markdown or HTML\"\"\"\n        if output_pdf is None:\n            output_pdf = self.work_dir \u002F 'report.pdf'\n        \n        # Try pandoc first\n        if self._command_exists('pandoc'):\n            subprocess.run(\n                ['pandoc', str(input_file), '-o', str(output_pdf), '--pdf-engine=xelatex'],\n                check=True\n            )\n        # Fall back to enscript + ps2pdf\n        elif self._command_exists('enscript'):\n            ps_file = self.work_dir \u002F 'report.ps'\n            subprocess.run(['enscript', '-o', str(ps_file), str(input_file)], check=True)\n            subprocess.run(['ps2pdf', str(ps_file), str(output_pdf)], check=True)\n        else:\n            raise RuntimeError(\"No PDF generator available (need pandoc or enscript)\")\n        \n        return output_pdf\n    \n    def _command_exists(self, cmd):\n        return subprocess.run(['which', cmd], capture_output=True).returncode == 0\n    \n    def full_workflow(self, source_pdfs, output_pdf):\n        \"\"\"Complete workflow: verify → extract → assemble → generate\"\"\"\n        # Verify and extract\n        sections = []\n        for pdf in source_pdfs:\n            meta = self.verify_pdf(pdf)\n            content = self.extract_pdf(pdf)\n            sections.append((Path(pdf).stem, content))\n        \n        # Assemble and generate\n        report_md = self.assemble_report(sections)\n        self.generate_pdf(report_md, output_pdf)\n        \n        return Path(output_pdf)\n\n# Usage example\ngenerator = PDFReportGenerator()\nfinal_pdf = generator.full_workflow(\n    source_pdfs=['guide.pdf', 'summary.pdf', 'results.pdf'],\n    output_pdf='final_report.pdf'\n)\nprint(f\"Report generated: {final_pdf}\")\n```\n\n## Troubleshooting\n\n**No PDF generation tools available**\n- Minimum: Use `enscript` + `ps2pdf` (usually available with ghostscript)\n- Alternative: Generate HTML report and let browser print to PDF\n- Last resort: Output as Markdown for manual conversion\n\n**pdftotext returns empty\u002Fgarbled output**\n- PDF may be image-based (scanned) - requires OCR tools like `tesseract`\n- PDF may be encrypted - check with `pdfinfo` for \"Encrypted\" field\n- Try `pdftotext -layout` or `pdftotext -raw` for different extraction modes\n\n**pandoc fails with missing LaTeX**\n- Install minimal LaTeX: `apt-get install texlive-latex-base`\n- Or use `--pdf-engine=wkhtmltopdf` instead of xelatex\n- Or fall back to enscript method\n\n**Report formatting is poor**\n- Use HTML intermediate format for better styling control\n- Adjust pandoc variables: `-V geometry:margin=1in`\n- Add CSS when using wkhtmltopdf\n\n## Best Practices\n\n1. **Verify before processing** - Always check source PDFs exist and are readable before starting the workflow\n2. **Use temporary directories** - Keep intermediate files isolated with `mktemp -d`\n3. **Preserve extraction context** - Save extracted text with clear naming that maps back to sources\n4. **Choose generation tool wisely** - pandoc for best quality, enscript for minimum dependencies\n5. **Handle failures gracefully** - Check tool availability at runtime, provide fallbacks\n6. **Document the workflow** - Include generation timestamp and source list in output\n\n## Tool Comparison\n\n| Tool | Purpose | Pros | Cons |\n|------|---------|------|------|\n| pdfinfo | Metadata extraction | Fast, reliable | Metadata only |\n| pdftotext | Text extraction | Preserves structure | Struggles with complex layouts |\n| pandoc | Document conversion | Best quality, flexible | Requires LaTeX for PDF |\n| wkhtmltopdf | HTML→PDF | Great styling support | Larger dependency |\n| enscript | Text→PS | Minimal dependencies | Basic formatting |\n| pdftk | PDF manipulation | Powerful merging | Not for content generation |\n*** End Files\n",{},"main","6b84b0689ca0d9e7b1e4061188ff6b60758e0f8a","openspace\u002Flegacy\u002Fv1\u002Fgdpval_bench\u002Fskills\u002Fpdf-verification-cli-enhanced",940,"https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot","https:\u002F\u002Fgithub.com\u002FHKUDS","https:\u002F\u002Fsites.google.com\u002Fview\u002Fchaoh","2026-11-06 23:04:01",{"source":7,"skillName":5,"commands":43,"prompt":53,"zipAvailable":54},[44,47,50],{"tool":45,"command":46},"npx","npx skills add HKUDS\u002FAgentPilot --skill pdf-verification-cli-enhanced",{"tool":48,"command":49},"bunx","bunx skills add HKUDS\u002FAgentPilot --skill pdf-verification-cli-enhanced",{"tool":51,"command":52},"pnpm","pnpm dlx skills add HKUDS\u002FAgentPilot --skill pdf-verification-cli-enhanced","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot\u002Fblob\u002Fmain\u002Fopenspace\u002Flegacy\u002Fv1\u002Fgdpval_bench\u002Fskills\u002Fpdf-verification-cli-enhanced\u002FSKILL.md",true,[56],{"path":57,"resourceType":58,"resourceTypeDesc":59,"sizeBytes":60,"sourceHtmlUrl":61},".skill_id","OTHER","其他",43,"https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot\u002Fblob\u002Fmain\u002Fopenspace\u002Flegacy\u002Fv1\u002Fgdpval_bench\u002Fskills\u002Fpdf-verification-cli-enhanced\u002F.skill_id",[63,83,102,125,149,172],{"skillNo":64,"name":65,"nameZh":6,"description":66,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":67,"categories":68,"keywords":69,"publishedAt":29},"01M6HYB297NDABRERJRVG0K8R0","agentpilot","Set up AgentPilot and use its single entry to prepare missing inputs and run configured RSI experiments. Use when the user requests AgentPilot installation or work with AgentPilot.","https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot\u002Fblob\u002Fmain\u002Fplugins\u002Fagentpilot\u002Fskills\u002Fagentpilot\u002FSKILL.md",[],[65,70,71,72,73,74,75,76,77,78,79,80,81,82],"prepare","requests","with","missing","installation","rsi","up","use","user","work","SET","run","experiments",{"skillNo":84,"name":85,"nameZh":6,"description":86,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":87,"categories":88,"keywords":89,"publishedAt":29},"01M6HYAYWXTTE1J7DJM0MFNQ4H","remember","Review auto-memory entries and propose promotions to OPENSPACE.md, OPENSPACE.local.md, or retained auto-memory topics; detect duplicates, stale facts, conflicts, and ambiguous entries before making changes.","https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot\u002Fblob\u002Fmain\u002Fopenspace\u002Fopenspace\u002Fskills\u002Fremember\u002FSKILL.md",[],[85,90,91,92,93,94,95,96,97,98,99,100,101],"memory","review","auto","openspace","changes","facts","detect","entries","local","conflicts","stale","duplicates",{"skillNo":103,"name":104,"nameZh":6,"description":105,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":106,"categories":107,"keywords":111,"publishedAt":29},"01M6HYAVMPM4SBDV60R2B5HPV8","skill-discovery","Search reusable OpenSpace skills locally, and browse cloud packages step by step when a cloud skill may help. Reusing proven skills saves tokens, improves reliability, and extends your capabilities beyond built-in tools.","https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot\u002Fblob\u002Fmain\u002Fopenspace\u002Fopenspace\u002Fhost_skills\u002Fskill-discovery\u002FSKILL.md",[108],{"categoryNo":109,"categoryName":110,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000023","Skill与智能体开发",[112,113,114,93,115,116,117,118,119,120,121,122,123,124],"skill discovery","discovery","reusable","step by step","tokens","packages","help","cloud","reliability","capabilities","browse","search","Tools",{"skillNo":126,"name":127,"nameZh":6,"description":128,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":129,"categories":130,"keywords":132,"publishedAt":29},"01M6HYAR811B3RB0M50RBEDGN6","delegate-task","Delegate tasks to OpenSpace — a full-stack autonomous worker for coding, DevOps, web research, and desktop automation, backed by an extensive MCP tool and skill library. Skills auto-improve through use, reducing token consumption over time. A cloud community lets agents share and collectively evolve reusable skills.","https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot\u002Fblob\u002Fmain\u002Fopenspace\u002Fopenspace\u002Fhost_skills\u002Fdelegate-task\u002FSKILL.md",[131],{"categoryNo":109,"categoryName":110,"taxonomyType":20,"taxonomyTypeDesc":21},[133,134,135,93,136,137,138,139,140,114,92,77,141,142,143,144,145,146,147,148],"delegate","task","delegate task","time","improve","desktop automation","worker","community","autonomous","tool","stack","for","share","tasks","automation","MCP",{"skillNo":150,"name":151,"nameZh":6,"description":152,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":153,"categories":154,"keywords":155,"publishedAt":29},"01M6HYAM9FSGV6E52AYENDQXYH","write-special-chars-ts-check","Workaround for write_file failures caused by special characters (apostrophes, backticks, template literals) using a Python heredoc script, plus a reliable TypeScript type-checking step that filters output to only the newly-written files to avoid being misled by pre-existing errors.","https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot\u002Fblob\u002Fmain\u002Fopenspace\u002Flegacy\u002Fv1\u002Fshowcase\u002Fskills\u002Fwrite-special-chars-ts-check\u002FSKILL.md",[],[156,157,158,159,160,161,162,163,164,165,166,167,168,169,144,170,171],"write special chars ts check","write","check","type","files","filters","only","template","file","python","typescript","output","Existing","type-checking","errors","script",{"skillNo":173,"name":174,"nameZh":6,"description":175,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":176,"categories":177,"keywords":178,"publishedAt":29},"01M6HYAH05HGV7SVWSVR8PJRQD","write-special-chars-readback","Workaround for write_file failures caused by special characters (apostrophes, backticks, template literals) using a Python heredoc script, with mandatory post-write verification via read_file and cleanup of \u002Ftmp helper scripts.","https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot\u002Fblob\u002Fmain\u002Fopenspace\u002Flegacy\u002Fv1\u002Fshowcase\u002Fskills\u002Fwrite-special-chars-readback\u002FSKILL.md",[],[179,180,157,72,144,171,181,182,183,184,185,163,164,25,186,165],"write special chars readback","readback","helper","post","via","scripts","read","cleanup"]