@@ -31,11 +31,6 @@ <h2>QSL Card Designer (QSLカードデザイナー)</h2>
3131 < label > QSL Card Background Image (QSLカード背景画像):
3232 < input type ="file " class ="bg-image-input " id ="bgInput " accept ="image/* ">
3333 </ label >
34- < div style ="margin-top:8px; ">
35- < button class ="btn " id ="toggleSizeMode " onclick ="toggleDisplayMode() " style ="width:100%;font-size:11px;padding:4px; ">
36- Mode: Fit to Screen (画面に合わせる)
37- </ button >
38- </ div >
3934 </ div >
4035 < div style ="margin-top:18px; ">
4136 < label > Import ADIF File (ADIFファイルをインポート): < input type ="file " id ="adifInput " accept =".adi,.adif "> </ label >
@@ -62,9 +57,6 @@ <h2>QSL Card Designer (QSLカードデザイナー)</h2>
6257 < div class ="qso-list " id ="qsoListDiv " style ="display:none;margin-top:18px; ">
6358 < label > Select QSO (QSO選択): < br > < select id ="qsoSelect "> </ select > </ label >
6459 </ div >
65- < div style ="margin-top:18px; ">
66- < button class ="btn " onclick ="downloadAsPNG() " style ="width:100%; "> Download as PNG (PNGとしてダウンロード)</ button >
67- </ div >
6860 </ div >
6961 < div class ="designer ">
7062 < div style ="text-align:center; "> < b id ="previewSize "> Preview Area (プレビューエリア) (500×320 px)</ b > </ div >
@@ -73,6 +65,12 @@ <h2>QSL Card Designer (QSLカードデザイナー)</h2>
7365 < img id ="designerBg " alt ="bg " style ="display: none; " />
7466 <!-- movable fields appended here -->
7567 </ div >
68+ < div style ="margin-top:10px;display:flex;gap:10px; ">
69+ < button class ="btn " id ="toggleSizeMode " onclick ="toggleDisplayMode() " style ="flex:1;font-size:11px;padding:6px; ">
70+ Mode: Fit to Screen (画面に合わせる)
71+ </ button >
72+ < button class ="btn " onclick ="downloadAsPNG() " style ="flex:1; "> Download as PNG (PNGとしてダウンロード)</ button >
73+ </ div >
7674 </ div >
7775</ div >
7876
@@ -125,7 +123,8 @@ <h2>QSL Card Designer (QSLカードデザイナー)</h2>
125123] ;
126124let movingFieldIdx = null , offsetX = 0 , offsetY = 0 ;
127125let resizingFieldIdx = null , resizeStartX = 0 , resizeStartY = 0 , startWidth = 0 , startHeight = 0 ;
128- let cardWidth = 500 , cardHeight = 320 ;
126+ let cardWidth = 500 , cardHeight = 320 ; // preview (screen) dimensions
127+ let fullCardWidth = 500 , fullCardHeight = 320 ; // full-resolution output dimensions
129128let displayMode = 'fit-screen' ; // 'fit-screen' or 'full-size'
130129let currentBgImage = null ; // Store current background image
131130
@@ -331,6 +330,8 @@ <h2>QSL Card Designer (QSLカードデザイナー)</h2>
331330 area . classList . remove ( 'has-bg' ) ;
332331 cardWidth = 500 ;
333332 cardHeight = 320 ;
333+ fullCardWidth = 500 ;
334+ fullCardHeight = 320 ;
334335 area . style . width = cardWidth + 'px' ;
335336 area . style . height = cardHeight + 'px' ;
336337 updatePreviewSize ( ) ;
@@ -367,6 +368,10 @@ <h2>QSL Card Designer (QSLカードデザイナー)</h2>
367368 }
368369 }
369370
371+ // Always store full-resolution dimensions for PNG export
372+ fullCardWidth = scaledWidth ;
373+ fullCardHeight = scaledHeight ;
374+
370375 if ( displayMode === 'fit-screen' ) {
371376 // 画面サイズに合わせるモード
372377 const designerRect = document . querySelector ( '.designer' ) . getBoundingClientRect ( ) ;
@@ -631,7 +636,10 @@ <h2>QSL Card Designer (QSLカードデザイナー)</h2>
631636 const sizeText = document . getElementById ( 'previewSize' ) ;
632637 const w = Math . round ( cardWidth ) ;
633638 const h = Math . round ( cardHeight ) ;
634- sizeText . textContent = `Preview Area (プレビューエリア) (${ w } ×${ h } px)` ;
639+ const fw = Math . round ( fullCardWidth ) ;
640+ const fh = Math . round ( fullCardHeight ) ;
641+ const suffix = ( fw !== w || fh !== h ) ? ` → Output: ${ fw } ×${ fh } px` : '' ;
642+ sizeText . textContent = `Preview Area (プレビューエリア) (${ w } ×${ h } px)${ suffix } ` ;
635643}
636644
637645// On window resize (for bg images)
@@ -643,29 +651,33 @@ <h2>QSL Card Designer (QSLカードデザイナー)</h2>
643651 const area = document . getElementById ( 'designerArea' ) ;
644652 const bgImg = document . getElementById ( 'designerBg' ) ;
645653
646- // Canvasを作成
654+ // Canvasを作成(フル解像度で出力)
647655 const canvas = document . createElement ( 'canvas' ) ;
648- canvas . width = cardWidth ;
649- canvas . height = cardHeight ;
656+ canvas . width = fullCardWidth ;
657+ canvas . height = fullCardHeight ;
650658 const ctx = canvas . getContext ( '2d' ) ;
651-
659+
660+ // Scale factor from preview size to full output size
661+ const scaleX = fullCardWidth / cardWidth ;
662+ const scaleY = fullCardHeight / cardHeight ;
663+
652664 // 背景色を白に設定(背景画像がない場合)
653665 ctx . fillStyle = '#eee' ;
654- ctx . fillRect ( 0 , 0 , cardWidth , cardHeight ) ;
655-
666+ ctx . fillRect ( 0 , 0 , fullCardWidth , fullCardHeight ) ;
667+
656668 // 背景画像を描画
657669 if ( bgImg . style . display !== 'none' && bgImg . complete && bgImg . naturalWidth > 0 ) {
658- ctx . drawImage ( bgImg , 0 , 0 , cardWidth , cardHeight ) ;
670+ ctx . drawImage ( bgImg , 0 , 0 , fullCardWidth , fullCardHeight ) ;
659671 }
660-
661- // フィールドを描画
672+
673+ // フィールドを描画(プレビュー座標をフル解像度にスケール)
662674 const rec = adifRecords [ qsoIdx ] || { } ;
663675 designerFields . forEach ( ( f ) => {
664676 const v = ( rec [ f . name ] !== undefined ? rec [ f . name ] : "" ) ;
665- const w = f . width || 160 ;
666- const h = f . height || 16 ;
667- const x = f . x || 0 ;
668- const y = f . y || 0 ;
677+ const w = ( f . width || 160 ) * scaleX ;
678+ const h = ( f . height || 16 ) * scaleY ;
679+ const x = ( f . x || 0 ) * scaleX ;
680+ const y = ( f . y || 0 ) * scaleY ;
669681
670682 // フィールドの背景(透明なのでスキップ)
671683 // テキストを描画
0 commit comments