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:
1 parent
e00487aa15
commit
319c23e69e
2 files changed
+54
-178
No files matched your search
+27
-89
@@ -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
@@ -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');
|
||||
|
||||
Reference in new issue
Block a user