← 返回 Skills 市场
39
总下载
1
收藏
0
当前安装
1
版本数
在 OpenClaw 中安装
/install code-to-images
功能描述
Convert code files to A4-ratio PNG/SVG images with line numbers and syntax highlighting, then merge to PDF
使用说明 (SKILL.md)
Code → A4 Images + PDF
Convert source code files into multi-page A4-ratio images (SVG+PNG) then merge into a single PDF, with:
- Line numbers (starting from 1)
- Syntax highlighting (keywords, registers, macros, numbers, comments)
- File name header with page info per sheet
- Light theme (white background)
- 50 lines per page, 14px Cascadia Code font
- Final output: one PDF per source file
Prerequisites
- Python 3 (tested with 3.14+)
- Node.js and
@resvg/resvg-jsfor SVG→PNG conversion - Python packages:
img2pdf
npm install -g @resvg/resvg-js
pip install img2pdf
Workflow
1. Get the source code
Ask the user to provide the source code file(s). Save them to the workspace.
2. Generate SVGs + PNGs + PDF (all-in-one)
Run the batch script below. It reads every source code file and for each one:
- Splits code into pages (50 lines/page)
- Generates SVG for each page (syntax-highlighted, light theme, A4 ratio)
- Renders each SVG to PNG via
@resvg/resvg-js - Merges all PNG pages into a single PDF via
img2pdf
Output structure:
filename.c/
├── code_page_1.svg
├── code_page_1.png
├── code_page_2.svg
├── code_page_2.png
└── ...
filename.c.pdf ← merged PDF
Python Batch Script
Save as gen_code_pdfs.py and run with python gen_code_pdfs.py:
"""Batch convert code files → images + PDF. One PDF per source file."""
import os, sys, subprocess, json
# === CONFIG ===
FILES = [
# List source files here, e.g.:
# 'main.c', 'key.c', 'KEY.h', 'adc.h', 'ADC.c',
# 'DS1302.c', 'DS1302.h', 'EEPROM.c', 'EEPROM.h',
]
BASE = os.getcwd()
LINES_PER_PAGE = 50
# Colors (light theme)
KW = {'void','char','int','u8','u16','u32','uchar','unsigned',
'for','if','else','while','switch','case','break','return',
'static','bit','sbit','xdata','idata','code','interrupt',
'do','default','continue','struct','typedef','enum','const',
'#ifndef','#define','#endif','#ifdef','extern'}
RG = {'P0','P1','P2','P3','P4','P5','P6','P7','RST','SCLK','IO','SCK'}
CO = {'keyword':'#d63384','register':'#e8590c','macro':'#099268',
'number':'#2b8a3e','string':'#099268','comment':'#868e96',
'var':'#1971c2','text':'#212529'}
BG='#ffffff'; HDR_BG='#f1f3f5'; HDR_BORDER='#dee2e6'
GT_BG='#f8f9fa'; GT_BORDER='#e9ecef'; LN_COLOR='#868e96'
def tokenize(line):
t,i,n=[],0,len(line)
while i\x3Cn:
if line[i]=='#':
j=i
while j\x3Cn and line[j]!='\
': j+=1
t.append((line[i:j],'macro')); break
if line[i:i+2]=='//': t.append((line[i:],'comment')); break
if line[i:i+2]=='/*':
j=i+2
while j\x3Cn and line[j:j+2]!='*/': j+=1
if j\x3Cn: j+=2
t.append((line[i:j],'comment')); break
if line[i]=='"':
j=i+1
while j\x3Cn and line[j]!='"':
if line[j]=='\\': j+=1
j+=1
if j\x3Cn: j+=1
t.append((line[i:j],'string')); i=j; continue
if line[i] in ' ':
j=i
while j\x3Cn and line[j] in ' ': j+=1
t.append((line[i:j],'space')); i=j; continue
if line[i].isdigit() or (line[i]=='0' and i+1\x3Cn and line[i+1] in 'xX'):
if line[i]=='0'and i+1\x3Cn and line[i+1]in'xX': j=i+2
else: j=i
while j\x3Cn and (line[j].isalnum() or line[j] in '.xXa-fA-F'): j+=1
t.append((line[i:j],'number')); i=j; continue
if line[i].isalpha() or line[i]=='_':
j=i
while j\x3Cn and (line[j].isalnum() or line[j]=='_'): j+=1
w=line[i:j]
if w in KW: t.append((w,'keyword'))
elif w in RG: t.append((w,'register'))
else: t.append((w,'text'))
i=j; continue
t.append((line[i],'text')); i+=1
if not t: t.append(('','space'))
return t
def gen_svg_page(pg_lines, start_ln, page_num, total_pages, total_lines, fname):
lh=24; fs=14; cw=8.4; gutter=56; xc=gutter+16; hh=60; mt=2; mb=4; mw=794
max_l=max(len(l) for l in pg_lines) if pg_lines else 10
w=max(mw, xc+max_l*cw+30)
h=hh+len(pg_lines)*lh+mt+mb+20
out=[]
out.append(f'\x3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {w} {h}">')
out.append(f'\x3Cstyle>text{{font-family:"Cascadia Code","Fira Code","JetBrains Mono","Consolas","Courier New",monospace;font-size:{fs}px;}}\x3C/style>')
out.append(f'\x3Crect width="{w}" height="{h}" fill="{BG}"/>')
out.append(f'\x3Crect x="0" y="0" width="{w}" height="{hh}" fill="{HDR_BG}"/>')
out.append(f'\x3Cline x1="0" y1="{hh}" x2="{w}" y2="{hh}" stroke="{HDR_BORDER}" stroke-width="1.5"/>')
out.append(f'\x3Ctext x="20" y="24" fill="#343a40" font-size="16" font-family="Arial,Helvetica,sans-serif" font-weight="700">📄 {fname}\x3C/text>')
out.append(f'\x3Ctext x="20" y="48" fill="#868e96" font-size="12" font-family="Arial,Helvetica,sans-serif">第 {page_num} 页 · 共 {total_pages} 页 · {total_lines} 行\x3C/text>')
cy=hh+mt
out.append(f'\x3Crect x="0" y="{hh}" width="{xc-8}" height="{h-hh}" fill="{GT_BG}"/>')
out.append(f'\x3Cline x1="{xc-8}" y1="{hh}" x2="{xc-8}" y2="{h}" stroke="{GT_BORDER}" stroke-width="1"/>')
for idx,line in enumerate(pg_lines):
y=cy+idx*lh; ln=start_ln+idx
out.append(f'\x3Ctext x="{xc-14}" y="{y+lh//2+4}" fill="{LN_COLOR}" text-anchor="end" font-size="12">{ln}\x3C/text>')
if not line.strip(): continue
toks=tokenize(line); x=xc
for val,typ in toks:
if typ=='space': x+=(val.count(' ')+val.count(' ')*4)*cw
else:
c=CO.get(typ,'#212529'); e=val.replace('&','&').replace('\x3C','<').replace('>','>')
out.append(f'\x3Ctext x="{x:.1f}" y="{y+lh//2+4}" fill="{c}">{e}\x3C/text>')
x+=len(val)*cw
out.append(f'\x3Ctext x="{w//2}" y="{h-8}" text-anchor="middle" fill="#adb5bd" font-size="10" font-family="Arial,Helvetica,sans-serif">- {page_num} -\x3C/text>')
out.append('\x3C/svg>')
return '\
'.join(out)
# === Main loop ===
for fname in FILES:
src = os.path.join(BASE, fname)
if not os.path.exists(src):
print(f'SKIP {fname}'); continue
with open(src, 'r', encoding='utf-8') as f:
lines = f.readlines()
total = len(lines)
np = (total + LINES_PER_PAGE - 1) // LINES_PER_PAGE
out_dir = os.path.join(BASE, fname + '_images')
os.makedirs(out_dir, exist_ok=True)
# Generate SVGs
for p in range(1, np + 1):
s = (p-1)*LINES_PER_PAGE; e = min(s+LINES_PER_PAGE, total)
svg = gen_svg_page(lines[s:e], s+1, p, np, total, fname)
with open(os.path.join(out_dir, f'code_page_{p}.svg'), 'w', encoding='utf-8') as f:
f.write(svg)
print(f'{fname}: {total} lines, {np} pages -> SVG OK')
# Convert SVGs to PNGs via node + @resvg/resvg-js
js = (
'const fs=require("fs");const{Resvg}=require("@resvg/resvg-js");'
'const dir=' + json.dumps(out_dir.replace('\\','\\\\')) + ';'
'for(let p=1;p\x3C=' + str(np) + ';p++){'
'const s=dir+"\\\\code_page_"+p+".svg";'
'const pn=dir+"\\\\code_page_"+p+".png";'
'try{const d=fs.readFileSync(s,"utf8");const r=new Resvg(d,{background:"#ffffff"});'
'const b=r.render();fs.writeFileSync(pn,b.asPng())}'
'catch(e){console.log(" ERR:"+p+" "+e.message)}}'
)
subprocess.run(['node','-e',js], check=True)
print(f'{fname}: PNG OK')
# Merge PNGs into a single PDF via img2pdf
import img2pdf
png_files = sorted(
[os.path.join(out_dir, f) for f in os.listdir(out_dir) if f.endswith('.png')],
key=lambda x: int(os.path.basename(x).replace('code_page_','').replace('.png',''))
)
pdf_path = os.path.join(BASE, fname + '.pdf')
with open(pdf_path, 'wb') as f:
f.write(img2pdf.convert(png_files))
kb = os.path.getsize(pdf_path) // 1024
print(f'{fname}: PDF -> {pdf_path} ({kb}KB)')
print('\
=== All done! ===')
Customization
| Parameter | Default | Description |
|---|---|---|
LINES_PER_PAGE |
50 | Lines per page |
font_size |
14 | Base font size (px) |
line_height |
24 | Line spacing (px) |
COLOR_* |
— | Tweak colors for theme |
REGS |
P0..P7 |
Register/pin names to highlight |
Tips
- Always unpack tokens as
for val, typ in tokens(nottt, tv) —val= code text,typ= type name for color lookup - SVG viewBox keeps content at 1x; resvg renders at native A4 resolution
- If node PNG conversion is slow, break into smaller batches
- Keep the
_images/folders (SVG+PNG) for later editing; the PDF is the deliverable
安全使用建议
Install only if you are comfortable running local Python and Node tooling. Prefer a virtual environment or project-local npm install, review the generated script before running it, and only add source files you intend to convert.
能力评估
Purpose & Capability
The stated purpose, generated script, and outputs all align around rendering source files into SVG/PNG pages and merging them into PDFs.
Instruction Scope
Runtime instructions are limited to user-provided source files listed in the script and workspace-local output generation; no hidden network use, credential access, or unrelated actions were found.
Install Mechanism
The skill tells users to install @resvg/resvg-js globally with npm and img2pdf with pip, which is disclosed but not version-pinned or isolated.
Credentials
Reading code files and writing image/PDF artifacts in the workspace is proportionate to the conversion task.
Persistence & Privilege
The only persistence is generated output folders and PDF files; there is no background worker, startup hook, privilege escalation, or credential/session handling.
如何使用
- 确保已安装 OpenClaw(本地或 Docker 部署)
- 在对话框中输入安装命令:
/install code-to-images - 安装完成后,直接呼叫该 Skill 的名称或使用
/code-to-images触发 - 根据 Skill 的参数说明提供必要输入,即可获得结构化输出
版本历史
v1.0.0
- Initial release of "code-to-images" skill.
- Converts code files to syntax-highlighted, line-numbered A4-ratio images (SVG and PNG) with light theme.
- Generates one multipage PDF per source file, each page showing 50 lines of code in 14px Cascadia Code font.
- Output includes file name headers, per-page info, whitespace handling, and preserves code formatting.
- Requires Python 3, Node.js, @resvg/resvg-js, and img2pdf; usage and workflow detailed in SKILL.md.
元数据
常见问题
Code To Images 是什么?
Convert code files to A4-ratio PNG/SVG images with line numbers and syntax highlighting, then merge to PDF. 它是一个面向 Claude Code / OpenClaw 的 AI Agent Skill 插件,目前累计下载 39 次。
如何安装 Code To Images?
在 OpenClaw 或 Claude Code 对话框中运行命令「/install code-to-images」即可一键安装,无需额外配置。
Code To Images 是免费的吗?
是的,Code To Images 完全免费,采用 MIT-0 许可证,可自由下载、安装和使用。
Code To Images 支持哪些平台?
Code To Images 跨平台运行,可在任意部署了 OpenClaw / Claude Code 的环境中使用(cross-platform)。
谁开发了 Code To Images?
由 yon-gjun(@yon-gjun)开发并维护,当前版本 v1.0.0。
推荐 Skills