antifa-sticker-generator/index.html

326 lines
18 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Antifa Sticker Generator</title>
<style>
form {
display: grid;
grid-template-columns: 30% 50%;
}
svg {
background: repeating-conic-gradient(#dddddd 0% 25%, #999999 0% 50%) 50% / 20px 20px;
}
</style>
</head>
<body>
<h1>Antifa Sticker Generator</h1>
<p>
A purely client-side sticker generator. <a href="https://git.kabelsalat.ch/s3lph/antifa-sticker-generator">Source Code.</a>
</p>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 500 500" width="500" height="500" id="svg">
<defs>
<filter id="black">
<feColorMatrix in="SourceGraphic" type="matrix"
values="1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0" />
</filter>
<filter id="red">
<feColorMatrix in="SourceGraphic" type="matrix"
values="1 1 1 1 0
0 0 0 0 0
0 0 0 0 0
0 0 0 1 0" />
</filter>
</defs>
<g id="svgroot" transform="scale(1, 1)">
<circle id="bleed" cx="250" cy="250" r="249" stroke="#ff00ff" fill="none" stroke-width="0" />
<circle cx="250" cy="250" r="225" stroke="black" fill="white" stroke-width="50" />
<g id="icon">
<image transform="translate(0 0) translate(-9 -2) scale(1, 1)" id="iconred" xlink:href="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iNTEycHgiCiAgICAgaGVpZ2h0PSI1MTJweCIgdmlld0JveD0iMCAwIDUxMiA1MTIiPgogIDxwYXRoIGZpbGw9IiMwMDAwMDAiIGQ9Ik0xMjUuNjY3LDEyMi45MTdjMCwwLDI4LDMzLjA4Myw4OS4yNSwzNC4zMzNjNjEuMjUxLDEuMjUsOTUuOTE3LTM3LjkxNywxMzAuOTE3LTMzLjMzM3M2Myw0My4zMzMsNzcuNzUsNTAuMTY3TDMzMyw0MjUuNzUKICAgICAgICBjMCwwLTEuODMzLDEuMzM0LTExLDQuNjY3cy0xMS4zMzMsNC4zMzMtMTEuMzMzLDQuMzMzbDQwLjI1LTEyMi45MTdjMCwwLTM4Ljc1LTMyLjY4Ny03My43NS0zNQogICAgICAgIGMtMzUuMDAxLTIuMzEzLTUwLjkxNywyMy43NS0xMTAuMDgzLDEzLjc1QzEwNy45MTcsMjgwLjU4Myw2OS41LDI0My41LDY5LjUsMjQzLjVzLTEuNDE1LTIxLjA2NywxNC41LTU5LjgzMwogICAgICAgIEM5OS45MTQsMTQ0LjkwMiwxMjUuNjY3LDEyMi45MTcsMTI1LjY2NywxMjIuOTE3eiIvPgo8L3N2Zz4K" width="512" height="512" filter="url(#red)" />
<image transform="translate(0 0) translate(-9 -2) scale(1, 1)" id="iconblack" xlink:href="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iNTEycHgiCiAgICAgaGVpZ2h0PSI1MTJweCIgdmlld0JveD0iMCAwIDUxMiA1MTIiPgogIDxwYXRoIGZpbGw9IiMwMDAwMDAiIGQ9Ik02OS45MTcsMjUxLjI1YzAsMCw0MS4wODQsMzguNDE2LDEwMi40MTcsNDYuMDgzYzYxLjMzMiw3LjY2Nyw2Ny4wMzktMTQuNDkxLDEwMC45MTYtMTQuMjUKICAgICAgICBjMjEuNTQ4LDAuMTUzLDI5LjMzNCw5LjU4NCwyOS4zMzQsOS41ODRsLTUzLjUwNiwxNDkuNjdjMCwwLTQuMTgtMC4wNDQtMTAuMDkyLTAuNjkyYy01LjkxMS0wLjY0OC05LjU0MS0xLjI5NC05LjU0MS0xLjI5NAogICAgICAgIEwyNTcsMzU3LjVjMCwwLTEzLjU4NC04LjcwNy0yNy4yNS0xMC41Yy0xMy42NjgtMS43OTMtMjEuMDQyLDMtNDYuNSwyLjVjLTI0LjA3My0wLjQ3My03MS4yNS0xMi43NS05My4zMzMtNDUuNzUKICAgICAgICBTNjkuOTE3LDI1MS4yNSw2OS45MTcsMjUxLjI1eiIvPgo8L3N2Zz4K" width="512" height="512" filter="url(#black)" />
</g>
<path id="upper" stroke="none" fill="none" d="M 250 460 A 210 210 0 0 1 250 40 A 210 210 0 1 1 250 460"/>
<path id="lower" stroke="none" fill="none" d="M 250 10 A 240 240 0 1 0 250 490 A 210 210 0 0 0 250 10"/>
<text style="fill: white; font-family: sans-serif; font-weight: bold; font-size: 40px; text-align: center; text-anchor: middle;"><textPath id="uppertext" startOffset="50%" xlink:href="#upper">ANTIFASCHISTISCHE</textPath></text>
<text style="fill: white; font-family: sans-serif; font-weight: bold; font-size: 40px; text-align: center; text-anchor: middle;"><textPath id="lowertext" startOffset="50%" xlink:href="#lower">AKTION</textPath></text>
</g>
</svg>
<form>
<label for="input-text-upper">Upper text</label>
<input type="text" id="input-text-upper" name="upper" value="ANTIFASCHISTISCHE">
<label for="input-text-lower">Lower text</label>
<input type="text" id="input-text-lower" name="lower" value="AKTION">
<label for="input-range-shift-x">X Position</label>
<input type="range" id="input-range-position-x" name="position-x" value="-9" min="-500" max="500">
<label for="input-range-shift-y">Y Position</label>
<input type="range" id="input-range-position-y" name="position-y" value="-2" min="-500" max="500">
<label for="input-range-shift-x">X Shift</label>
<input type="range" id="input-range-shift-x" name="distance-x" value="0" min="-250" max="250">
<label for="input-range-shift-y">Y Shift</label>
<input type="range" id="input-range-shift-y" name="distance-y" value="0" min="-250" max="250">
<label for="input-range-scale-black">Black Scale</label>
<input type="range" id="input-range-scale-black" name="scale-black" value="0" min="-3" max="3" step="0.01">
<label for="input-range-scale-red">Red Scale</label>
<input type="range" id="input-range-scale-red" name="scale-red" value="0" min="-3" max="3" step="0.01">
<label for="input-file-icon-black">Black Icon (black+white+alpha only)</label>
<input type="file" id="input-file-icon-black" name="icon-black" accept="image/*">
<label for="input-file-icon-red">Red Icon (black+white+alpha only)</label>
<input type="file" id="input-file-icon-red" name="icon-red" accept="image/*">
<label for="input-range-bleed">Bleed (black in download)</label>
<input type="range" id="input-range-bleed" name="bleed" value="0" min="0" max="50">
<div>
<input type="checkbox" id="input-check-swap-red-black" name="swap-red-black">
<label for="input-check-swap-red-black">Red on top of black</label>
<input type="checkbox" id="input-check-lock-scale" name="lock-scale">
<label for="input-check-lock-scale">Same scale for red and black</label>
</div>
<div>
<input type="button" id="input-button-download" value="Download SVG">
<input type="button" id="input-button-download-png" value="Download PNG">
</div>
<label for="input-file-import-svg">Import downloaded SVG</label>
<input type="file" id="input-file-import-svg" name="import-svg" accept="image/svg+xml">
</form>
<a id="download" download="antifa.svg" href="#"></a>
<a id="download-png" download="antifa.png" href="#"></a>
<script>
let doc = document.getElementById('svg');
let inputTextUpper = document.getElementById("input-text-upper");
let inputTextLower = document.getElementById("input-text-lower");
let inputRangePositionX = document.getElementById("input-range-position-x");
let inputRangePositionY = document.getElementById("input-range-position-y");
let inputRangeShiftX = document.getElementById("input-range-shift-x");
let inputRangeShiftY = document.getElementById("input-range-shift-y");
let inputRangeScaleBlack = document.getElementById("input-range-scale-black");
let inputRangeScaleRed = document.getElementById("input-range-scale-red");
let inputFileIconBlack = document.getElementById("input-file-icon-black");
let inputFileIconRed = document.getElementById("input-file-icon-red");
let inputCheckSwap = document.getElementById("input-check-swap-red-black");
let inputCheckLockScale = document.getElementById("input-check-lock-scale");
let inputRangeBleed = document.getElementById("input-range-bleed");
let inputButtonDownload = document.getElementById("input-button-download");
let inputButtonDownloadPng = document.getElementById("input-button-download-png");
let inputFileImportSvg = document.getElementById("input-file-import-svg");
inputTextUpper.oninput = (ev) => {
doc.getElementById("uppertext").textContent = inputTextUpper.value;
};
inputTextLower.oninput = (ev) => {
doc.getElementById("lowertext").textContent = inputTextLower.value;
};
inputRangePositionX.oninput = (ev) => {
doc.getElementById("iconblack").transform.baseVal[1].matrix.e = inputRangePositionX.value;
doc.getElementById("iconred").transform.baseVal[1].matrix.e = inputRangePositionX.value;
};
inputRangePositionY.oninput = (ev) => {
doc.getElementById("iconblack").transform.baseVal[1].matrix.f = inputRangePositionY.value;
doc.getElementById("iconred").transform.baseVal[1].matrix.f = inputRangePositionY.value;
};
inputRangeShiftX.oninput = (ev) => {
doc.getElementById("iconblack").transform.baseVal[0].matrix.e = inputRangeShiftX.value;
doc.getElementById("iconred").transform.baseVal[0].matrix.e = -inputRangeShiftX.value;
};
inputRangeShiftY.oninput = (ev) => {
doc.getElementById("iconblack").transform.baseVal[0].matrix.f = inputRangeShiftY.value;
doc.getElementById("iconred").transform.baseVal[0].matrix.f = -inputRangeShiftY.value;
};
inputRangeScaleBlack.oninput = (ev) => {
let iconblack = doc.getElementById("iconblack");
iconblack.transform.baseVal[2].matrix.a = Math.pow(10, inputRangeScaleBlack.value);
iconblack.transform.baseVal[2].matrix.d = Math.pow(10, inputRangeScaleBlack.value);
if (inputCheckLockScale.checked) {
let iconred = doc.getElementById("iconred");
iconred.transform.baseVal[2].matrix.a = Math.pow(10, inputRangeScaleBlack.value);
iconred.transform.baseVal[2].matrix.d = Math.pow(10, inputRangeScaleBlack.value);
}
};
inputRangeScaleRed.oninput = (ev) => {
let iconred = doc.getElementById("iconred");
iconred.transform.baseVal[2].matrix.a = Math.pow(10, inputRangeScaleRed.value);
iconred.transform.baseVal[2].matrix.d = Math.pow(10, inputRangeScaleRed.value);
};
inputFileIconBlack.onchange = (ev) => {
let file = ev.target.files[0];
let reader = new FileReader();
let iconblack = doc.getElementById("iconblack");
reader.onload = (re) => {
let img = new Image();
img.onload = (i) => {
iconblack.href.baseVal = re.target.result;
iconblack.width.baseVal.value = i.path[0].width;
iconblack.height.baseVal.value = i.path[0].height;
};
img.src = re.target.result;
};
reader.readAsDataURL(file);
};
inputFileIconRed.onchange = (ev) => {
let file = ev.target.files[0];
let reader = new FileReader();
let iconred = doc.getElementById("iconred");
reader.onload = (re) => {
let img = new Image();
img.onload = (i) => {
iconred.href.baseVal = re.target.result;
iconred.width.baseVal.value = i.path[0].width;
iconred.height.baseVal.value = i.path[0].height;
};
img.src = re.target.result;
};
reader.readAsDataURL(file);
};
inputCheckSwap.onchange = (ev) => {
if (ev.target.checked) {
doc.getElementById("icon").insertBefore(doc.getElementById("iconblack"), doc.getElementById("iconred"));
} else {
doc.getElementById("icon").insertBefore(doc.getElementById("iconred"), doc.getElementById("iconblack"));
}
};
inputCheckLockScale.onchange = (ev) => {
inputRangeScaleRed.disabled = ev.target.checked;
let iconred = doc.getElementById("iconred");
if (ev.target.checked) {
iconred.transform.baseVal[2].matrix.a = Math.pow(10, inputRangeScaleBlack.value);
iconred.transform.baseVal[2].matrix.d = Math.pow(10, inputRangeScaleBlack.value);
} else {
iconred.transform.baseVal[2].matrix.a = Math.pow(10, inputRangeScaleRed.value);
iconred.transform.baseVal[2].matrix.d = Math.pow(10, inputRangeScaleRed.value);
}
}
inputRangeBleed.oninput = (ev) => {
let bleed = doc.getElementById("bleed");
let bleedVal = parseInt(inputRangeBleed.value);
if (bleedVal == 0) {
bleed.style.strokeWidth = 0;
} else {
bleed.style.strokeWidth = bleedVal * 2 + 1;
}
doc.width.baseVal.value = 500 + bleedVal * 2;
doc.height.baseVal.value = 500 + bleedVal * 2;
doc.viewBox.baseVal.x = -bleedVal;
doc.viewBox.baseVal.y = -bleedVal;
doc.viewBox.baseVal.width = 500 + bleedVal * 2;
doc.viewBox.baseVal.height = 500 + bleedVal * 2;
};
inputButtonDownload.onclick = (ev) => {
doc.getElementById("bleed").style.stroke = 'black';
let serialized = new XMLSerializer().serializeToString(doc);
doc.getElementById("bleed").style.stroke = '#ff00ff';
let a = document.getElementById('download');
a.setAttribute('href', 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent('<?xml version="1.0" encoding="utf-8"?>\n' + serialized));
a.click();
};
inputButtonDownloadPng.onclick = (ev) => {
let svgSize = doc.width.baseVal.value;
let svgOffset = doc.viewBox.baseVal.x;
let imgSize = parseInt(prompt("Enter image size (e.g. 2048)", "2048"));
let svgRoot = doc.getElementById("svgroot");
if (Number.isNaN(imgSize)) {
return;
}
// Resize SVG to output size, then export as XML, and resize back to default size
doc.getElementById("bleed").style.stroke = 'black';
doc.width.baseVal.value = imgSize;
doc.height.baseVal.value = imgSize;
doc.viewBox.baseVal.width = imgSize;
doc.viewBox.baseVal.height = imgSize;
doc.viewBox.baseVal.x = svgOffset * imgSize/svgSize;
doc.viewBox.baseVal.y = svgOffset * imgSize/svgSize;
svgRoot.transform.baseVal[0].matrix.a = imgSize/svgSize;
svgRoot.transform.baseVal[0].matrix.d = imgSize/svgSize;
let serialized = new XMLSerializer().serializeToString(doc);
doc.getElementById("bleed").style.stroke = '#ff00ff';
svgRoot.transform.baseVal[0].matrix.a = 1;
svgRoot.transform.baseVal[0].matrix.d = 1;
doc.viewBox.baseVal.width = svgSize;
doc.viewBox.baseVal.height = svgSize;
doc.viewBox.baseVal.x = svgOffset;
doc.viewBox.baseVal.y = svgOffset;
doc.width.baseVal.value = svgSize;
doc.height.baseVal.value = svgSize;
let canvas = document.createElement('canvas');
canvas.width = imgSize;
canvas.height = imgSize;
let ctx = canvas.getContext("2d");
let image = new Image();
image.onload = () => {
ctx.drawImage(image, 0, 0);
let a = document.getElementById('download-png');
a.setAttribute('href', canvas.toDataURL());
a.click();
};
image.src = 'data:image/png;charset=utf-8,' + encodeURIComponent(serialized);
};
inputFileImportSvg.onchange = (ev) => {
let file = ev.target.files[0];
let reader = new FileReader();
reader.onload = (re) => {
let parser = new DOMParser();
let imp = parser.parseFromString(re.target.result, 'image/svg+xml');
let iconblack = doc.getElementById('iconblack');
let iconred = doc.getElementById('iconred');
// texts
inputTextUpper.value = imp.getElementById('uppertext').textContent;
inputTextLower.value = imp.getElementById('lowertext').textContent;
doc.getElementById('uppertext').textContent = inputTextUpper.value;
doc.getElementById('lowertext').textContent = inputTextLower.value;
// img href
iconblack.href.baseVal = imp.getElementById('iconblack').href.baseVal;
iconred.href.baseVal = imp.getElementById('iconred').href.baseVal;
// shift
inputRangeShiftX.value = imp.getElementById('iconblack').transform.baseVal[0].matrix.e;
inputRangeShiftY.value = imp.getElementById('iconblack').transform.baseVal[0].matrix.f;
iconblack.transform.baseVal[0].matrix.e = inputRangeShiftX.value;
iconred.transform.baseVal[0].matrix.e = -inputRangeShiftX.value;
iconblack.transform.baseVal[0].matrix.f = inputRangeShiftY.value;
iconred.transform.baseVal[0].matrix.f = -inputRangeShiftY.value;
// position
inputRangePositionX.value = imp.getElementById('iconblack').transform.baseVal[1].matrix.e;
inputRangePositionY.value = imp.getElementById('iconblack').transform.baseVal[1].matrix.f;
iconblack.transform.baseVal[1].matrix.e = inputRangePositionX.value;
iconred.transform.baseVal[1].matrix.e = inputRangePositionX.value;
iconblack.transform.baseVal[1].matrix.f = inputRangePositionY.value;
iconred.transform.baseVal[1].matrix.f = inputRangePositionY.value;
// scale
let scaleBlack = imp.getElementById('iconblack').transform.baseVal[2].matrix.a;
let scaleRed = imp.getElementById('iconred').transform.baseVal[2].matrix.a;
inputRangeScaleBlack.value = Math.log10(scaleBlack);
inputRangeScaleRed.value = Math.log10(scaleRed);
iconblack.transform.baseVal[2].matrix.a = scaleBlack;
iconblack.transform.baseVal[2].matrix.d = scaleBlack;
iconred.transform.baseVal[2].matrix.a = scaleRed;
iconred.transform.baseVal[2].matrix.d = scaleRed;
// swap
if (imp.getElementById('icon').children[0].id == 'iconblack') {
inputCheckSwap.checked = true;
doc.getElementById('icon').insertBefore(doc.getElementById('iconblack'), doc.getElementById('iconred'));
} else {
inputCheckSwap.checked = false;
doc.getElementById('icon').insertBefore(doc.getElementById('iconred'), doc.getElementById('iconblack'));
}
// bleed
inputRangeBleed.value = Math.max(imp.getElementById('bleed').style.strokeWidth - 1, 0);
let bleed = doc.getElementById("bleed");
let bleedVal = parseInt(inputRangeBleed.value);
if (bleedVal == 0) {
bleed.style.strokeWidth = 0;
} else {
bleed.style.strokeWidth = bleedVal * 2 + 1;
}
doc.width.baseVal.value = 500 + bleedVal * 2;
doc.height.baseVal.value = 500 + bleedVal * 2;
doc.viewBox.baseVal.x = -bleedVal;
doc.viewBox.baseVal.y = -bleedVal;
doc.viewBox.baseVal.width = 500 + bleedVal * 2;
doc.viewBox.baseVal.height = 500 + bleedVal * 2;
};
reader.readAsText(file);
};
</script>
</body>
</html>