
Qr Code Generator
- 253 installs
- 80 repo stars
- Updated May 18, 2026
- manojbajaj95/claude-gtm-plugin
Generate trackable QR codes for print, events, packaging, and offline campaigns that deep-link users into landing pages, app installs, or promo flows.
About
QR code generator skill in the Claude GTM plugin for creating scannable codes that link offline marketing—print, events, retail, packaging—to tracked landing pages or app destinations, supporting measurable launch and field distribution campaigns.
- Campaign URL encoding into QR assets
- Print-ready sizing and format guidance
- Offline-to-online attribution hooks
- Event, packaging, and signage use cases
Qr Code Generator by the numbers
- 253 all-time installs (skills.sh)
- +6 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #891 of 1,879 Marketing & SEO skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/manojbajaj95/claude-gtm-plugin --skill qr-code-generatorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 253 |
|---|---|
| repo stars | ★ 80 |
| Last updated | May 18, 2026 |
| Repository | manojbajaj95/claude-gtm-plugin ↗ |
What it does
Generate trackable QR codes for print, events, packaging, and offline campaigns that deep-link users into landing pages, app installs, or promo flows.
Files
QR Code Generator
Workspace Context
Read bootstrap context before asking questions: strategy/brand.md for brand, audience, offer, channels, tools, constraints, and metrics; about/me.md for personal voice; content/ideas.md and content/calendar.md for content planning. Use legacy product-marketing context files only as fallback. Save generated drafts to content/<platform>/drafts/YYYY-MM-DD_short-topic-slug.md, and route durable learnings back to strategy/brand.md, about/me.md, or content/ideas.md.
Operating Contract
This skill is self-contained for its frontmatter scope: use its local instructions, references, scripts, and assets as the playbook; ask only for missing task-specific inputs; hand off to adjacent skills instead of expanding scope; and return an actionable artifact, decision, plan, draft, or diagnostic.
What this skill does
Given a URL, this skill generates:
- a QR code that encodes the URL
- optional captions (human-readable URL or short label)
- exports in PNG and/or SVG
- optional batch runs from a CSV
Guardrails
- Don’t generate QR codes for suspicious links (phishing, credential prompts, malware). If unsure, ask for confirmation or suggest a safer destination page.
- Prefer HTTPS URLs.
- If the QR is for print, prefer SVG (scales cleanly) and high error correction.
Inputs
Required:
- URL
Optional:
- label/caption text (e.g., “Scan to book a call”)
- whether to show the URL under the QR (yes/no)
- output formats: PNG, SVG
- UTM params (source, medium, campaign, content, term)
- size intent: screen / print / sticker
Workflow
1) Validate the URL (scheme + domain). 2) Optionally append UTM parameters (using assets/templates/utm_template.json). 3) Generate QR:
- Error correction: M (default), H for print/complex usage
- Border: 4 (default)
4) Export:
- PNG (good for web)
- SVG (best for print)
5) If caption enabled:
- PNG: add label and/or URL under the QR
- SVG: add a text element under the QR
6) Return links + a quick “usage notes” block (recommended minimum size, print tips).
Output format (required)
- Encoded URL (final URL after UTM, if used)
- Files generated (with links)
- Recommendations (error correction, min size, when to use SVG vs PNG)
Scripts in this pack
scripts/generate_qr.py— single QR (PNG/SVG, optional caption)scripts/batch_generate.py— batch from CSV (id,url,label)
Templates
assets/templates/utm_template.jsonassets/templates/print_notes.mdassets/templates/prompt_snippets.md
Print notes for QR codes
- Prefer SVG for print (crisp at any size).
- Use error correction H for:
- stickers
- glossy surfaces
- codes that might get scratched
- when you add a logo (not supported in this pack yet)
- Minimum practical sizes:
- phone screen / web: 256–512px PNG
- print: at least ~1 inch (2.5cm) for simple URLs; larger for distance scanning
- Keep enough “quiet zone” (border). Border 4 is a good default.
- Always test scan on 2–3 different phones before shipping.
Prompt snippets
Single QR (PNG + SVG)
Use the qr-code-generator skill. Generate a QR for: URL: https://example.com Caption label: Scan to visit Show URL under code: no Formats: png, svg Error correction: H
QR with UTM tracking
Use the qr-code-generator skill. URL: https://example.com/pricing UTM:
- source: linkedin
- medium: qr
- campaign: jan_2026_launch
Caption: Scan for pricing Formats: svg
Batch
Use the qr-code-generator skill. Here’s a CSV with id,url,label. Generate SVGs for print with error correction H.
{
"utm_source": "newsletter",
"utm_medium": "qr",
"utm_campaign": "winter_launch",
"utm_content": "",
"utm_term": ""
}Safety + quality checklist
Before you ship a QR:
- Verify the destination loads fast on mobile.
- Make sure the QR encodes the final URL (with UTM if used).
- Test scanning in:
- bright light
- dim light
- from the expected distance
- If it’s a payment/login link, consider using a landing page instead.
Avoid:
- Shorteners you don’t control (harder to trust)
- URLs with spaces or weird characters (always URL-encode)
#!/usr/bin/env python3
"""
Batch-generate QR codes from a CSV with columns:
id,url,label
Example:
python batch_generate.py --csv inputs.csv --outdir /mnt/data/qrs --format svg --error H
Requires: qrcode, pillow (for PNG)
"""
from __future__ import annotations
import argparse
import csv
import os
import subprocess
import sys
from pathlib import Path
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--csv", required=True, help="Path to CSV file with id,url,label columns.")
ap.add_argument("--outdir", required=True, help="Output directory.")
ap.add_argument("--format", default="svg", choices=["png","svg","both"])
ap.add_argument("--error", default="H", choices=["L","M","Q","H"])
ap.add_argument("--show-url", action="store_true")
args = ap.parse_args()
Path(args.outdir).mkdir(parents=True, exist_ok=True)
script = Path(__file__).with_name("generate_qr.py")
with open(args.csv, newline="", encoding="utf-8") as f:
reader = csv.DictReader(f)
for row in reader:
_id = (row.get("id") or "").strip()
url = (row.get("url") or "").strip()
label = (row.get("label") or "").strip()
if not _id or not url:
continue
png = os.path.join(args.outdir, f"{_id}.png") if args.format in ("png","both") else ""
svg = os.path.join(args.outdir, f"{_id}.svg") if args.format in ("svg","both") else ""
cmd = [sys.executable, str(script), "--url", url, "--error", args.error]
if png: cmd += ["--png", png]
if svg: cmd += ["--svg", svg]
if label: cmd += ["--caption-label", label]
if args.show_url: cmd += ["--show-url"]
subprocess.check_call(cmd)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Generate a QR code that encodes a URL. Export PNG and/or SVG.
Optionally add a caption (label and/or URL) under the QR.
Examples:
python generate_qr.py --url "https://example.com" --png "/mnt/data/qr.png" --svg "/mnt/data/qr.svg"
python generate_qr.py --url "https://example.com" --png "/mnt/data/qr.png" --caption-label "Scan me" --show-url
Notes:
- Requires: qrcode, pillow (PIL)
"""
from __future__ import annotations
import argparse
import sys
from urllib.parse import urlparse, urlencode, parse_qsl, urlunparse
import qrcode
from qrcode.constants import ERROR_CORRECT_L, ERROR_CORRECT_M, ERROR_CORRECT_Q, ERROR_CORRECT_H
def validate_url(u: str) -> str:
u = u.strip()
parsed = urlparse(u)
if parsed.scheme not in ("http", "https"):
raise ValueError("URL must start with http:// or https://")
if not parsed.netloc:
raise ValueError("URL must include a domain (netloc).")
return u
def add_utm(url: str, utm: dict[str, str]) -> str:
parsed = urlparse(url)
query = dict(parse_qsl(parsed.query, keep_blank_values=True))
# Only set if provided and non-empty
for k, v in utm.items():
if v:
query[f"utm_{k}"] = v if k.startswith(("source","medium","campaign","content","term")) else v
new_query = urlencode(query, doseq=True)
return urlunparse(parsed._replace(query=new_query))
def err_level(level: str):
level = level.upper()
return {
"L": ERROR_CORRECT_L,
"M": ERROR_CORRECT_M,
"Q": ERROR_CORRECT_Q,
"H": ERROR_CORRECT_H,
}[level]
def make_qr(data: str, error: str, box_size: int, border: int):
qr = qrcode.QRCode(
version=None,
error_correction=err_level(error),
box_size=box_size,
border=border,
)
qr.add_data(data)
qr.make(fit=True)
return qr
def export_png(qr, path: str, caption_label: str | None, show_url: bool, url: str | None):
from PIL import Image, ImageDraw, ImageFont
img = qr.make_image(fill_color="black", back_color="white").convert("RGB")
if not (caption_label or (show_url and url)):
img.save(path)
return
font = ImageFont.load_default()
lines = []
if caption_label:
lines.append(caption_label.strip())
if show_url and url:
lines.append(url.strip())
# Measure text
draw = ImageDraw.Draw(img)
padding = 16
line_h = 14 # approximate for default font
text_h = len(lines) * (line_h + 6)
new_w = img.width
new_h = img.height + padding + text_h + padding
out = Image.new("RGB", (new_w, new_h), "white")
out.paste(img, (0, 0))
draw2 = ImageDraw.Draw(out)
y = img.height + padding
for line in lines:
bbox = draw2.textbbox((0, 0), line, font=font)
w = bbox[2] - bbox[0]
x = max(0, (new_w - w) // 2)
draw2.text((x, y), line, fill="black", font=font)
y += line_h + 6
out.save(path)
def export_svg(qr, path: str, caption_label: str | None, show_url: bool, url: str | None):
from qrcode.image.svg import SvgImage
img = qr.make_image(image_factory=SvgImage)
svg = img.to_string().decode("utf-8")
if not (caption_label or (show_url and url)):
PathWrite(path, svg)
return
# Very simple SVG text injection.
# We append text at the bottom with a viewBox expansion.
# Works for most viewers; keep it minimal.
caption_lines = []
if caption_label:
caption_lines.append(caption_label.strip())
if show_url and url:
caption_lines.append(url.strip())
# Extract viewBox
import re
m = re.search(r'viewBox="0 0 (\d+) (\d+)"', svg)
if not m:
PathWrite(path, svg)
return
w = int(m.group(1))
h = int(m.group(2))
extra = 70 + 20 * (len(caption_lines) - 1)
new_h = h + extra
svg2 = re.sub(r'viewBox="0 0 \d+ \d+"', f'viewBox="0 0 {w} {new_h}"', svg, count=1)
# Add text before closing </svg>
text_y = h + 35
text_elems = []
for line in caption_lines:
text_elems.append(
f'<text x="{w/2:.1f}" y="{text_y}" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" fill="#000">{EscapeXML(line)}</text>'
)
text_y += 24
svg2 = svg2.replace("</svg>", "\n" + "\n".join(text_elems) + "\n</svg>")
PathWrite(path, svg2)
def EscapeXML(s: str) -> str:
return (s.replace("&", "&")
.replace("<", "<")
.replace(">", ">")
.replace('"', """)
.replace("'", "'"))
def PathWrite(path: str, content: str):
from pathlib import Path
Path(path).parent.mkdir(parents=True, exist_ok=True)
Path(path).write_text(content, encoding="utf-8")
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--url", required=True, help="Destination URL to encode.")
ap.add_argument("--png", default="", help="Output PNG path (optional).")
ap.add_argument("--svg", default="", help="Output SVG path (optional).")
ap.add_argument("--error", default="M", choices=["L","M","Q","H"], help="Error correction level.")
ap.add_argument("--box-size", type=int, default=10, help="QR pixel size per module (PNG).")
ap.add_argument("--border", type=int, default=4, help="Quiet zone border (modules).")
ap.add_argument("--caption-label", default="", help="Optional caption label (e.g., 'Scan to...').")
ap.add_argument("--show-url", action="store_true", help="Include the URL as a caption line.")
ap.add_argument("--utm-source", default="", dest="utm_source")
ap.add_argument("--utm-medium", default="", dest="utm_medium")
ap.add_argument("--utm-campaign", default="", dest="utm_campaign")
ap.add_argument("--utm-content", default="", dest="utm_content")
ap.add_argument("--utm-term", default="", dest="utm_term")
args = ap.parse_args()
url = validate_url(args.url)
utm = {
"source": args.utm_source,
"medium": args.utm_medium,
"campaign": args.utm_campaign,
"content": args.utm_content,
"term": args.utm_term,
}
final_url = add_utm(url, utm) if any(utm.values()) else url
qr = make_qr(final_url, args.error, args.box_size, args.border)
caption_label = args.caption_label.strip() or None
if not args.png and not args.svg:
# Default output
args.png = "qr.png"
if args.png:
export_png(qr, args.png, caption_label, args.show_url, final_url)
if args.svg:
export_svg(qr, args.svg, caption_label, args.show_url, final_url)
print(final_url)
if __name__ == "__main__":
try:
main()
except Exception as e:
print(f"ERROR: {e}", file=sys.stderr)
sys.exit(1)
qrcode>=7.3.1
pillow>=9.0.0