@@ -10,8 +10,11 @@ const popupPorts = new Set();
1010const IMAGE_FETCH_TIMEOUT_MS = 12000 ;
1111const FRAME_COLLECTION_TIMEOUT_MS = 45000 ;
1212const PRECOMPUTED_IR_TRANSFER_MAX_BYTES = 8 * 1024 * 1024 ;
13+ const FONT_ASSET_FORMATS = new Set ( [ 'html' , 'svg' , 'pdf' ] ) ;
1314const DEFAULT_EXPORT_OPTIONS = Object . freeze ( {
1415 rootScrollBehavior : 'clip' ,
16+ embedFonts : true ,
17+ pdfUseFontEditorCore : true ,
1518} ) ;
1619
1720const FRAME_EXTRACTION_OPTIONS = {
@@ -127,27 +130,35 @@ async function startExport(tabId, format, exportOptions = DEFAULT_EXPORT_OPTIONS
127130
128131 try {
129132 const normalizedOptions = normalizeExportOptions ( exportOptions ) ;
130- const mergedIr = selectPrecomputedIrForTransfer ( await collectMergedIrForTab ( tabId , normalizedOptions ) ) ;
133+ const precomputedExport = selectPrecomputedExportForTransfer (
134+ await collectMergedIrForTab ( tabId , format , normalizedOptions )
135+ ) ;
131136
132137 // 1. Set the requested format and precomputed IR in the content-script world
133138 await extensionApi . scripting . executeScript ( {
134139 target : { tabId } ,
135- func : ( f , ir , options ) => {
140+ func : ( f , precomputed , options ) => {
136141 globalThis . __web2vector_format = f ;
137142
138- if ( Array . isArray ( ir ) ) {
139- globalThis . __web2vector_precomputed_ir = ir ;
143+ if ( Array . isArray ( precomputed ?. ir ) ) {
144+ globalThis . __web2vector_precomputed_ir = precomputed . ir ;
140145 } else {
141146 delete globalThis . __web2vector_precomputed_ir ;
142147 }
143148
149+ if ( precomputed ?. fontAssets && Array . isArray ( precomputed . fontAssets . faces ) ) {
150+ globalThis . __web2vector_precomputed_font_assets = precomputed . fontAssets ;
151+ } else {
152+ delete globalThis . __web2vector_precomputed_font_assets ;
153+ }
154+
144155 if ( options && typeof options === 'object' ) {
145156 globalThis . __web2vector_export_options = options ;
146157 } else {
147158 delete globalThis . __web2vector_export_options ;
148159 }
149160 } ,
150- args : [ format , mergedIr , normalizedOptions ] ,
161+ args : [ format , precomputedExport , normalizedOptions ] ,
151162 } ) ;
152163
153164 // 2. Lazy-load writer bundle when required
@@ -173,9 +184,11 @@ async function startExport(tabId, format, exportOptions = DEFAULT_EXPORT_OPTIONS
173184 }
174185}
175186
176- async function collectMergedIrForTab ( tabId , exportOptions = DEFAULT_EXPORT_OPTIONS ) {
187+ async function collectMergedIrForTab ( tabId , format , exportOptions = DEFAULT_EXPORT_OPTIONS ) {
177188 try {
178189 return await withTimeout ( ( async ( ) => {
190+ const normalizedOptions = normalizeExportOptions ( exportOptions ) ;
191+
179192 await extensionApi . scripting . executeScript ( {
180193 target : { tabId, allFrames : true } ,
181194 files : [ 'core-lib.js' , 'frame-support.js' ] ,
@@ -191,11 +204,12 @@ async function collectMergedIrForTab(tabId, exportOptions = DEFAULT_EXPORT_OPTIO
191204 func : ( options ) => globalThis . __web2vectorFrameSupport ?. collectFrameData ?. ( options ) ?? null ,
192205 args : [ {
193206 ...FRAME_EXTRACTION_OPTIONS ,
194- rootScrollBehavior : normalizeExportOptions ( exportOptions ) . rootScrollBehavior ,
207+ includeFonts : shouldCollectFontAssets ( format , normalizedOptions ) ,
208+ rootScrollBehavior : normalizedOptions . rootScrollBehavior ,
195209 } ] ,
196210 } ) ;
197211
198- return mergeFrameExtractionResults ( frameResults , { rootFrameId : 0 } ) ?. ir ?? null ;
212+ return mergeFrameExtractionResults ( frameResults , { rootFrameId : 0 } ) ;
199213 } ) ( ) , FRAME_COLLECTION_TIMEOUT_MS , ( ) => {
200214 console . warn ( '[Web2Vector] Timed out while collecting merged frame IR; falling back to in-page extraction.' ) ;
201215 return null ;
@@ -205,12 +219,12 @@ async function collectMergedIrForTab(tabId, exportOptions = DEFAULT_EXPORT_OPTIO
205219 }
206220}
207221
208- function selectPrecomputedIrForTransfer ( ir ) {
209- if ( ! Array . isArray ( ir ) ) return null ;
222+ function selectPrecomputedExportForTransfer ( precomputedExport ) {
223+ if ( ! Array . isArray ( precomputedExport ?. ir ) ) return null ;
210224
211- const estimatedBytes = estimateSerializedSize ( ir ) ;
225+ const estimatedBytes = estimateTransferSize ( precomputedExport ) ;
212226 if ( estimatedBytes <= PRECOMPUTED_IR_TRANSFER_MAX_BYTES ) {
213- return ir ;
227+ return precomputedExport ;
214228 }
215229
216230 console . warn (
@@ -219,20 +233,54 @@ function selectPrecomputedIrForTransfer(ir) {
219233 return null ;
220234}
221235
222- function estimateSerializedSize ( value ) {
223- try {
224- return JSON . stringify ( value ) ?. length ?? Number . POSITIVE_INFINITY ;
225- } catch {
226- return Number . POSITIVE_INFINITY ;
236+ function estimateTransferSize ( value , seen = new Set ( ) ) {
237+ if ( value == null ) return 0 ;
238+
239+ const valueType = typeof value ;
240+ if ( valueType === 'string' ) return value . length ;
241+ if ( valueType === 'number' ) return 8 ;
242+ if ( valueType === 'boolean' ) return 4 ;
243+
244+ if ( ArrayBuffer . isView ( value ) ) {
245+ return value . byteLength ;
227246 }
247+
248+ if ( value instanceof ArrayBuffer ) {
249+ return value . byteLength ;
250+ }
251+
252+ if ( valueType !== 'object' ) {
253+ return 0 ;
254+ }
255+
256+ if ( seen . has ( value ) ) {
257+ return 0 ;
258+ }
259+
260+ seen . add ( value ) ;
261+
262+ if ( Array . isArray ( value ) ) {
263+ return value . reduce ( ( total , entry ) => total + estimateTransferSize ( entry , seen ) , 0 ) ;
264+ }
265+
266+ return Object . entries ( value ) . reduce (
267+ ( total , [ key , entry ] ) => total + key . length + estimateTransferSize ( entry , seen ) ,
268+ 0 ,
269+ ) ;
228270}
229271
230272function normalizeExportOptions ( options ) {
231273 return {
232274 rootScrollBehavior : options ?. rootScrollBehavior === 'expand' ? 'expand' : 'clip' ,
275+ embedFonts : options ?. embedFonts !== false ,
276+ pdfUseFontEditorCore : options ?. pdfUseFontEditorCore !== false ,
233277 } ;
234278}
235279
280+ function shouldCollectFontAssets ( format , exportOptions ) {
281+ return FONT_ASSET_FORMATS . has ( format ) && exportOptions ?. embedFonts !== false ;
282+ }
283+
236284async function withTimeout ( promise , timeoutMs , onTimeout ) {
237285 let timeoutId = null ;
238286
0 commit comments