fix(pdf): switch to window.print() for reliable PDF export

html2canvas (bundled in html2pdf.js) can't parse CSS color-mix()
function regardless of onclone workarounds. Using window.print()
which leverages the browser's native renderer with full CSS support.
In Chrome/Edge the print dialog defaults to 'Save as PDF' destination.
This commit is contained in:
Local Build committed 2026-08-13 21:14:24 +02:00
1 parent e00487aa15
commit 319c23e69e
2 files changed
+54 -178

No files matched your search

+27 -89
View File
@@ -973,23 +973,10 @@
<script>
(function () {
var btn = document.querySelector('[data-resume-pdf-btn]');
console.log('[PDF] btn found:', !!btn);
if (!btn) return;
var CDN = 'https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.2/html2pdf.bundle.min.js';
var busy = false;
function loadScript(src) {
return new Promise(function (ok, fail) {
if (window.html2pdf) { ok(); return; }
var s = document.createElement('script');
s.src = src;
s.onload = ok;
s.onerror = fail;
document.head.appendChild(s);
});
}
btn.addEventListener('click', function () {
if (busy) return;
busy = true;
@@ -1025,88 +1012,39 @@
photoReady = Promise.resolve(null);
}
function resolveStylesInClone(clonedDoc, original) {
var props = ['color','background-color','background','border-color','border-top-color','border-bottom-color','border-left-color','border-right-color','box-shadow','opacity','font-family','font-size','font-weight','line-height','letter-spacing','text-align','text-decoration','text-transform','padding','padding-top','padding-bottom','padding-left','padding-right','margin','margin-top','margin-bottom','margin-left','margin-right','display','flex-direction','align-items','justify-content','gap','grid-template-columns','grid-column','width','height','min-width','min-height','max-width','max-height','position','top','bottom','left','right','z-index','overflow','border-radius','border-width','border-style','white-space','list-style-type','object-fit','content'];
var origEls = original.querySelectorAll('*');
var cloneEls = clonedDoc.querySelectorAll('*');
for (var i = 0; i < origEls.length && i < cloneEls.length; i++) {
var cs = getComputedStyle(origEls[i]);
for (var j = 0; j < props.length; j++) {
var v = cs.getPropertyValue(props[j]);
if (v) cloneEls[i].style.setProperty(props[j], v);
}
}
var rootCs = getComputedStyle(original);
for (var j = 0; j < props.length; j++) {
var v = rootCs.getPropertyValue(props[j]);
if (v) clonedDoc.style.setProperty(props[j], v);
}
var sheets = clonedDoc.styleSheets;
for (var s = sheets.length - 1; s >= 0; s--) {
try {
var sheet = sheets[s];
while (sheet.cssRules.length > 0) sheet.deleteRule(0);
} catch (e) {}
}
clonedDoc.querySelectorAll('style').forEach(function (el) { el.remove(); });
clonedDoc.querySelectorAll('link[rel="stylesheet"]').forEach(function (el) { el.remove(); });
var pseudoProps = ['content','color','background-color','background','border-color','border-top-color','border-bottom-color','border-left-color','border-right-color','box-shadow','opacity','font-family','font-size','font-weight','line-height','letter-spacing','text-align','text-decoration','text-transform','padding','padding-top','padding-bottom','padding-left','padding-right','margin','margin-top','margin-bottom','margin-left','margin-right','display','width','height','border-radius','border-width','border-style','white-space','position','top','bottom','left','right','z-index','overflow','list-style-type','object-fit'];
for (var i = 0; i < origEls.length && i < cloneEls.length; i++) {
['::before','::after'].forEach(function(pseudo) {
try {
var cs = getComputedStyle(origEls[i], pseudo);
var content = cs.getPropertyValue('content');
if (!content || content === 'none' || content === 'normal') return;
var el = document.createElement('span');
el.style.cssText = 'display:block;margin:0;padding:0;border:none;background:none;';
for (var j = 0; j < pseudoProps.length; j++) {
var v = cs.getPropertyValue(pseudoProps[j]);
if (v && v !== 'normal' && v !== 'auto') el.style.setProperty(pseudoProps[j], v);
}
el.textContent = content.replace(/^["']|["']$/g, '');
if (pseudo === '::before') cloneEls[i].prepend(el);
else cloneEls[i].appendChild(el);
} catch (e) {}
});
}
}
photoReady.then(function (dataUrl) {
if (dataUrl && photo) photo.setAttribute('src', dataUrl);
return document.fonts ? document.fonts.ready : Promise.resolve();
})
.then(function () {
return loadScript(CDN);
var styles = [];
document.querySelectorAll('link[rel="stylesheet"]').forEach(function (l) {
styles.push('<link rel="stylesheet" href="' + l.href + '">');
});
document.querySelectorAll('style').forEach(function (s) {
styles.push('<style>' + s.textContent + '</style>');
});
var win = window.open('', '_blank');
if (!win) { throw new Error('Popup blocked'); }
win.document.write(
'<!DOCTYPE html><html><head><meta charset="utf-8">' +
styles.join('') +
'<style>@media print{body{margin:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;}}</style>' +
'</head><body>' + doc.outerHTML + '</body></html>'
);
win.document.close();
win.onload = function () {
setTimeout(function () {
win.print();
if (origSrc && photo) photo.setAttribute('src', origSrc);
busy = false;
btn.removeAttribute('data-loading');
}, 500);
};
})
.then(function () {
return html2pdf().from(doc).set({
margin: 10,
filename: 'Mamadaev_Resume.pdf',
image: { type: 'jpeg', quality: 0.95 },
html2canvas: {
scale: 2,
useCORS: true,
backgroundColor: '#fff',
onclone: function (clonedDoc) {
var clonedEl = clonedDoc.querySelector('.resume-doc');
if (!clonedEl) return;
resolveStylesInClone(clonedDoc, doc);
}
},
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' }
}).save();
})
.then(function () {
if (origSrc && photo) photo.setAttribute('src', origSrc);
busy = false;
btn.removeAttribute('data-loading');
})
.catch(function (e) {
console.error('[PDF] error:', e.message || e, e.stack || '');
.catch(function () {
if (origSrc && photo) photo.setAttribute('src', origSrc);
busy = false;
btn.removeAttribute('data-loading');
+27 -89
View File
@@ -842,23 +842,10 @@
<script>
(function () {
var btn = document.querySelector('[data-resume-pdf-btn]');
console.log('[PDF] btn found:', !!btn);
if (!btn) return;
var CDN = 'https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.2/html2pdf.bundle.min.js';
var busy = false;
function loadScript(src) {
return new Promise(function (ok, fail) {
if (window.html2pdf) { ok(); return; }
var s = document.createElement('script');
s.src = src;
s.onload = ok;
s.onerror = fail;
document.head.appendChild(s);
});
}
btn.addEventListener('click', function () {
if (busy) return;
busy = true;
@@ -894,88 +881,39 @@
photoReady = Promise.resolve(null);
}
function resolveStylesInClone(clonedDoc, original) {
var props = ['color','background-color','background','border-color','border-top-color','border-bottom-color','border-left-color','border-right-color','box-shadow','opacity','font-family','font-size','font-weight','line-height','letter-spacing','text-align','text-decoration','text-transform','padding','padding-top','padding-bottom','padding-left','padding-right','margin','margin-top','margin-bottom','margin-left','margin-right','display','flex-direction','align-items','justify-content','gap','grid-template-columns','grid-column','width','height','min-width','min-height','max-width','max-height','position','top','bottom','left','right','z-index','overflow','border-radius','border-width','border-style','white-space','list-style-type','object-fit','content'];
var origEls = original.querySelectorAll('*');
var cloneEls = clonedDoc.querySelectorAll('*');
for (var i = 0; i < origEls.length && i < cloneEls.length; i++) {
var cs = getComputedStyle(origEls[i]);
for (var j = 0; j < props.length; j++) {
var v = cs.getPropertyValue(props[j]);
if (v) cloneEls[i].style.setProperty(props[j], v);
}
}
var rootCs = getComputedStyle(original);
for (var j = 0; j < props.length; j++) {
var v = rootCs.getPropertyValue(props[j]);
if (v) clonedDoc.style.setProperty(props[j], v);
}
var sheets = clonedDoc.styleSheets;
for (var s = sheets.length - 1; s >= 0; s--) {
try {
var sheet = sheets[s];
while (sheet.cssRules.length > 0) sheet.deleteRule(0);
} catch (e) {}
}
clonedDoc.querySelectorAll('style').forEach(function (el) { el.remove(); });
clonedDoc.querySelectorAll('link[rel="stylesheet"]').forEach(function (el) { el.remove(); });
var pseudoProps = ['content','color','background-color','background','border-color','border-top-color','border-bottom-color','border-left-color','border-right-color','box-shadow','opacity','font-family','font-size','font-weight','line-height','letter-spacing','text-align','text-decoration','text-transform','padding','padding-top','padding-bottom','padding-left','padding-right','margin','margin-top','margin-bottom','margin-left','margin-right','display','width','height','border-radius','border-width','border-style','white-space','position','top','bottom','left','right','z-index','overflow','list-style-type','object-fit'];
for (var i = 0; i < origEls.length && i < cloneEls.length; i++) {
['::before','::after'].forEach(function(pseudo) {
try {
var cs = getComputedStyle(origEls[i], pseudo);
var content = cs.getPropertyValue('content');
if (!content || content === 'none' || content === 'normal') return;
var el = document.createElement('span');
el.style.cssText = 'display:block;margin:0;padding:0;border:none;background:none;';
for (var j = 0; j < pseudoProps.length; j++) {
var v = cs.getPropertyValue(pseudoProps[j]);
if (v && v !== 'normal' && v !== 'auto') el.style.setProperty(pseudoProps[j], v);
}
el.textContent = content.replace(/^["']|["']$/g, '');
if (pseudo === '::before') cloneEls[i].prepend(el);
else cloneEls[i].appendChild(el);
} catch (e) {}
});
}
}
photoReady.then(function (dataUrl) {
if (dataUrl && photo) photo.setAttribute('src', dataUrl);
return document.fonts ? document.fonts.ready : Promise.resolve();
})
.then(function () {
return loadScript(CDN);
var styles = [];
document.querySelectorAll('link[rel="stylesheet"]').forEach(function (l) {
styles.push('<link rel="stylesheet" href="' + l.href + '">');
});
document.querySelectorAll('style').forEach(function (s) {
styles.push('<style>' + s.textContent + '</style>');
});
var win = window.open('', '_blank');
if (!win) { throw new Error('Popup blocked'); }
win.document.write(
'<!DOCTYPE html><html><head><meta charset="utf-8">' +
styles.join('') +
'<style>@media print{body{margin:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;}}</style>' +
'</head><body>' + doc.outerHTML + '</body></html>'
);
win.document.close();
win.onload = function () {
setTimeout(function () {
win.print();
if (origSrc && photo) photo.setAttribute('src', origSrc);
busy = false;
btn.removeAttribute('data-loading');
}, 500);
};
})
.then(function () {
return html2pdf().from(doc).set({
margin: 10,
filename: 'Mamadaev_Resume.pdf',
image: { type: 'jpeg', quality: 0.95 },
html2canvas: {
scale: 2,
useCORS: true,
backgroundColor: '#fff',
onclone: function (clonedDoc) {
var clonedEl = clonedDoc.querySelector('.resume-doc');
if (!clonedEl) return;
resolveStylesInClone(clonedDoc, doc);
}
},
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' }
}).save();
})
.then(function () {
if (origSrc && photo) photo.setAttribute('src', origSrc);
busy = false;
btn.removeAttribute('data-loading');
})
.catch(function (e) {
console.error('[PDF] error:', e.message || e, e.stack || '');
.catch(function () {
if (origSrc && photo) photo.setAttribute('src', origSrc);
busy = false;
btn.removeAttribute('data-loading');