html-to-images — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited html-to-images (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Export any single-file HTML slide deck as individual PNG images. Each slide becomes its own file: slide-01.png, slide-02.png, etc.
fullPage: true) → one tall PNGNo PPTX, no python-pptx. Just clean image files ready for anywhere.
| Tool | Install |
|---|---|
| Node.js | https://nodejs.org |
| Python 3 | https://python.org |
| Puppeteer | npm install puppeteer (auto-handled) |
| Pillow | pip install pillow |
| Chrome or Edge | Already installed on most systems |
You need:
slides/ subfolder next to the HTML)Auto-detect Chrome from these paths (check in order):
C:/Program Files/Google/Chrome/Application/chrome.exeC:/Program Files (x86)/Google/Chrome/Application/chrome.exeC:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe/usr/bin/google-chrome/usr/bin/chromium-browsernode -e "require('puppeteer')" 2>&1
npm install puppeteer --save-devWrite _screenshot.mjs to the same folder as the HTML:
import puppeteer from 'puppeteer';
import path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const HTML_PATH = path.join(__dirname, '<HTML_FILENAME>');
const W = <WIDTH>; // e.g. 1920
const H = <HEIGHT>; // e.g. 1080
const browser = await puppeteer.launch({
executablePath: '<CHROME_PATH>',
headless: true,
args: ['--no-sandbox', `--window-size=${W},${H}`],
});
const page = await browser.newPage();
await page.setViewport({ width: W, height: H, deviceScaleFactor: 1 });
await page.goto(`file:///${HTML_PATH.replace(/\\/g, '/')}`, {
waitUntil: 'networkidle0',
timeout: 30000,
});
// Wait for fonts and transitions to settle
await new Promise(r => setTimeout(r, 2500));
// Freeze animations, hide fixed UI chrome
await page.addStyleTag({ content: `
* { animation: none !important; transition: none !important; }
body::after { display: none !important; }
#nav, #progress, nav, header[data-fixed] { display: none !important; }
` });
await page.screenshot({
path: path.join(__dirname, '_fullpage.png'),
fullPage: true,
});
console.log('Full page captured.');
await browser.close();Write _slice_images.py to the same folder:
from PIL import Image
import os
FOLDER = r'<FOLDER_PATH>'
FULLPAGE = os.path.join(FOLDER, '_fullpage.png')
OUT_DIR = os.path.join(FOLDER, '<OUTPUT_SUBFOLDER>') # e.g. 'slides'
SLIDES = <SLIDE_COUNT>
FORMAT = '<FORMAT>' # 'PNG' or 'JPEG'
EXT = 'jpg' if FORMAT == 'JPEG' else 'png'
os.makedirs(OUT_DIR, exist_ok=True)
img = Image.open(FULLPAGE)
W, H = img.size
slide_h = H // SLIDES
print(f"Full image: {W}x{H}, {SLIDES} slides, {slide_h}px each")
print(f"Saving to: {OUT_DIR}")
for i in range(SLIDES):
top = i * slide_h
crop = img.crop((0, top, W, top + slide_h))
out_path = os.path.join(OUT_DIR, f'slide-{i+1:02d}.{EXT}')
if FORMAT == 'JPEG':
crop = crop.convert('RGB') # JPEG does not support transparency
crop.save(out_path, format=FORMAT, quality=95)
else:
crop.save(out_path, format=FORMAT)
print(f" Saved slide-{i+1:02d}.{EXT} (rows {top}-{top+slide_h})")
print(f"\nDone. {SLIDES} images saved to {OUT_DIR}/")node _screenshot.mjs
python _slice_images.pyAfter confirming the images look correct, delete:
_fullpage.png_screenshot.mjs_slice_images.pyThe output images in the slides/ folder are kept.
slides/
├── slide-01.png
├── slide-02.png
├── slide-03.png
...
└── slide-10.png| Problem | Cause | Fix |
|---|---|---|
| All images identical | Using scroll + viewport clip | Always use fullPage: true + slice — never scroll |
| Slide count wrong | Slide height inconsistency in HTML | Ensure every slide is exactly 100vh |
| JPEG has black background | Transparency not supported by JPEG | Skill auto-converts to RGB before saving |
| Chrome not found | Wrong exe path | Check Edge as fallback |
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.