@@ -225,7 +225,7 @@ fn mm_to_mil_10(val: f64) -> f64 {
225225}
226226
227227fn compute_mark_points ( bounds : & BoardBounds ) -> Vec < ( f64 , f64 ) > {
228- // Inset by approximately 3 mil to mirror the original script behaviour.
228+ // Inset by approximately 3 mil (0.0762mm) to mirror the original script behaviour.
229229 const INSET_MM : f64 = 0.0762 ;
230230 let min_x = bounds. min_x + INSET_MM ;
231231 let max_x = bounds. max_x - INSET_MM ;
@@ -236,26 +236,37 @@ fn compute_mark_points(bounds: &BoardBounds) -> Vec<(f64, f64)> {
236236}
237237
238238fn build_bottom_svg ( bounds : & BoardBounds , image : & SilkscreenImage ) -> String {
239- let ox = mm_to_mil_10 ( bounds. origin_x ) ;
240- let oy = mm_to_mil_10 ( bounds. origin_y ) ;
239+ const CLIP_MARGIN : f64 = 0.8374 ; // shrink (10 mil units)
240+ const EXPAND : f64 = 0.5 ; // expand (10 mil units)
241+
242+ let min_x = mm_to_mil_10 ( bounds. min_x ) ;
243+ let max_x = mm_to_mil_10 ( bounds. max_x ) ;
244+ // Y axis in the reference SVG is inverted: use -max_y as the min value and
245+ // -min_y as the max value, while keeping width/height unchanged.
246+ let min_y = -mm_to_mil_10 ( bounds. max_y ) ;
247+ let max_y = -mm_to_mil_10 ( bounds. min_y ) ;
241248 let w = mm_to_mil_10 ( bounds. width ) ;
242249 let h = mm_to_mil_10 ( bounds. height ) ;
250+ let center_x = min_x + w / 2.0 ;
243251 let image_w = image. width ;
244252 let image_h = image. height ;
245253
254+ let mark_points = compute_mark_points ( bounds)
255+ . iter ( )
256+ . flat_map ( |( x, y) | [ mm_to_mil_10 ( * x) . to_string ( ) , mm_to_mil_10 ( * y) . to_string ( ) ] )
257+ . collect :: < Vec < _ > > ( )
258+ . join ( " " ) ;
259+
246260 let mut writer = create_writer ( ) ;
247261
248262 writer. start_element ( "svg" ) ;
249263 writer. write_attribute ( "width" , & format ! ( "{}mm" , bounds. width) ) ;
250264 writer. write_attribute ( "height" , & format ! ( "{}mm" , bounds. height) ) ;
251- writer. write_attribute ( "boardBox" , & format ! ( "{} {} {} {}" , ox , oy + h , w , h ) ) ;
252- writer. write_attribute ( "viewBox" , & format ! ( "{} {} {} {}" , ox , oy + h , w , h ) ) ;
265+ writer. write_attribute ( "boardBox" , & format ! ( "{min_x } {min_y } {w } {h}" ) ) ;
266+ writer. write_attribute ( "viewBox" , & format ! ( "{min_x } {min_y } {w } {h}" ) ) ;
253267 writer. write_attribute ( "version" , "1.1" ) ;
254268 writer. write_attribute ( "eda-version" , "1.6(2025-08-27)" ) ;
255- writer. write_attribute (
256- "mark-points" ,
257- "-115.11024 -36.37008 -115.11024 154.48031000000003 193.85038999999998 154.48031" ,
258- ) ;
269+ writer. write_attribute ( "mark-points" , & mark_points) ;
259270 writer. write_attribute (
260271 "xmlns:inkscape" ,
261272 "http://www.inkscape.org/namespaces/inkscape" ,
@@ -268,7 +279,7 @@ fn build_bottom_svg(bounds: &BoardBounds, image: &SilkscreenImage) -> String {
268279 writer. write_attribute ( "xmlns" , "http://www.w3.org/2000/svg" ) ;
269280 writer. write_attribute ( "xmlns:svg" , "http://www.w3.org/2000/svg" ) ;
270281
271- // defs/clipPath 0
282+ // clipPath0 shrink
272283 writer. start_element ( "defs" ) ;
273284 writer. start_element ( "clipPath" ) ;
274285 writer. write_attribute ( "id" , "clipPath0" ) ;
@@ -277,26 +288,26 @@ fn build_bottom_svg(bounds: &BoardBounds, image: &SilkscreenImage) -> String {
277288 "d" ,
278289 & format ! (
279290 "M {} {} L {} {} {} {} {} {} {} {} " ,
280- ox + w - 0.05 ,
281- -oy - 0.05 , // svg中y轴向下递增
282- ox + 0.05 ,
283- -oy - 0.05 ,
284- ox + 0.05 ,
285- - ( oy + h ) - 0.05 ,
286- ox + w - 0.05 ,
287- - ( oy + h ) - 0.05 ,
288- ox + w - 0.05 ,
289- -oy - 0.05 ,
291+ max_x - CLIP_MARGIN ,
292+ max_y - CLIP_MARGIN ,
293+ min_x + CLIP_MARGIN ,
294+ max_y - CLIP_MARGIN ,
295+ min_x + CLIP_MARGIN ,
296+ min_y + CLIP_MARGIN ,
297+ max_x - CLIP_MARGIN ,
298+ min_y + CLIP_MARGIN ,
299+ max_x - CLIP_MARGIN ,
300+ max_y - CLIP_MARGIN
290301 ) ,
291302 ) ;
292303 writer. write_attribute ( "id" , "outline0" ) ;
293304 writer. write_attribute ( "stroke" , "none" ) ;
294305 writer. write_attribute ( "style" , "fill-opacity:1;fill-rule:nonzero;fill:block;" ) ;
295- writer. end_element ( ) ; // path
296- writer. end_element ( ) ; // clipPath
297- writer. end_element ( ) ; // defs
306+ writer. end_element ( ) ;
307+ writer. end_element ( ) ;
308+ writer. end_element ( ) ;
298309
299- // defs/clipPath 1
310+ // clipPath1 expand
300311 writer. start_element ( "defs" ) ;
301312 writer. start_element ( "clipPath" ) ;
302313 writer. write_attribute ( "id" , "clipPath1" ) ;
@@ -306,55 +317,55 @@ fn build_bottom_svg(bounds: &BoardBounds, image: &SilkscreenImage) -> String {
306317 "d" ,
307318 & format ! (
308319 "M {} {} L {} {} {} {} {} {} {} {} " ,
309- ox + w + 0.05 ,
310- - ( oy + h ) + 0.05 , // svg中y轴向下递增
311- ox + w + 0.05 ,
312- -oy + 0.05 ,
313- ox - 0.05 ,
314- -oy + 0.05 ,
315- ox - 0.05 ,
316- - ( oy + h ) + 0.05 ,
317- ox + w + 0.05 ,
318- - ( oy + h ) + 0.05 ,
320+ max_x + EXPAND ,
321+ min_y - EXPAND ,
322+ max_x + EXPAND ,
323+ max_y + EXPAND ,
324+ min_x - EXPAND ,
325+ max_y + EXPAND ,
326+ min_x - EXPAND ,
327+ min_y - EXPAND ,
328+ max_x + EXPAND ,
329+ min_y - EXPAND
319330 ) ,
320331 ) ;
321332 writer. write_attribute ( "id" , "solder1" ) ;
322333 writer. write_attribute ( "stroke" , "none" ) ;
323334 writer. write_attribute ( "style" , "fill-opacity:1;fill-rule:nonzero;fill:block;" ) ;
324- writer. end_element ( ) ; // path
325- writer. end_element ( ) ; // clipPath
326- writer. end_element ( ) ; // defs
335+ writer. end_element ( ) ;
336+ writer. end_element ( ) ;
337+ writer. end_element ( ) ;
327338
328- // main group
339+ // main group (mirrored)
329340 writer. start_element ( "g" ) ;
330341 writer. write_attribute ( "clip-path" , "url(#clipPath1)" ) ;
331342 writer. write_attribute (
332343 "transform" ,
333- & format ! ( "scale(-1 1) translate({} 0)" , -( ( oy + h ) / 2f64 ) ) ,
344+ & format ! ( "scale(-1 1) translate({} 0)" , -2.0 * center_x ) ,
334345 ) ;
335346
336347 writer. start_element ( "path" ) ;
337348 writer. write_attribute (
338349 "d" ,
339350 & format ! (
340351 "M {} {} L {} {} {} {} {} {} {} {} " ,
341- ox - 0.05 ,
342- -oy + 0.05 ,
343- ox - 0.05 ,
344- - ( oy + h ) + 0.05 ,
345- ox + w + 0.05 ,
346- - ( oy + h ) + 0.05 ,
347- ox + w + 0.05 ,
348- -oy + 0.05 ,
349- ox - 0.05 ,
350- -oy + 0.05 ,
352+ min_x - EXPAND ,
353+ max_y + EXPAND ,
354+ min_x - EXPAND ,
355+ min_y - EXPAND ,
356+ max_x + EXPAND ,
357+ min_y - EXPAND ,
358+ max_x + EXPAND ,
359+ max_y + EXPAND ,
360+ min_x - EXPAND ,
361+ max_y + EXPAND
351362 ) ,
352363 ) ;
353364 writer. write_attribute ( "fill" , "#FFFFFF" ) ;
354365 writer. write_attribute ( "stroke" , "none" ) ;
355366 writer. write_attribute ( "stroke-width" , "0" ) ;
356367 writer. write_attribute ( "id" , "background" ) ;
357- writer. end_element ( ) ; // path
368+ writer. end_element ( ) ;
358369
359370 writer. start_element ( "image" ) ;
360371 writer. write_attribute ( "width" , & image_w. to_string ( ) ) ;
@@ -367,8 +378,8 @@ fn build_bottom_svg(bounds: &BoardBounds, image: &SilkscreenImage) -> String {
367378 "matrix({} 0 0 {} {} {})" ,
368379 -( w / image_w as f64 ) ,
369380 h / image_h as f64 ,
370- h ,
371- - ( oy + h )
381+ max_x ,
382+ min_y
372383 ) ,
373384 ) ;
374385 writer. end_element ( ) ; // image
@@ -460,26 +471,35 @@ fn format_coord(x_mm: f64, y_mm: f64) -> (String, String) {
460471}
461472
462473fn build_top_svg ( bounds : & BoardBounds , image : & SilkscreenImage ) -> String {
463- let ox = mm_to_mil_10 ( bounds. origin_x ) ;
464- let oy = mm_to_mil_10 ( bounds. origin_y ) ;
474+ const CLIP_MARGIN : f64 = 0.8374 ; // in 10-mil units
475+ const EXPAND : f64 = 0.5 ; // in 10-mil units
476+
477+ // Convert mm to 10-mil units (as used by sample SVGs)
478+ let min_x = mm_to_mil_10 ( bounds. min_x ) ;
479+ let max_x = mm_to_mil_10 ( bounds. max_x ) ;
480+ let min_y = -mm_to_mil_10 ( bounds. max_y ) ;
481+ let max_y = -mm_to_mil_10 ( bounds. min_y ) ;
465482 let w = mm_to_mil_10 ( bounds. width ) ;
466483 let h = mm_to_mil_10 ( bounds. height ) ;
467484 let image_w = image. width ;
468485 let image_h = image. height ;
469486
487+ let mark_points = compute_mark_points ( bounds)
488+ . iter ( )
489+ . flat_map ( |( x, y) | [ mm_to_mil_10 ( * x) . to_string ( ) , mm_to_mil_10 ( * y) . to_string ( ) ] )
490+ . collect :: < Vec < _ > > ( )
491+ . join ( " " ) ;
492+
470493 let mut writer = create_writer ( ) ;
471494
472495 writer. start_element ( "svg" ) ;
473496 writer. write_attribute ( "width" , & format ! ( "{}mm" , bounds. width) ) ;
474497 writer. write_attribute ( "height" , & format ! ( "{}mm" , bounds. height) ) ;
475- writer. write_attribute ( "boardBox" , & format ! ( "{} {} {} {}" , ox , - ( oy + h ) , w , h ) ) ;
476- writer. write_attribute ( "viewBox" , & format ! ( "{} {} {} {}" , ox , - ( oy + h ) , w , h ) ) ;
498+ writer. write_attribute ( "boardBox" , & format ! ( "{min_x } {min_y } {w } {h}" ) ) ;
499+ writer. write_attribute ( "viewBox" , & format ! ( "{min_x } {min_y } {w } {h}" ) ) ;
477500 writer. write_attribute ( "version" , "1.1" ) ;
478501 writer. write_attribute ( "eda-version" , "1.6(2025-08-27)" ) ;
479- writer. write_attribute (
480- "mark-points" ,
481- "-115.11024 -36.37008 -115.11024 154.48031000000003 193.85038999999998 154.48031" ,
482- ) ;
502+ writer. write_attribute ( "mark-points" , & mark_points) ;
483503 writer. write_attribute (
484504 "xmlns:inkscape" ,
485505 "http://www.inkscape.org/namespaces/inkscape" ,
@@ -492,7 +512,6 @@ fn build_top_svg(bounds: &BoardBounds, image: &SilkscreenImage) -> String {
492512 writer. write_attribute ( "xmlns" , "http://www.w3.org/2000/svg" ) ;
493513 writer. write_attribute ( "xmlns:svg" , "http://www.w3.org/2000/svg" ) ;
494514
495- // defs/clipPath 0
496515 writer. start_element ( "defs" ) ;
497516 writer. start_element ( "clipPath" ) ;
498517 writer. write_attribute ( "id" , "clipPath0" ) ;
@@ -501,16 +520,16 @@ fn build_top_svg(bounds: &BoardBounds, image: &SilkscreenImage) -> String {
501520 "d" ,
502521 & format ! (
503522 "M {} {} L {} {} {} {} {} {} {} {} " ,
504- ox + w - 0.05 ,
505- -oy - 0.05 , // svg中y轴向下递增
506- ox + 0.05 ,
507- -oy - 0.05 ,
508- ox + 0.05 ,
509- - ( oy + h ) - 0.05 ,
510- ox + w - 0.05 ,
511- - ( oy + h ) - 0.05 ,
512- ox + w - 0.05 ,
513- -oy - 0.05 ,
523+ max_x - CLIP_MARGIN ,
524+ max_y - CLIP_MARGIN ,
525+ min_x + CLIP_MARGIN ,
526+ max_y - CLIP_MARGIN ,
527+ min_x + CLIP_MARGIN ,
528+ min_y + CLIP_MARGIN ,
529+ max_x - CLIP_MARGIN ,
530+ min_y + CLIP_MARGIN ,
531+ max_x - CLIP_MARGIN ,
532+ max_y - CLIP_MARGIN
514533 ) ,
515534 ) ;
516535 writer. write_attribute ( "id" , "outline0" ) ;
@@ -520,7 +539,6 @@ fn build_top_svg(bounds: &BoardBounds, image: &SilkscreenImage) -> String {
520539 writer. end_element ( ) ; // clipPath
521540 writer. end_element ( ) ; // defs
522541
523- // defs/clipPath 1
524542 writer. start_element ( "defs" ) ;
525543 writer. start_element ( "clipPath" ) ;
526544 writer. write_attribute ( "id" , "clipPath1" ) ;
@@ -530,26 +548,25 @@ fn build_top_svg(bounds: &BoardBounds, image: &SilkscreenImage) -> String {
530548 "d" ,
531549 & format ! (
532550 "M {} {} L {} {} {} {} {} {} {} {} " ,
533- ox + w + 0.05 ,
534- - ( oy + h ) + 0.05 , // svg中y轴向下递增
535- ox + w + 0.05 ,
536- -oy + 0.05 ,
537- ox - 0.05 ,
538- -oy + 0.05 ,
539- ox - 0.05 ,
540- - ( oy + h ) + 0.05 ,
541- ox + w + 0.05 ,
542- - ( oy + h ) + 0.05 ,
551+ max_x + EXPAND ,
552+ min_y - EXPAND ,
553+ max_x + EXPAND ,
554+ max_y + EXPAND ,
555+ min_x - EXPAND ,
556+ max_y + EXPAND ,
557+ min_x - EXPAND ,
558+ min_y - EXPAND ,
559+ max_x + EXPAND ,
560+ min_y - EXPAND
543561 ) ,
544562 ) ;
545563 writer. write_attribute ( "id" , "solder1" ) ;
546564 writer. write_attribute ( "stroke" , "none" ) ;
547- writer. write_attribute ( "style" , "fill-opacity:1;fill-rule:nonzero;fill:block" ) ;
565+ writer. write_attribute ( "style" , "fill-opacity:1;fill-rule:nonzero;fill:block; " ) ;
548566 writer. end_element ( ) ; // path
549567 writer. end_element ( ) ; // clipPath
550568 writer. end_element ( ) ; // defs
551569
552- // main group
553570 writer. start_element ( "g" ) ;
554571 writer. write_attribute ( "clip-path" , "url(#clipPath1)" ) ;
555572 writer. write_attribute ( "transform" , "scale(1 1) translate(0 0)" ) ;
@@ -559,16 +576,16 @@ fn build_top_svg(bounds: &BoardBounds, image: &SilkscreenImage) -> String {
559576 "d" ,
560577 & format ! (
561578 "M {} {} L {} {} {} {} {} {} {} {} " ,
562- ox - 0.05 ,
563- -oy + 0.05 ,
564- ox - 0.05 ,
565- - ( oy + h ) + 0.05 ,
566- ox + w + 0.05 ,
567- - ( oy + h ) + 0.05 ,
568- ox + w + 0.05 ,
569- -oy + 0.05 ,
570- ox - 0.05 ,
571- -oy + 0.05 ,
579+ min_x - EXPAND ,
580+ max_y + EXPAND ,
581+ min_x - EXPAND ,
582+ min_y - EXPAND ,
583+ max_x + EXPAND ,
584+ min_y - EXPAND ,
585+ max_x + EXPAND ,
586+ max_y + EXPAND ,
587+ min_x - EXPAND ,
588+ max_y + EXPAND
572589 ) ,
573590 ) ;
574591 writer. write_attribute ( "fill" , "#FFFFFF" ) ;
@@ -577,19 +594,19 @@ fn build_top_svg(bounds: &BoardBounds, image: &SilkscreenImage) -> String {
577594 writer. write_attribute ( "id" , "background" ) ;
578595 writer. end_element ( ) ; // path
579596
580- writer. start_element ( "image" ) ;
581- writer. write_attribute ( "width" , & image_w. to_string ( ) ) ;
582- writer. write_attribute ( "height" , & image_h. to_string ( ) ) ;
583- writer. write_attribute ( "preserveAspectRatio" , "none" ) ;
584- writer. write_attribute ( "xlink:href" , & image. data_uri ) ;
585- writer. write_attribute (
586- "transform" ,
587- & format ! (
597+ writer. start_element ( "image" ) ;
598+ writer. write_attribute ( "width" , & image_w. to_string ( ) ) ;
599+ writer. write_attribute ( "height" , & image_h. to_string ( ) ) ;
600+ writer. write_attribute ( "preserveAspectRatio" , "none" ) ;
601+ writer. write_attribute ( "xlink:href" , & image. data_uri ) ;
602+ writer. write_attribute (
603+ "transform" ,
604+ & format ! (
588605 "matrix({} 0 0 {} {} {})" ,
589606 w / image_w as f64 ,
590607 h / image_h as f64 ,
591- ox ,
592- - ( oy + h )
608+ min_x ,
609+ min_y
593610 ) ,
594611 ) ;
595612 writer. end_element ( ) ; // image
0 commit comments