马一丁

Solving the Problem of Garbled Characters in PDF Rendering

@@ -259,6 +259,10 @@ class HTMLRenderer: @@ -259,6 +259,10 @@ class HTMLRenderer:
259 jspdf_tag = f"<script>{jspdf}</script>" if jspdf else '<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>' 259 jspdf_tag = f"<script>{jspdf}</script>" if jspdf else '<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>'
260 mathjax_tag = f"<script defer>{mathjax}</script>" if mathjax else '<script defer src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>' 260 mathjax_tag = f"<script defer>{mathjax}</script>" if mathjax else '<script defer src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>'
261 261
  262 + # 加载PDF字体数据
  263 + pdf_font_data = self._load_pdf_font_data()
  264 + pdf_font_script = f"<script>window.pdfFontData = '{pdf_font_data}';</script>" if pdf_font_data else ""
  265 +
262 return f""" 266 return f"""
263 <head> 267 <head>
264 <meta charset="utf-8" /> 268 <meta charset="utf-8" />
@@ -282,6 +286,7 @@ class HTMLRenderer: @@ -282,6 +286,7 @@ class HTMLRenderer:
282 }}; 286 }};
283 </script> 287 </script>
284 {mathjax_tag} 288 {mathjax_tag}
  289 + {pdf_font_script}
