Convert raw HTML pasted from a resume builder's browser inspector (Tailwind arbitrary-value classes, data-editable-text spans, resume-*-action buttons, SVG icon cruft) into clean semantic HTML/CSS and render it to a single-page PDF. Trigger when the user pastes an HTML dump containing patterns like data-editable-text, resume-section-, resume-action-button, or resume-inline-entry-action and asks for a resume/PDF.
Scanned 9/6/2026
Install to Claude Code
npx -y skills add ankitdey01/free-resume-export --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of free-resume-export?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/ankitdey01-free-resume-export)More formats (shields.io, HTML) on the badges page.
---
name: free-resume-export
description: Convert raw HTML pasted from a resume builder's browser inspector (Tailwind arbitrary-value classes, data-editable-text spans, resume-*-action buttons, SVG icon cruft) into clean semantic HTML/CSS and render it to a single-page PDF. Trigger when the user pastes an HTML dump containing patterns like data-editable-text, resume-section-, resume-action-button, or resume-inline-entry-action and asks for a resume/PDF.
---
# Free Resume Export
Converts messy Tailwind + interactive-editor HTML dump (copied via "Inspect Element" from resume-builder web app) into a clean, static, single-page resume PDF.
---
## How to Use This Skill
### Example User Requests
**Direct file reference:**
```
Convert #sample-resume.html to PDF
Make a PDF from #my-resume.html
Clean up #resume-raw.html and generate PDF
```
**Skill name mention:**
```
Use free-resume-export on #my-file.html
Run the free-resume-export skill on #resume.html
Apply free-resume-export to #downloaded-resume.html
```
**Contextual request:**
```
I pasted HTML from Polishme.ai inspector into #resume.html, convert it to PDF
Turn this messy resume HTML (#file.html) into a clean PDF
```
**⚠️ IMPORTANT: User MUST provide an HTML file reference (via #filename or file path)**
### Pre-Execution Validation
**Before starting the conversion, you MUST validate the HTML file:**
1. **Check file exists and is readable**
2. **Verify it's actually resume HTML** by checking for at least 2 of these patterns:
- Contains `data-editable-text` attributes
- Contains resume-specific classes: `resume-section`, `resume-entry`, `resume-action-button`, `resume-inline-entry-action`
- Contains common resume section markers: Education, Experience, Skills, Projects (case-insensitive)
- Contains contact info patterns: email, phone, LinkedIn, GitHub links
- Contains Tailwind arbitrary-value classes: `text-[`, `mb-[`, `gap-[`, `w-[`
3. **Minimum content check:**
- File size > 5KB (too small = likely not a full resume)
- Contains at least one heading tag (`<h1>`, `<h2>`, or `<h3>`)
### If Validation Fails
Stop immediately and display:
```
❌ This doesn't look like a resume HTML file.
This skill is designed for HTML copied from resume builder tools like Polishme.ai.
📋 To use this skill:
1. 🌐 Open your resume in the resume builder (like polishme.ai)
2. Select a template and build your new Resume.
3. 🖱️ Right-click → Inspect Element
4. 📄 Copy the HTML content
5. 💾 Paste into a .html file
6. ✨ Reference that file when calling this skill
Example: "Convert #my-resume.html to PDF"
```
### If Validation Passes
**DO NOT display validation details to the user.** Simply proceed with the execution protocol below.
---
## Execution Protocol
**ALWAYS start by displaying the full execution plan to the user:**
```
🚀 Starting Resume HTML → PDF Conversion
📋 Plan:
0/5 ✅ Initialize process
1/5 ⏳ Format raw HTML
2/5 ⏳ Extract content model
3/5 ⏳ Build clean semantic HTML
4/5 ⏳ Render to PDF via headless Chrome
5/5 ⏳ Verify single-page output
Beginning execution...
```
After displaying the plan, execute each step and update the user with:
- Current step number and description
- What you're doing
- Result/status
**Format:** `{step}/{total} ✅ {Step Name} - {brief status message}`
**Example during execution:**
```
1/5 ✅ Format raw HTML - Formatted 15,234 chars with proper line breaks
2/5 ⏳ Extract content model - Reading sections...
```
**At the end, show final summary:**
```
✅ All steps completed successfully
Resume PDF created at: {path}
File size: {size}
```
---
## Input Recognition Patterns
The pasted markup usually contains these signals:
- **Tailwind arbitrary-value classes:** `text-[15px]`, `mb-[2px]`, `gap-[3px]`
- **Editor scaffolding:** `data-editable-text="true"`, `data-placeholder="..."`, `contenteditable`
- **Action buttons/icons to discard:** classes containing `resume-action-button`, `resume-section-title-action`, `resume-inline-entry-action`, `resume-entry-reorder-action`, plus their inline `<svg>` icons and `+ new` / `+ bullet` / `delete section` labels
- **Section order:** `style="order: N;"` — this N is the **real visual section order**, which can differ from DOM order. Always sort sections by this before rebuilding
- **Empty placeholders:** empty `data-placeholder="Location"` spans with no text mean the field was left blank; drop the element and its adjacent separator
---
## STEP 1: Format Raw HTML
**Display to user:**
```
1/5 ⏳ Format raw HTML
```
### Check if formatting is needed
Check if any line in the file exceeds 10,000 characters (indicates content on a single line):
```powershell
python -c "f=open(r'<path>', 'r', encoding='utf-8'); lines=f.readlines(); f.close(); has_long_line='yes' if any(len(line) > 10000 for line in lines) else 'no'; print(has_long_line)"
```
**If output is "yes"**, the file has content on giant lines and needs formatting.
### Action: Format the file
Use regex to add line breaks between HTML tags. **Important:** If the command fails due to line-length issues in PowerShell, use the file-based approach instead.
**Method 1 - Direct execution (try this first):**
```powershell
python -c "import re; f=open(r'<path>', 'r', encoding='utf-8'); html=f.read(); f.close(); formatted=re.sub(r'>\s*<', '>\n<', html); f2=open(r'<path>', 'w', encoding='utf-8'); f2.write(formatted); f2.close(); print('Formatted')"
```
**Method 2 - File-based (use if Method 1 fails):**
Create a temporary Python script and execute it:
```python
# Save as format_html.py
import re
import sys
html_path = sys.argv[1]
with open(html_path, 'r', encoding='utf-8') as f:
html = f.read()
formatted = re.sub(r'>\s*<', '>\n<', html)
with open(html_path, 'w', encoding='utf-8') as f:
f.write(formatted)
print('Formatted')
```
Then execute:
```powershell
python format_html.py "<path>"
```
After running either method, verify formatting worked:
```powershell
python -c "f=open(r'<path>', 'r', encoding='utf-8'); lines=f.readlines(); f.close(); long_lines = [i for i,line in enumerate(lines) if len(line) > 10000]; print('Formatting successful' if not long_lines else f'Still has {len(long_lines)} long lines')"
```
**CRITICAL:** Always run the formatting step if long lines are detected. Resume HTML from browser inspector typically has the `<body>` content on one giant line even if the `<head>` section is formatted.
### If formatting script fails or content is still on one line
If you detect the file is still on one giant line after running the formatting script, or if content extraction is failing:
**Ask the user:**
```
⚠️ The HTML file may not have formatted correctly.
🔧 Please manually format the file:
1. 📂 Open sample-raw.html in VS Code
2. ⌨️ Press Alt+Shift+F (or right-click → Format Document)
3. 💾 Save the file
4. ✅ Let me know when done, and I'll continue
This ensures all resume content is properly extracted.
```
Wait for user confirmation before proceeding to Step 2.
**Cleanup:** If you created a temporary `format_html.py` script, delete it after successful formatting.
---
## When to Show Contact Link
**Show the contact support link when:**
- Validation fails repeatedly
- Formatting script encounters errors
- PDF generation fails after multiple attempts
- Browser not found errors
- User reports bugs or unexpected behavior
- Any error that you cannot resolve automatically
**Format:**
```
Need help? Contact support: https://www.ankit.systems/#contact
```
or
```
Still having issues? Contact support: https://www.ankit.systems/#contact
```
---
### Update User
```
1/5 ✅ Format raw HTML
```
**Never say "already formatted, skipping" or explain technical details about line counts.**
---
## STEP 2: Extract Content Model
**Display to user:**
```
2/5 ⏳ Extract content model
```
### Action
Use `read_file` to read the formatted HTML directly into context, then manually extract the visible text content by parsing the HTML structure.
**Practical approach:**
1. **Read the entire formatted HTML** with `read_file` tool
2. **Visually identify and extract** all the actual resume text you can see in the HTML:
- Name from the `<h1>` with `data-editable-text`
- Contact info from links and spans in the header
- Section titles from `<h3>` elements (text inside `data-editable-text` spans)
- Entry titles, companies, dates from entry headers
- Bullet points from `<li>` elements with `data-editable-text`
3. **Build the clean HTML directly** using the extracted text - don't create intermediate data structures or parser scripts
**DO NOT:**
- Create separate Python scripts or parsers
- Use BeautifulSoup or lxml
- Write extraction logic to files
**DO:**
- Read the HTML file with `read_file`
- Manually extract visible text from the readable HTML structure
- Directly create the clean Resume.html with the extracted content
### Extraction Rules
- Extract text from `data-editable-text` spans (the real resume content)
- Sort sections by their `order: N` value (not DOM order)
- Ignore classes containing: `resume-action-button`, `resume-section-title-action`, `resume-inline-entry-action`, `resume-entry-reorder-action`
- Ignore `<svg>` elements
- Ignore empty `data-placeholder` spans
### Update User
```
2/5 ✅ Extract content model
```
**Do not display section counts, names, or entry details. Keep it simple.**
---
## STEP 3: Build Clean Semantic HTML
**Display to user:**
```
3/5 ⏳ Build clean semantic HTML
```
### Action
Use the extracted content model to build a clean HTML file with embedded CSS.
Use this proven baseline structure (validated against real resume-builder exports):
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{Name}} - Resume</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: "Times New Roman", Times, "Liberation Serif", "Nimbus Roman", Georgia, serif;
line-height: 1.25;
color: #000;
background: #f5f5f5;
padding: 20px;
}
.container {
max-width: 794px;
margin: 0 auto;
background: white;
padding: 36px 50px;
box-shadow: 0 2px 16px rgba(0,0,0,0.07), 0 0 0 1px rgba(0,0,0,0.04);
border-radius: 10px;
}
.header { text-align: center; margin-bottom: 10px; }
h1 {
font-size: 28px;
font-weight: 700;
letter-spacing: 0.01em;
font-variant: small-caps;
margin-bottom: 2px;
line-height: 1.05;
}
.contact-info { font-size: 14px; line-height: 1.25; margin-top: 2px; }
.contact-info a { color: #000; text-decoration: underline; text-decoration-offset: 2px; }
.contact-info a:hover { opacity: 0.7; }
.contact-info span { margin: 0 5px; }
h2 {
font-size: 18px;
font-weight: 400;
font-variant: small-caps;
letter-spacing: 0.04em;
margin: 10px 0 2px 0;
line-height: 1.1;
padding-bottom: 2px;
border-bottom: 1px solid #000;
}
.section { margin-bottom: 10px; }
.section-content { padding-left: 12px; }
.entry { margin-bottom: 3px; }
.entry-header { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; flex-wrap: wrap; margin-bottom: 2px; }
.entry-title { font-size: 15px; font-weight: 700; color: #000; }
.entry-subtitle { font-size: 14px; font-style: italic; color: #000; }
.entry-meta { font-size: 14px; color: #000; white-space: nowrap; flex-shrink: 0; }
.entry-company { font-size: 14px; font-style: italic; color: #000; }
.tools { font-size: 15px; font-style: italic; color: #000; margin: 0 5px; }
.tools-link { font-size: 15px; color: #000; text-decoration: none; cursor: pointer; border: none; background: none; padding: 0; font-family: inherit; margin-left: 5px; }
.tools-link:hover { opacity: 0.7; }
ul { list-style: none; display: flex; flex-direction: column; gap: 3px; }
li { font-size: 13px; color: #3f3f46; padding-left: 14px; position: relative; line-height: 1.55; }
li:before { content: "•"; position: absolute; left: 0; }
.skill-group { margin-bottom: 2px; display: flex; flex-wrap: wrap; gap: 0; }
.skill-label { font-weight: 700; font-size: 14px; margin-right: 5px; }
.skill-value { font-size: 14px; color: #000; flex: 1; }
.project-header { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0; }
.project-name { font-weight: 700; font-size: 15px; margin-right: 5px; }
.separator { margin: 0 5px; font-weight: 400; }
@media (max-width: 760px) {
.container { padding: 24px 18px; }
h1 { font-size: 24px; }
.contact-info { font-size: 13px; }
}
/* Print / PDF export */
@page { size: Letter; margin: 0; }
@media print {
body { background: white; padding: 0; }
.container {
max-width: 100%; width: 100%; margin: 0;
padding: 0.35in 0.6in;
box-shadow: none; border-radius: 0;
}
h2 { break-after: avoid; }
.entry { break-inside: avoid; }
li { line-height: 1.45; }
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>{{Name}}</h1>
<div class="contact-info">
<!-- phone | email | links | location -->
</div>
</div>
<!-- one .section per resume section, in order: N sequence -->
</div>
</body>
</html>
```
### Important Notes
- Section order in output HTML must follow the `order: N` values from source, not DOM order
- For entries with no date, omit `.entry-meta` rather than rendering empty span
- Bullet color `#3f3f46` and body text `#000` are intentional (two-tone hierarchy)
### Update User
```
3/5 ✅ Build clean semantic HTML
```
### Action: Open HTML in Browser
Open the generated HTML file in the default browser for user preview:
```powershell
Start-Process "{path_to_Resume.html}"
```
### Ask User for Confirmation
Display this message and wait for user response:
```
✨ Resume HTML Generated Successfully
The clean resume HTML is now open in your browser.
📍 Location: {full_path_to_Resume.html}
💡 Options:
1. 🤖 I can generate a PDF for you automatically (continues to Step 4)
2. 🖨️ You can manually print to PDF via browser (Ctrl+P → Save as PDF) [Recommended, saves your tokens]
Would you like me to generate the PDF automatically?
```
**If user says YES/continue/proceed:**
- Continue to Step 4 (automatic PDF generation)
**If user says NO/manual/browser:**
- Skip to final summary showing only HTML location
- Display:
```
✅ Process Complete
📍 Clean HTML Location: {full_path_to_Resume.html}
🖨️ To create PDF manually:
1. 🌐 Open the HTML file in your browser (already open)
2. ⌨️ Press Ctrl+P (or Cmd+P on Mac)
3. 🖨️ Select "Save as PDF" as printer
4. 💾 Click Save
✨ The HTML file is ready to use!
Want any changes in your resume? Just let me know! 💬
```
**If user wants modifications:**
- Make requested changes to the HTML
- Re-open in browser
- Ask again
---
## STEP 4: Render to PDF via Headless Chrome
**Only proceed if user requested automatic PDF generation.**
**Display to user:**
```
4/5 ⏳ Render to PDF
```
### Action 4.1: Locate Browser
```powershell
# Try Chrome first
if (Test-Path "C:\Program Files\Google\Chrome\Application\chrome.exe") {
$browser = "C:\Program Files\Google\Chrome\Application\chrome.exe"
$browserName = "Chrome"
} elseif (Test-Path "C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe") {
$browser = "C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe"
$browserName = "Edge"
}
```
### Action 4.2: Render PDF
**CRITICAL - OneDrive gotcha:** If the resume HTML lives in a OneDrive-synced folder, headless Chrome's `--print-to-pdf` can fail with `Access is denied (0x5)` when writing directly there. Always print to local temp first, then copy into place.
```powershell
# Create temp directory
$tempDir = "$env:LOCALAPPDATA\Temp\resume_pdf"
New-Item -ItemType Directory -Path $tempDir -Force | Out-Null
# Render to temp location
& $browser --headless --disable-gpu --no-pdf-header-footer `
--print-to-pdf="$tempDir\Resume.pdf" `
--no-margins `
"file:///{absolute_path_to_resume_html}"
# Copy to target location
Copy-Item "$tempDir\Resume.pdf" "{target_folder}\Resume.pdf" -Force
```
### Update User
```
4/5 ✅ Render to PDF
```
---
## STEP 5: Verify Single-Page Output
**Display to user:**
```
5/5 ⏳ Verify single-page output
```
### Action
Use the `read_file` tool to check the PDF:
1. Verify page count == 1
2. Check that no section header is orphaned at bottom with content on next page
3. Ensure no bullet/line is visibly cut or duplicated
### If Page Count == 1
```
5/5 ✅ Verify single-page output
```
### If Page Count > 1
```
5/5 ⚠️ Optimizing layout to fit on 1 page... 🔧
```
**Tightening strategy (apply in order, least to most disruptive):**
1. **Container padding:** `.container` print padding: `0.35in` → `0.28in`
2. **Section spacing:** `.section` margin-bottom: `10px` → `7px` (print-only override)
3. **Header spacing:** `h2` top margin: `10px` → `8px` (print-only override)
4. **Line height:** `li` line-height: `1.55` → `1.45` (print-only override)
**After each adjustment:**
- Re-render PDF
- Re-check page count
- Update user with progress
**Important:** Avoid `break-inside: avoid` on whole `.section` — it pushes entire sections to next page, leaving large gaps. Instead use:
- `break-after: avoid` on `h2` (keeps header glued to first content line)
- `break-inside: avoid` on individual `.entry` blocks only
**Final update after tightening:**
```
5/5 ✅ Verify single-page output
```
---
## Final Summary
Display this completion message:
```
🎉 Resume Conversion Complete!
📍 PDF Location: {full_path}
✅ Your PDF is ready to use!
Want any changes in your resume? Just let me know! 💬
```
**Always open the PDF automatically for user verification:**
```powershell
Start-Process "{pdf_path}"
```
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!