## New Skill: continue-claude-work (v1.1.0) - Recover actionable context from local `.claude` session artifacts - Compact-boundary-aware extraction (reads Claude's own compaction summaries) - Subagent workflow recovery (reports completed vs interrupted subagents) - Session end reason detection (clean exit, interrupted, error cascade, abandoned) - Size-adaptive strategy for small/large sessions - Noise filtering (skips 37-53% of session lines) - Self-session exclusion, stale index fallback, MEMORY.md integration - Bundled Python script (no external dependencies) - Security scan passed, argument-hint added ## Skill Updates - **skill-creator** (v1.5.0): Complete rewrite with evaluation framework - Added agents/ (analyzer, comparator, grader) - Added eval-viewer/ (generate_review.py, viewer.html) - Added scripts/ (run_eval, aggregate_benchmark, improve_description, run_loop) - Added references/schemas.md (eval/benchmark schemas) - Expanded SKILL.md with inline vs fork guidance, progressive disclosure patterns - Enhanced package_skill.py and quick_validate.py - **transcript-fixer** (v1.2.0): CLI improvements and test coverage - Enhanced argument_parser.py and commands.py - Added correction_service.py improvements - Added test_correction_service.py - **tunnel-doctor** (v1.4.0): Quick diagnostic script - Added scripts/quick_diagnose.py - Enhanced SKILL.md with 5-layer conflict model - **pdf-creator** (v1.1.0): Auto DYLD_LIBRARY_PATH + rendering fixes - Auto-detect and set DYLD_LIBRARY_PATH for weasyprint - Fixed list rendering and CSS improvements - **github-contributor** (v1.0.3): Enhanced project evaluation - Added evidence-loop, redaction, and merge-ready PR guidance ## Documentation - Updated marketplace.json (v1.38.0, 42 skills) - Updated CHANGELOG.md with v1.38.0 entry - Updated CLAUDE.md (skill count, marketplace version, #42 description) - Updated README.md (badges, skill section #42, use case, requirements) - Updated README.zh-CN.md (badges, skill section #42, use case, requirements) - Fixed absolute paths in continue-claude-work/references/file_structure.md ## Validation - All skills passed quick_validate.py - continue-claude-work passed security_scan.py - marketplace.json validated (valid JSON) - Cross-checked version consistency across all docs
222 lines
4.7 KiB
Python
222 lines
4.7 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Markdown to PDF converter with Chinese font support.
|
|
|
|
Converts markdown files to PDF using pandoc (markdown→HTML) + weasyprint (HTML→PDF).
|
|
Designed for formal documents (trademark filings, legal documents, reports).
|
|
|
|
Usage:
|
|
python md_to_pdf.py input.md output.pdf
|
|
python md_to_pdf.py input.md # outputs input.pdf
|
|
|
|
Requirements:
|
|
pip install weasyprint
|
|
pandoc (system install, e.g. brew install pandoc)
|
|
|
|
macOS environment setup (if needed):
|
|
export DYLD_LIBRARY_PATH="/opt/homebrew/lib:$DYLD_LIBRARY_PATH"
|
|
"""
|
|
|
|
import os
|
|
import platform
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
# Auto-configure library path on macOS ARM (Homebrew) — must be before weasyprint import
|
|
if platform.system() == 'Darwin':
|
|
_homebrew_lib = '/opt/homebrew/lib'
|
|
if Path(_homebrew_lib).is_dir():
|
|
_cur = os.environ.get('DYLD_LIBRARY_PATH', '')
|
|
if _homebrew_lib not in _cur:
|
|
os.environ['DYLD_LIBRARY_PATH'] = f"{_homebrew_lib}:{_cur}" if _cur else _homebrew_lib
|
|
|
|
from weasyprint import CSS, HTML
|
|
|
|
|
|
# CSS with Chinese font support
|
|
CSS_STYLES = """
|
|
@page {
|
|
size: A4;
|
|
margin: 2.5cm 2cm;
|
|
}
|
|
|
|
body {
|
|
font-family: 'Songti SC', 'SimSun', 'STSong', 'Noto Serif CJK SC', serif;
|
|
font-size: 12pt;
|
|
line-height: 1.8;
|
|
color: #000;
|
|
width: 100%;
|
|
}
|
|
|
|
h1 {
|
|
font-family: 'Heiti SC', 'SimHei', 'STHeiti', 'Noto Sans CJK SC', sans-serif;
|
|
font-size: 18pt;
|
|
font-weight: bold;
|
|
text-align: center;
|
|
margin-top: 0;
|
|
margin-bottom: 1.5em;
|
|
}
|
|
|
|
h2 {
|
|
font-family: 'Heiti SC', 'SimHei', 'STHeiti', 'Noto Sans CJK SC', sans-serif;
|
|
font-size: 14pt;
|
|
font-weight: bold;
|
|
margin-top: 1.5em;
|
|
margin-bottom: 0.8em;
|
|
}
|
|
|
|
h3 {
|
|
font-family: 'Heiti SC', 'SimHei', 'STHeiti', 'Noto Sans CJK SC', sans-serif;
|
|
font-size: 12pt;
|
|
font-weight: bold;
|
|
margin-top: 1em;
|
|
margin-bottom: 0.5em;
|
|
}
|
|
|
|
p {
|
|
margin: 0.8em 0;
|
|
text-align: justify;
|
|
}
|
|
|
|
ul, ol {
|
|
margin: 0.8em 0;
|
|
padding-left: 2em;
|
|
}
|
|
|
|
li {
|
|
margin: 0.4em 0;
|
|
}
|
|
|
|
table {
|
|
border-collapse: collapse;
|
|
width: 100%;
|
|
margin: 1em 0;
|
|
font-size: 10pt;
|
|
table-layout: fixed;
|
|
}
|
|
|
|
th, td {
|
|
border: 1px solid #666;
|
|
padding: 8px 6px;
|
|
text-align: left;
|
|
overflow-wrap: break-word;
|
|
word-break: normal;
|
|
}
|
|
|
|
th {
|
|
background-color: #f0f0f0;
|
|
font-weight: bold;
|
|
}
|
|
|
|
hr {
|
|
border: none;
|
|
border-top: 1px solid #ccc;
|
|
margin: 1.5em 0;
|
|
}
|
|
|
|
strong {
|
|
font-weight: bold;
|
|
}
|
|
|
|
code {
|
|
font-family: 'SF Mono', 'Monaco', 'Menlo', monospace;
|
|
font-size: 10pt;
|
|
background-color: #f5f5f5;
|
|
padding: 0.2em 0.4em;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
pre {
|
|
background-color: #f5f5f5;
|
|
padding: 1em;
|
|
overflow-x: auto;
|
|
font-size: 10pt;
|
|
line-height: 1.4;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
blockquote {
|
|
border-left: 3px solid #ccc;
|
|
margin: 1em 0;
|
|
padding-left: 1em;
|
|
color: #555;
|
|
}
|
|
"""
|
|
|
|
|
|
def _md_to_html(md_file: str) -> str:
|
|
"""Convert markdown to HTML using pandoc."""
|
|
if not shutil.which('pandoc'):
|
|
print("Error: pandoc not found. Install with: brew install pandoc", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
result = subprocess.run(
|
|
['pandoc', md_file, '-f', 'markdown', '-t', 'html'],
|
|
capture_output=True, text=True,
|
|
)
|
|
if result.returncode != 0:
|
|
print(f"Error: pandoc failed: {result.stderr}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
return result.stdout
|
|
|
|
|
|
def markdown_to_pdf(md_file: str, pdf_file: str | None = None) -> str:
|
|
"""
|
|
Convert markdown file to PDF with Chinese font support.
|
|
|
|
Args:
|
|
md_file: Path to input markdown file
|
|
pdf_file: Path to output PDF file (optional, defaults to same name as input)
|
|
|
|
Returns:
|
|
Path to generated PDF file
|
|
"""
|
|
md_path = Path(md_file)
|
|
|
|
if pdf_file is None:
|
|
pdf_file = str(md_path.with_suffix('.pdf'))
|
|
|
|
# Convert to HTML via pandoc
|
|
html_content = _md_to_html(md_file)
|
|
|
|
# Create full HTML document
|
|
full_html = f"""<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>{md_path.stem}</title>
|
|
</head>
|
|
<body>
|
|
{html_content}
|
|
</body>
|
|
</html>"""
|
|
|
|
# Generate PDF
|
|
HTML(string=full_html).write_pdf(pdf_file, stylesheets=[CSS(string=CSS_STYLES)])
|
|
|
|
return pdf_file
|
|
|
|
|
|
def main():
|
|
if len(sys.argv) < 2:
|
|
print("Usage: python md_to_pdf.py <input.md> [output.pdf]")
|
|
print("\nConverts markdown to PDF with Chinese font support.")
|
|
sys.exit(1)
|
|
|
|
md_file = sys.argv[1]
|
|
pdf_file = sys.argv[2] if len(sys.argv) > 2 else None
|
|
|
|
if not Path(md_file).exists():
|
|
print(f"Error: File not found: {md_file}")
|
|
sys.exit(1)
|
|
|
|
output = markdown_to_pdf(md_file, pdf_file)
|
|
print(f"Generated: {output}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|