285 <style> 290 <style>
286 {css} 291 {css}
287 </style> 292 </style>
@@ -2353,6 +2358,7 @@ pre.code-block {{ @@ -2353,6 +2358,7 @@ pre.code-block {{
2353 main {{ 2358 main {{
2354 box-shadow: none; 2359 box-shadow: none;
2355 margin: 0; 2360 margin: 0;
  2361 + max-width: 100%;
2356 }} 2362 }}
2357 .chapter > *, 2363 .chapter > *,
2358 .hero-section, 2364 .hero-section,
@@ -2364,6 +2370,7 @@ figure, @@ -2364,6 +2370,7 @@ figure,
2364 blockquote {{ 2370 blockquote {{
2365 break-inside: avoid; 2371 break-inside: avoid;
2366 page-break-inside: avoid; 2372 page-break-inside: avoid;
  2373 + max-width: 100%;
2367 }} 2374 }}
2368 .chapter h2, 2375 .chapter h2,
2369 .chapter h3, 2376 .chapter h3,
@@ -2375,18 +2382,37 @@ blockquote {{ @@ -2375,18 +2382,37 @@ blockquote {{
2375 .chart-card, 2382 .chart-card,
2376 .table-wrap {{ 2383 .table-wrap {{
2377 overflow: visible !important; 2384 overflow: visible !important;
  2385 + max-width: 100% !important;
  2386 + box-sizing: border-box;
2378 }} 2387 }}
2379 .chart-card canvas {{ 2388 .chart-card canvas {{
2380 width: 100% !important; 2389 width: 100% !important;
2381 height: auto !important; 2390 height: auto !important;
  2391 + max-width: 100% !important;
  2392 +}}
  2393 +.table-wrap {{
  2394 + overflow-x: auto;
  2395 + max-width: 100%;
2382 }} 2396 }}
2383 .table-wrap table {{ 2397 .table-wrap table {{
2384 table-layout: fixed; 2398 table-layout: fixed;
2385 width: 100%; 2399 width: 100%;
  2400 + max-width: 100%;
2386 }} 2401 }}
2387 .table-wrap table th, 2402 .table-wrap table th,
2388 .table-wrap table td {{ 2403 .table-wrap table td {{
2389 word-break: break-word; 2404 word-break: break-word;
  2405 + overflow-wrap: break-word;
  2406 +}}
  2407 +/* 防止图片和图表溢出 */
  2408 +img, canvas, svg {{
  2409 + max-width: 100% !important;
  2410 + height: auto !important;
  2411 +}}
  2412 +/* 确保所有容器不超出页面宽度 */
  2413 +* {{
  2414 + box-sizing: border-box;
  2415 + max-width: 100%;
2390 }} 2416 }}
2391 }} 2417 }}
2392 """ 2418 """
@@ -2858,6 +2884,9 @@ function exportPdf() { @@ -2858,6 +2884,9 @@ function exportPdf() {
2858 pdf.addFileToVFS('SourceHanSerifSC-Medium.otf', window.pdfFontData); 2884 pdf.addFileToVFS('SourceHanSerifSC-Medium.otf', window.pdfFontData);
2859 pdf.addFont('SourceHanSerifSC-Medium.otf', 'SourceHanSerif', 'normal'); 2885 pdf.addFont('SourceHanSerifSC-Medium.otf', 'SourceHanSerif', 'normal');
2860 pdf.setFont('SourceHanSerif'); 2886 pdf.setFont('SourceHanSerif');
  2887 + console.log('PDF字体已成功加载');
  2888 + } else {
  2889 + console.warn('PDF字体数据未找到,将使用默认字体');
2861 } 2890 }
2862 } catch (err) { 2891 } catch (err) {
2863 console.warn('Custom PDF font setup failed, fallback to default', err); 2892 console.warn('Custom PDF font setup failed, fallback to default', err);
@@ -2890,11 +2919,13 @@ function exportPdf() { @@ -2890,11 +2919,13 @@ function exportPdf() {
2890 autoPaging: 'text', 2919 autoPaging: 'text',
2891 windowWidth: pxWidth, 2920 windowWidth: pxWidth,
2892 html2canvas: { 2921 html2canvas: {
2893 - scale: Math.min(1.2, Math.max(0.8, pageWidth / (target.clientWidth || pageWidth))), 2922 + scale: Math.min(1.5, Math.max(1.0, pageWidth / (target.clientWidth || pageWidth))),
2894 useCORS: true, 2923 useCORS: true,
2895 scrollX: 0, 2924 scrollX: 0,
2896 scrollY: -window.scrollY, 2925 scrollY: -window.scrollY,
2897 - logging: false 2926 + logging: false,
  2927 + allowTaint: true,
  2928 + backgroundColor: '#ffffff'
2898 }, 2929 },
2899 pagebreak: { 2930 pagebreak: {
2900 mode: ['css', 'legacy'], 2931 mode: ['css', 'legacy'],
@@ -3867,6 +3867,22 @@ @@ -3867,6 +3867,22 @@
3867 throw new Error('PDF依赖未加载'); 3867 throw new Error('PDF依赖未加载');
3868 } 3868 }
3869 const pdf = new jsPDF('p', 'mm', 'a4'); 3869 const pdf = new jsPDF('p', 'mm', 'a4');
  3870 +
  3871 + // 添加中文字体支持
  3872 + try {
  3873 + const fontData = iframe.contentWindow.pdfFontData || window.pdfFontData;
  3874 + if (fontData) {
  3875 + pdf.addFileToVFS('SourceHanSerifSC-Medium.otf', fontData);
  3876 + pdf.addFont('SourceHanSerifSC-Medium.otf', 'SourceHanSerif', 'normal');
  3877 + pdf.setFont('SourceHanSerif');
  3878 + console.log('PDF字体已加载:SourceHanSerif');
  3879 + } else {
  3880 + console.warn('PDF字体数据未找到,将使用默认字体');
  3881 + }
  3882 + } catch (fontErr) {
  3883 + console.warn('PDF字体加载失败:', fontErr);
  3884 + }
  3885 +
3870 const pageWidth = pdf.internal.pageSize.getWidth(); 3886 const pageWidth = pdf.internal.pageSize.getWidth();
3871 const pxWidth = Math.max(target.scrollWidth || 0, Math.round(pageWidth * 3.78)); 3887 const pxWidth = Math.max(target.scrollWidth || 0, Math.round(pageWidth * 3.78));
3872 const renderTask = pdf.html(target, { 3888 const renderTask = pdf.html(target, {
@@ -3877,11 +3893,13 @@ @@ -3877,11 +3893,13 @@
3877 margin: [10, 10, 16, 10], 3893 margin: [10, 10, 16, 10],
3878 autoPaging: 'text', 3894 autoPaging: 'text',
3879 html2canvas: { 3895 html2canvas: {
3880 - scale: Math.min(1.2, Math.max(0.8, pageWidth / (target.clientWidth || pageWidth))), 3896 + scale: Math.min(1.5, Math.max(1.0, pageWidth / (target.clientWidth || pageWidth))),
3881 useCORS: true, 3897 useCORS: true,
3882 scrollX: 0, 3898 scrollX: 0,
3883 scrollY: -iframe.contentWindow.scrollY, 3899 scrollY: -iframe.contentWindow.scrollY,
3884 - logging: false 3900 + logging: false,
  3901 + allowTaint: true,
  3902 + backgroundColor: '#ffffff'
3885 }, 3903 },
3886 pagebreak: { 3904 pagebreak: {
3887 mode: ['css', 'legacy'], 3905 mode: ['css', 'legacy'],