1+ <!DOCTYPE html>
2+
3+ < title > Minecraft UNICODE Font Texture Genterator Online</ title >
4+
5+ < style >
6+ html , body {margin : 0 ; padding : 0 ;}
7+ html {
8+ background : # aaa ;
9+ font-family : monospace;
10+ }
11+ .root {
12+ width : 100vw ;
13+ height : 100vh ;
14+ padding-top : 64px ;
15+ box-sizing : border-box;
16+ transition : padding ease .2s ;
17+ min-width : 700px ;
18+ }
19+
20+ .header {
21+ height : 64px ;
22+ background : # ccc ;
23+ font-size : 32px ;
24+ box-sizing : border-box;
25+ display : flex;
26+ align-items : center;
27+ padding : 32px ;
28+ text-shadow : white 0 0 10px ;
29+ position : fixed;
30+ width : 100vw ;
31+ top : 0 ;
32+ transition : height ease .2s ;
33+ z-index : 9 ;
34+ min-width : 700px ;
35+ }
36+
37+ @media screen and (max-width : 1000px ) {
38+ .header {
39+ height : 128px ;
40+ }
41+
42+ .root {
43+ padding-top : 128px ;
44+ }
45+ }
46+
47+ .content {
48+ position : relative;
49+ z-index : 0 ;
50+ padding : 32px ;
51+ display : flex;
52+ }
53+
54+ .preview {
55+ display : flex;
56+ background : # ccc ;
57+ align-items : center;
58+ justify-content : flex-end;
59+ position : relative;
60+ }
61+
62+ .preview-info {
63+ position : absolute;
64+ left : 0 ;
65+ top : 288px ;
66+ min-width : 288px ;
67+ min-height : 200px ;
68+ background : # ccc ;
69+ padding : 16px ;
70+ box-sizing : border-box;
71+ }
72+
73+ # preview-canvas {
74+ width : 256px ;
75+ height : 256px ;
76+ background : black;
77+ border : 16px solid # ddd ;
78+ }
79+
80+ .panel {
81+ flex : 1 ;
82+ background : # bbb ;
83+ height : 288px ;
84+ overflow : show;
85+ }
86+
87+ .panel-content {
88+ background : # f7f7f7 ;
89+ margin : 16px ;
90+ width : calc (100% - 32px );
91+ height : 500px ;
92+ }
93+
94+ @media screen and (max-width : 1000px ) {
95+ .content {
96+ flex-direction : column;
97+ }
98+
99+ .preview-info {
100+ position : static;
101+ flex : 1 ;
102+ left : initial;
103+ top : initial;
104+ height : 288px ;
105+ background : transparent;
106+ }
107+ }
108+
109+ .form {
110+ padding : 8px ;
111+ padding-left : 0 ;
112+ padding-right : 0 ;
113+ }
114+
115+ .form > div {
116+ display : flex;
117+ font-size : 20px ;
118+ margin : 16px ;
119+ }
120+
121+ .form > div > .label {
122+ width : 256px ;
123+ height : 32px ;
124+ display : flex;
125+ align-items : center;
126+ }
127+
128+ .form > div > .input {
129+ flex : 1 ;
130+ display : flex;
131+ height : 32px ;
132+ }
133+
134+ .input > input {
135+ border : none;
136+ background : # bbb ;
137+ outline : none;
138+ margin : 0 ;
139+ padding : 0 ;
140+ width : 100% ;
141+ height : 100% ;
142+ font-family : monospace;
143+ text-indent : 8px ;
144+ box-shadow : 0 0 0 0 black, 0 0 0 0 black inset;
145+ transition : box-shadow ease .2s , background ease .2s ;
146+ }
147+
148+ .input > input : hover {
149+ box-shadow : 0 0 0 1px black, 0 0 0 1px black inset;
150+ background : # ccc ;
151+ }
152+
153+ .input > input : focus {
154+ box-shadow : 0 0 0 2px black, 0 0 0 2px black inset;
155+ background : # fff ;
156+ }
157+
158+ hr {
159+ margin : 0 ;
160+ padding : 0 ;
161+ height : 16px ;
162+ border : none;
163+ background : # bbb ;
164+ }
165+
166+ .buttonbar {
167+ display : flex;
168+ padding : 8px ;
169+ }
170+
171+ .buttonbar > button {
172+ flex : 1 ;
173+ height : 32px ;
174+ font-family : monospace;
175+ font-size : 20px ;
176+ border : none;
177+ margin : 8px ;
178+ outline : none;
179+ background : # fff ;
180+ box-shadow : 0 0 0 0 # bbb, 0 0 0 16px # aaa inset;
181+ color : black;
182+ transition : box-shadow ease .2s , background ease .2s , color ease .2s ;
183+ }
184+
185+ .buttonbar > button : hover {
186+ box-shadow : 0 0 0 16px # bbb, 0 0 0 0 # aaa inset;
187+ }
188+
189+ .buttonbar > button : active {
190+ background : # 000 ;
191+ color : white;
192+ }
193+ </ style >
194+
195+ < div class ="root ">
196+ < div class ="header "> Minecraft UNICODE Font Texture Genterator Online</ div >
197+ < div class ="content ">
198+ < div class ="preview ">
199+ < div class ="preview-info ">
200+ Loading...
201+ </ div >
202+ < canvas id ="preview-canvas "> </ canvas >
203+ </ div >
204+ < div class ="panel ">
205+ < div class ="panel-content ">
206+ < form class ="form ">
207+ < div class ="fontname ">
208+ < div class ="label ">
209+ Font Name:
210+ </ div >
211+ < div class ="input ">
212+ < input name ="fontname " value ="monospace "/>
213+ </ div >
214+ </ div >
215+ < div class ="fontsize ">
216+ < div class ="label ">
217+ Font Size:
218+ </ div >
219+ < div class ="input ">
220+ < input name ="fontsize " type ="number " min ="6 " step ="1 " value ="14 "/>
221+ </ div >
222+ </ div >
223+ < div class ="gridsize ">
224+ < div class ="label ">
225+ Grid Size:
226+ </ div >
227+ < div class ="input ">
228+ < input name ="gridsize " type ="number " min ="8 " step ="1 " value ="16 "/>
229+ </ div >
230+ </ div >
231+ < div class ="formatstring ">
232+ < div class ="label ">
233+ Format Template:
234+ </ div >
235+ < div class ="input ">
236+ < input name ="formatstring " value ="unicode_page_%02x.png "/>
237+ </ div >
238+ </ div >
239+ < div class ="exportfilename ">
240+ < div class ="label ">
241+ Export File Name:
242+ </ div >
243+ < div class ="input ">
244+ < input name ="exportfilename " value ="export.zip "/>
245+ </ div >
246+ </ div >
247+ </ form >
248+ < hr />
249+ < div class ="buttonbar ">
250+ < button id ="gen_btn "> Generate!</ button >
251+ < button id ="reset_btn "> Reset!</ button >
252+ </ div >
253+ < hr />
254+ </ div >
255+ </ div >
256+ </ div >
257+ </ div >
258+ < script src ="./sprintf.js ">
259+ </ script >
260+ < script src ="./jszip.min.js ">
261+ </ script >
262+ < script src ="./FileSaver.min.js ">
263+ </ script >
264+ < script >
265+ ( function ( ) {
266+ "use strict" ;
267+ const preview_canvas = document . querySelector ( "#preview-canvas" ) ;
268+ const preview_info = document . querySelector ( ".preview-info" ) ;
269+ const gen_btn = document . querySelector ( "#gen_btn" ) ;
270+ const reset_btn = document . querySelector ( "#reset_btn" ) ;
271+ const form = document . forms [ 0 ] ;
272+ const drawCharAt = ( ctx , size , ch , x , y ) => {
273+ ctx . fillText ( ch , x , y ) ;
274+ return ctx . measureText ( ch ) / size * 0xF ;
275+ } ;
276+ const drawPage = ( ctx , glyph , size , start , grid ) => {
277+ for ( let i = 0 ; i < 16 ; i ++ )
278+ for ( let j = 0 ; j < 16 ; j ++ )
279+ glyph [ ( start << 8 ) + i * 16 + j ] = drawCharAt ( ctx , size , String . fromCharCode ( ( start << 8 ) + i * 16 + j ) , j * grid , i * grid ) ;
280+ } ;
281+ const stripDataurl = dataurl => dataurl . substr ( dataurl . indexOf ( ',' ) + 1 ) ;
282+ const updateProgress = ch => preview_info . innerText = sprintf ( "Process %02x (%5.2f%%)" , ch , ch / 0xFF * 100 ) ;
283+
284+ gen_btn . addEventListener ( "click" , ( ) => {
285+ const zip = new JSZip ( ) ;
286+ const textures = zip . folder ( "textures" ) ;
287+ const ctx = preview_canvas . getContext ( '2d' ) ;
288+ const glyph_sizes = new Uint8Array ( 64 * 1024 ) ;
289+ preview_canvas . width = form . elements . gridsize . value * 16 ;
290+ preview_canvas . height = form . elements . gridsize . value * 16 ;
291+ ctx . fillStyle = "white" ;
292+ ctx . font = sprintf ( "%dpx %s" , form . elements . fontsize . value , form . elements . fontname . value ) ;
293+ ctx . textBaseline = "top" ;
294+ const draw = current => {
295+ updateProgress ( current ) ;
296+ ctx . clearRect ( 0 , 0 , preview_canvas . width , preview_canvas . height ) ;
297+ drawPage ( ctx , glyph_sizes , form . elements . gridsize . value , current , form . elements . gridsize . value ) ;
298+ textures . file ( sprintf ( form . elements . formatstring . value , current ) , stripDataurl ( preview_canvas . toDataURL ( "image/png" ) ) , { base64 : true } ) ;
299+ if ( current == 0xff ) return finish ( ) ;
300+ setTimeout ( ( ) => draw ( current + 1 ) , 10 ) ;
301+ } ;
302+ draw ( 0 ) ;
303+ const finish = ( ) => {
304+ preview_info . innerText = "storing glyph_sizes.bin" ;
305+ zip . file ( "glyph_sizes.bin" , glyph_sizes , { binary : true } ) ;
306+ preview_info . innerText = "finish...generating zip file...."
307+ zip . generateAsync ( { type :"blob" } ) . then ( content => {
308+ saveAs ( content , form . elements . exportfilename . value ) ;
309+ preview_info . innerText = "Ready." ;
310+ } ) ;
311+ } ;
312+ } ) ;
313+ reset_btn . addEventListener ( "click" , ( ) => {
314+ form . reset ( ) ;
315+ } ) ;
316+
317+ preview_info . innerText = "Ready."
318+ } ) ( ) ;
319+ </ script >
0 commit comments