@@ -65,6 +65,8 @@ export interface RendererOptions {
6565 fontCjk : string ; // base64 — 思源宋体(中文正文)
6666 fontSans : string ; // base64 — Inter(正文英文/数字,无衬线比例字体)
6767 fontMono : string ; // base64 — JetBrains Mono(代码/行内代码,等宽)
68+ fontCjkBold : string ; // base64 — 思源宋体 700(中文加粗)
69+ fontSansBold : string ; // base64 — Inter 700(英文/数字加粗)
6870 courseDir : string ;
6971}
7072
@@ -85,7 +87,14 @@ export class PdfRenderer {
8587 /** 单元格默认字色(标题渲染时临时覆盖)。 */
8688 private _cellDefaultColor : [ number , number , number ] | null = null ;
8789
88- constructor ( { fontCjk, fontSans, fontMono, courseDir } : RendererOptions ) {
90+ constructor ( {
91+ fontCjk,
92+ fontSans,
93+ fontMono,
94+ fontCjkBold,
95+ fontSansBold,
96+ courseDir,
97+ } : RendererOptions ) {
8998 this . courseDir = courseDir ;
9099 this . doc = new jsPDF ( { unit : "mm" , format : "a4" } ) ;
91100 // 三字体(均为 glyf TrueType,jsPDF 可解析):
@@ -98,6 +107,13 @@ export class PdfRenderer {
98107 this . doc . addFont ( "Sans.ttf" , "Sans" , "normal" ) ;
99108 this . doc . addFileToVFS ( "Mono.ttf" , fontMono ) ;
100109 this . doc . addFont ( "Mono.ttf" , "Mono" , "normal" ) ;
110+ // 真粗体字型(wght:700):注册为同名字体族的 "bold" 风格,setFont(name, "bold") 即可切换。
111+ // 关键:用真粗体字形后 getTextWidth 返回粗体自身的 advance 宽度,排版按真实宽度推进,
112+ // 从根本上消除描边伪粗体导致的中文字符重叠/行距挤压(与 EPUB 端真粗体方案一致)。
113+ this . doc . addFileToVFS ( "CJK-Bold.ttf" , fontCjkBold ) ;
114+ this . doc . addFont ( "CJK-Bold.ttf" , "CJK" , "bold" ) ;
115+ this . doc . addFileToVFS ( "Sans-Bold.ttf" , fontSansBold ) ;
116+ this . doc . addFont ( "Sans-Bold.ttf" , "Sans" , "bold" ) ;
101117 this . doc . setFont ( "CJK" , "normal" ) ;
102118
103119 this . y = MARGIN . top ;
@@ -323,7 +339,10 @@ export class PdfRenderer {
323339 // CJK 走 CJK 字体;正文英文走无衬线 Sans;行内代码走等宽 Mono
324340 const isCjkPiece = this . isCjk ( piece [ 0 ] || "" ) ;
325341 const fn = isCode ? "Mono" : isCjkPiece ? cjkFont : "Sans" ;
326- this . doc . setFont ( fn , "normal" ) ;
342+ // 加粗用真粗体字型(仅 CJK/Sans 有 bold 变体;Mono 行内代码保持 normal)。
343+ // 用 bold 字体测宽,getTextWidth 返回粗体真实 advance,排版按真实宽度推进。
344+ const style = bold && fn !== "Mono" ? "bold" : "normal" ;
345+ this . doc . setFont ( fn , style ) ;
327346 const w = this . doc . getTextWidth ( piece ) ;
328347 if ( x + w > startX + maxW && piece !== " " ) {
329348 x = startX ;
@@ -349,17 +368,8 @@ export class PdfRenderer {
349368 }
350369 this . doc . setTextColor ( 20 , 90 , 200 ) ;
351370 }
352- if ( ! this . _dry ) {
353- if ( bold ) {
354- // 伪粗体:用填充 + 描边模式加粗笔画(无需额外 bold 字体)
355- const dc = link ? [ 20 , 90 , 200 ] : [ 30 , 30 , 30 ] ;
356- this . doc . setDrawColor ( dc [ 0 ] , dc [ 1 ] , dc [ 2 ] ) ;
357- this . doc . setLineWidth ( 0.25 ) ;
358- this . doc . text ( piece , x , curY , { renderingMode : "fillThenStroke" } ) ;
359- } else {
360- this . doc . text ( piece , x , curY ) ;
361- }
362- }
371+ // 用真粗体字形绘制(style 已按 bold 设好),不再使用描边伪粗体。
372+ if ( ! this . _dry ) this . doc . text ( piece , x , curY ) ;
363373 if ( link && ! this . _dry ) this . doc . setTextColor ( 30 , 30 , 30 ) ;
364374 x += w ;
365375 }
0 commit comments