1+ <!DOCTYPE html>
2+ < html lang ="en ">
3+ < head >
4+ < meta charset ="UTF-8 ">
5+ < meta name ="viewport " content ="width=device-width, initial-scale=1.0 ">
6+ < title > SVG to Image Converter</ title >
7+ < style >
8+ body {
9+ font-family : Arial, sans-serif;
10+ max-width : 800px ;
11+ margin : 0 auto;
12+ padding : 20px ;
13+ font-size : 16px ;
14+ }
15+ textarea , input , button , select {
16+ font-size : 16px ;
17+ }
18+ # dropZone {
19+ width : 100% ;
20+ height : 150px ;
21+ border : 2px dashed # ccc ;
22+ border-radius : 5px ;
23+ padding : 10px ;
24+ margin-bottom : 10px ;
25+ position : relative;
26+ }
27+ # dropZone .dragover {
28+ border-color : # 000 ;
29+ background-color : # f0f0f0 ;
30+ }
31+ # svgInput {
32+ width : 100% ;
33+ height : 100% ;
34+ border : none;
35+ resize : none;
36+ }
37+ # imageContainer {
38+ margin-top : 20px ;
39+ text-align : center;
40+ }
41+ # convertedImage {
42+ max-width : 90% ;
43+ cursor : pointer;
44+ transition : max-width 0.3s ease;
45+ }
46+ # convertedImage .full-size {
47+ max-width : unset;
48+ }
49+ # base64Output {
50+ word-wrap : break-word;
51+ }
52+ .option-group {
53+ margin-bottom : 10px ;
54+ }
55+ # fileInput {
56+ margin-bottom : 10px ;
57+ }
58+ # downloadLink , # loadExampleLink {
59+ display : inline-block;
60+ margin-top : 10px ;
61+ margin-right : 10px ;
62+ }
63+ # widthInput {
64+ width : 60px ;
65+ }
66+ # bgColor {
67+ vertical-align : middle;
68+ }
69+ # fileSize {
70+ margin-left : 10px ;
71+ }
72+ </ style >
73+ </ head >
74+ < body >
75+ < h1 > SVG to Image Converter</ h1 >
76+ < input type ="file " id ="fileInput " accept =".svg ">
77+ < a href ="https://gist.githubusercontent.com/simonw/aedecb93564af13ac1596810d40cac3c/raw/83e7f3be5b65bba61124684700fa7925d37c36c3/tiger.svg " id ="loadExampleLink "> Load example image</ a >
78+ < div id ="dropZone ">
79+ < textarea id ="svgInput " placeholder ="Paste your SVG code here or drag & drop an SVG file "> </ textarea >
80+ </ div >
81+ < div class ="option-group ">
82+ < label >
83+ < input type ="radio " name ="format " value ="image/jpeg " checked > JPEG
84+ </ label >
85+ < label >
86+ < input type ="radio " name ="format " value ="image/png "> PNG
87+ </ label >
88+ </ div >
89+ < div class ="option-group ">
90+ < label for ="bgColor "> Background Color:</ label >
91+ < input type ="color " id ="bgColor " value ="#000000 ">
92+ < label >
93+ < input type ="checkbox " id ="transparentBg " checked > Transparent
94+ </ label >
95+ </ div >
96+ < div class ="option-group ">
97+ < label for ="widthInput "> Output Width:</ label >
98+ < input type ="number " id ="widthInput " value ="800 " min ="1 ">
99+ </ div >
100+ < button onclick ="convertSvgToImage() "> Convert SVG</ button >
101+ < div id ="imageContainer "> </ div >
102+ < a id ="downloadLink " style ="display: none; "> Download Image</ a >
103+ < span id ="fileSize "> </ span >
104+ < h2 > Base64 Image Tag:</ h2 >
105+ < pre id ="base64Output "> </ pre >
106+ < button onclick ="copyBase64Tag() "> Copy Image Tag</ button >
107+
108+ < script >
109+ const dropZone = document . getElementById ( 'dropZone' ) ;
110+ const svgInput = document . getElementById ( 'svgInput' ) ;
111+ const fileInput = document . getElementById ( 'fileInput' ) ;
112+ const widthInput = document . getElementById ( 'widthInput' ) ;
113+ const loadExampleLink = document . getElementById ( 'loadExampleLink' ) ;
114+ const bgColor = document . getElementById ( 'bgColor' ) ;
115+ const transparentBg = document . getElementById ( 'transparentBg' ) ;
116+ const fileSizeSpan = document . getElementById ( 'fileSize' ) ;
117+
118+ // Load example image functionality
119+ loadExampleLink . addEventListener ( 'click' , ( e ) => {
120+ e . preventDefault ( ) ;
121+ const exampleSvgUrl = loadExampleLink . href ;
122+ fetch ( exampleSvgUrl )
123+ . then ( response => response . text ( ) )
124+ . then ( data => {
125+ svgInput . value = data ;
126+ } )
127+ . catch ( error => {
128+ console . error ( 'Error loading example SVG:' , error ) ;
129+ alert ( 'Failed to load example SVG. Please try again later.' ) ;
130+ } ) ;
131+ } ) ;
132+
133+ // Color and transparency handling
134+ bgColor . addEventListener ( 'input' , ( ) => {
135+ transparentBg . checked = false ;
136+ } ) ;
137+
138+ transparentBg . addEventListener ( 'change' , ( ) => {
139+ if ( transparentBg . checked ) {
140+ bgColor . value = "#000000" ;
141+ }
142+ } ) ;
143+
144+ // Drag and drop functionality
145+ dropZone . addEventListener ( 'dragover' , ( e ) => {
146+ e . preventDefault ( ) ;
147+ dropZone . classList . add ( 'dragover' ) ;
148+ } ) ;
149+
150+ dropZone . addEventListener ( 'dragleave' , ( ) => {
151+ dropZone . classList . remove ( 'dragover' ) ;
152+ } ) ;
153+
154+ dropZone . addEventListener ( 'drop' , ( e ) => {
155+ e . preventDefault ( ) ;
156+ dropZone . classList . remove ( 'dragover' ) ;
157+ const file = e . dataTransfer . files [ 0 ] ;
158+ if ( file && file . type === 'image/svg+xml' ) {
159+ readFile ( file ) ;
160+ }
161+ } ) ;
162+
163+ // File input functionality
164+ fileInput . addEventListener ( 'change' , ( e ) => {
165+ const file = e . target . files [ 0 ] ;
166+ if ( file && file . type === 'image/svg+xml' ) {
167+ readFile ( file ) ;
168+ }
169+ } ) ;
170+
171+ function readFile ( file ) {
172+ const reader = new FileReader ( ) ;
173+ reader . onload = ( e ) => {
174+ svgInput . value = e . target . result ;
175+ } ;
176+ reader . readAsText ( file ) ;
177+ }
178+
179+ function convertSvgToImage ( ) {
180+ let svgInput = document . getElementById ( 'svgInput' ) . value ;
181+ const imageContainer = document . getElementById ( 'imageContainer' ) ;
182+ const base64Output = document . getElementById ( 'base64Output' ) ;
183+ const downloadLink = document . getElementById ( 'downloadLink' ) ;
184+ const format = document . querySelector ( 'input[name="format"]:checked' ) . value ;
185+ const newWidth = parseInt ( widthInput . value ) || 800 ;
186+
187+ // Clear previous content
188+ imageContainer . innerHTML = '' ;
189+ base64Output . textContent = '' ;
190+ downloadLink . style . display = 'none' ;
191+ fileSizeSpan . textContent = '' ;
192+
193+ // Find the <?xml tag and ignore everything before it
194+ const xmlIndex = svgInput . indexOf ( '<?xml' ) ;
195+ if ( xmlIndex !== - 1 ) {
196+ svgInput = svgInput . substring ( xmlIndex ) ;
197+ }
198+
199+ // Create a temporary SVG element
200+ const svgElement = new DOMParser ( ) . parseFromString ( svgInput , 'image/svg+xml' ) . documentElement ;
201+
202+ if ( ! svgElement || svgElement . nodeName !== 'svg' ) {
203+ alert ( 'Invalid SVG input' ) ;
204+ return ;
205+ }
206+
207+ // Get SVG viewBox
208+ let viewBox = svgElement . getAttribute ( 'viewBox' ) ;
209+ let width , height ;
210+ if ( viewBox ) {
211+ [ , , width , height ] = viewBox . split ( ' ' ) . map ( Number ) ;
212+ } else {
213+ width = parseInt ( svgElement . getAttribute ( 'width' ) ) || 300 ;
214+ height = parseInt ( svgElement . getAttribute ( 'height' ) ) || 150 ;
215+ }
216+
217+ // Calculate new dimensions
218+ const aspectRatio = width / height ;
219+ const newHeight = Math . round ( newWidth / aspectRatio ) ;
220+
221+ // Create off-screen canvas
222+ const canvas = document . createElement ( 'canvas' ) ;
223+ canvas . width = newWidth ;
224+ canvas . height = newHeight ;
225+
226+ // Draw SVG on canvas
227+ const ctx = canvas . getContext ( '2d' ) ;
228+
229+ // Set background color if not transparent
230+ if ( ! transparentBg . checked ) {
231+ ctx . fillStyle = bgColor . value ;
232+ ctx . fillRect ( 0 , 0 , newWidth , newHeight ) ;
233+ }
234+
235+ const svgBlob = new Blob ( [ svgInput ] , { type : 'image/svg+xml;charset=utf-8' } ) ;
236+ const URL = window . URL || window . webkitURL || window ;
237+ const svgUrl = URL . createObjectURL ( svgBlob ) ;
238+
239+ const img = new Image ( ) ;
240+ img . onload = function ( ) {
241+ ctx . drawImage ( img , 0 , 0 , newWidth , newHeight ) ;
242+ URL . revokeObjectURL ( svgUrl ) ;
243+
244+ // Convert to selected format
245+ const imageDataUrl = canvas . toDataURL ( format ) ;
246+
247+ // Calculate file size
248+ const fileSizeInBytes = Math . round ( ( imageDataUrl . length * 3 ) / 4 ) ;
249+ const fileSizeInKB = ( fileSizeInBytes / 1024 ) . toFixed ( 2 ) ;
250+
251+ // Display converted image
252+ const convertedImg = document . createElement ( 'img' ) ;
253+ convertedImg . src = imageDataUrl ;
254+ convertedImg . id = 'convertedImage' ;
255+ convertedImg . onclick = toggleImageSize ;
256+ imageContainer . appendChild ( convertedImg ) ;
257+
258+ // Set up download link and display file size
259+ downloadLink . href = imageDataUrl ;
260+ downloadLink . download = `converted_image.${ format === 'image/jpeg' ? 'jpg' : 'png' } ` ;
261+ downloadLink . style . display = 'inline-block' ;
262+ fileSizeSpan . textContent = `(${ fileSizeInKB } KB)` ;
263+
264+ // Display base64 image tag
265+ const imgTag = `<img src="${ imageDataUrl } " alt="Converted ${ format === 'image/jpeg' ? 'JPEG' : 'PNG' } " width="${ newWidth } " height="${ newHeight } ">` ;
266+ base64Output . textContent = imgTag ;
267+ } ;
268+ img . src = svgUrl ;
269+ }
270+
271+ function toggleImageSize ( ) {
272+ const img = document . getElementById ( 'convertedImage' ) ;
273+ img . classList . toggle ( 'full-size' ) ;
274+ }
275+
276+ function copyBase64Tag ( ) {
277+ const base64Output = document . getElementById ( 'base64Output' ) ;
278+ const range = document . createRange ( ) ;
279+ range . selectNode ( base64Output ) ;
280+ window . getSelection ( ) . removeAllRanges ( ) ;
281+ window . getSelection ( ) . addRange ( range ) ;
282+ document . execCommand ( 'copy' ) ;
283+ window . getSelection ( ) . removeAllRanges ( ) ;
284+ alert ( 'Image tag copied to clipboard!' ) ;
285+ }
286+ </ script >
287+ </ body >
288+ </ html >
0 commit comments