1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3
4
5#
6#
7# UTF-8 test program for the Fast Light Tool Kit (FLTK).
8#
9# Copyright 1998-2009 by Bill Spitzak and others.
10#
11# This library is free software; you can redistribute it and/or
12# modify it under the terms of the GNU Library General Public
13# License as published by the Free Software Foundation; either
14# version 2 of the License, or (at your option) any later version.
15#
16# This library is distributed in the hope that it will be useful,
17# but WITHOUT ANY WARRANTY; without even the implied warranty of
18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19# Library General Public License for more details.
20#
21# You should have received a copy of the GNU Library General Public
22# License along with this library; if not, write to the Free Software
23# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
24# USA.
25#
26# Please report all bugs and problems to "fltk-bugs@fltk.org".
27#
28
29from fltk import *
30import sys, math
31
32#
33# Font chooser widget for the Fast Light Tool Kit(FLTK).
34#
35
36
37DEF_SIZE = 16 # default value for the font size picker
38
39fnt_chooser_win = None
40fontobj = None
41sizeobj = None
42
43fnt_cnt = None
44refresh_btn = None
45choose_btn = None
46fix_prop = None
47own_face = None
48
49sizes = None
50numsizes = None
51pickedsize = DEF_SIZE
52label = ""
53
54main_win = None
55thescroll = None
56extra_font = None
57
58font_count = 0
59first_free = 0
60
61label = None
62
63
64class FontDisplay(Fl_Widget):
65    def __init__(self, B, X, Y, W, H, L):
66        Fl_Widget.__init__(self, X, Y, W, H, L)
67        self.box(B)
68        self.font = 0
69        self.size = DEF_SIZE
70        self.l = L
71
72    def draw(self):
73        global label
74        fl_draw_box(self.box(), self.x(), self.y(), self.w(), self.h(), self.color())
75        fl_font(self.font, self.size)
76        fl_color(FL_BLACK)
77        fl_draw(label, self.x()+3, self.y()+3, self.w()-6, self.h()-6, self.align())
78
79    def test_fixed_pitch(self):
80        w1 = 0
81        w2 = 0
82        h1 = 0
83        h2 = 0
84        fl_font(self.font, self.size)
85
86        w1, h1 = fl_measure("MHMHWWMHMHMHM###WWX__--HUW", 0)
87        w2, h2 = fl_measure("iiiiiiiiiiiiiiiiiiiiiiiiii", 0)
88
89        if w1 == w2:
90            return 1 # exact match - fixed pitch
91
92	# Is the font "nearly" fixed pitch? If it is within 5%, say it is...
93        f1 = w1*1.0
94        f2 = w2*1.0
95        delta = math.fabs(f1 - f2) * 5.0
96        if delta <= f1:
97            return 2 # nearly fixed pitch...
98        return 0 # NOT fixed pitch
99
100
101
102textobj = None
103
104
105def size_cb(widget):
106    global textobj
107
108    size_idx = sizeobj.value()
109
110    if size_idx == 0:
111        return
112
113    c = sizeobj.text(size_idx)
114
115    # find the first numeric char
116    i = 0
117    while c[i] < '0' or c[i] > '9':
118        i = i+1
119    # convert the number string to a value
120    pickedsize = int(c[i:])
121
122    # Now set the font view to the selected size and redraw it.
123    textobj.size = pickedsize
124    textobj.redraw()
125
126
127def font_cb(widget, param):
128    global first_free, numsizes, sizes, sizeobj, textobj, fix_prop
129
130    font_idx = fontobj.value() + first_free
131    if font_idx == 0:
132        return
133
134    font_idx = font_idx-1
135    textobj.font = font_idx
136    sizeobj.clear()
137
138    size_count = numsizes[font_idx-first_free]
139    size_array = sizes[font_idx-first_free]
140    if size_count == 0:
141        # no preferred sizes - probably TT fonts etc...
142        pass
143    elif size_array[0] == 0:
144        # many sizes, probably a scaleable font with preferred sizes
145        j = 1
146        i = 1
147        while i <= 64 or i < size_array[size_count-1]:
148            i = i+1
149            buf = ""
150            if j < size_count and i == size_array[j]:
151                buf = "@b%d"%i
152                j = j+1
153            else:
154                buf = "%d"%i
155
156            sizeobj.add(buf)
157
158        sizeobj.value(pickedsize)
159    else:
160        # some sizes, probably a font with a few fixed sizes available
161        w = 0
162        for i in range(size_count):
163            # find the nearest available size to the current picked size
164            if size_array[i] <= pickedsize:
165                w = i
166
167                buf = "@b%d"%size_array[i]
168                sizeobj.add(buf)
169            sizeobj.value(w+1)
170    # force selection of nearest valid size, then redraw
171    size_cb(sizeobj)
172
173    # Now check to see if the font looks like a fixed pitch font or not...
174    looks_fixed = textobj.test_fixed_pitch()
175    if looks_fixed != 0:
176        if looks_fixed > 1:
177            fix_prop.value("near")
178        else:
179            fix_prop.value("fixed")
180
181    else:
182        fix_prop.value("prop")
183
184
185def choose_cb(widget):
186    global fontobj, first_free, sizeobj, main_win
187
188    font_idx = fontobj.value()+first_free
189    if font_idx == 0:
190        print("No font chosen")
191    else:
192        font_idx = font_idx-1
193        name, font_type = Fl.get_font_name(font_idx)
194        print("idx %d\nUser name :%s:"%( font_idx, name))
195        print("FLTK name :%s:"%( Fl.get_font(font_idx)))
196
197        Fl.set_font(extra_font, font_idx)
198
199    size_idx = sizeobj.value()
200    if size_idx == 0:
201        print("No size selected")
202    else:
203        c = sizeobj.text(size_idx)
204
205        # find the first numeric char
206        i = 0
207        while c[i] < '0' or c[i] > '9':
208            i = i+1
209        # convert the number string to a value
210        pickedsize = int(c[i:])
211
212    main_win.redraw()
213
214def refresh_cb(widget):
215    global main_win
216    main_win.redraw()
217
218def own_face_cb(widget):
219    global first_free, fontobj, fnt_chooser_win, own_face, font_count
220
221    cursor_restore = 0
222    i_was = -1
223
224    if i_was < 0:
225        i_was = 1
226    else:
227        # record which was the topmost visible line
228        i_was = fontobj.topline()
229        fontobj.clear()
230        # Populating the font widget can be slower than an old dog with three legs
231        # on a bad day, show a wait cursor
232        fnt_chooser_win.cursor(FL_CURSOR_WAIT)
233        cursor_restore = 1
234
235    # Populate the font list with the names of the fonts found
236    first_free = FL_FREE_FONT
237
238    font_idx = first_free
239    font_type = 0
240    while font_idx < font_count:
241        name, font_type = Fl.get_font_name(font_idx)
242        font_idx = font_idx+1
243        buf = ""
244
245        if own_face.value() == 0:
246            prefix=""
247            if font_type&FL_BOLD:
248                prefix = prefix+"@b"
249            if font_type&FL_ITALIC:
250                prefix = prefix+"@i"
251            buf = prefix+"@."+name
252        else:
253            buf="@F%d@.%s"%(font_idx, name)
254        fontobj.add(buf)
255
256    fontobj.topline(i_was)
257    if cursor_restore != 0:
258        fnt_chooser_win.cursor(FL_CURSOR_DEFAULT)
259
260def create_font_widget():
261    global fontobj
262    global sizeobj
263    global fnt_cnt
264    global fnt_chooser_win
265    global refresh_btn
266    global stat_bar
267    global textobj, own_face, fix_prop , label
268
269    fnt_chooser_win = Fl_Double_Window(380, 420, "Font Selector")
270
271    label = "Font Sample\n"
272    n = 0;
273    c = ord(' ')+1
274    while c < 127:
275        if not c&0x1f:
276            label = label+'\n'
277        if chr(c)=='@':
278            label = label+chr(c)
279        label = label+chr(c)
280        c = c+1
281
282    label = label+'\n'
283
284    c = 0xA1
285    while c < 0x600:
286        n = n+1
287        if not n&0x1f:
288            label = label+'\n'
289        if sys.version > '3':
290            label = label+str(chr(c).encode("utf-8"))
291        else:
292            label = label+unichr(c).encode("utf-8")
293        #label = label+unichr(c)
294        c = c+9
295    #label = label+'\0'
296
297    textobj = FontDisplay(FL_ENGRAVED_BOX, 10, 10, 360, 90, label)
298    #
299    #textobj = FontDisplay(FL_ENGRAVED_BOX, 10, 10, 360, 90, None)
300    textobj.align(FL_ALIGN_TOP | FL_ALIGN_LEFT | FL_ALIGN_INSIDE | FL_ALIGN_CLIP);
301    textobj.color(53, 3);
302
303    fontobj = Fl_Hold_Browser(10, 110, 290, 270)
304    fontobj.box(FL_ENGRAVED_BOX)
305    fontobj.color(53, 3)
306    fontobj.callback(font_cb, 0)
307    fnt_chooser_win.resizable(fontobj)
308
309    sizeobj = Fl_Hold_Browser(310, 110, 60, 270)
310    sizeobj.box(FL_ENGRAVED_BOX)
311    sizeobj.color(53, 3)
312    sizeobj.callback(size_cb)
313
314    # Create the status bar
315    stat_bar = Fl_Group (10, 385, 380, 30)
316    stat_bar.begin()
317
318    fnt_cnt = Fl_Value_Output(10, 390, 40, 20)
319    fnt_cnt.label("fonts")
320    fnt_cnt.align(FL_ALIGN_RIGHT)
321
322    fix_prop = Fl_Output(100, 390, 40, 20)
323    fix_prop.color(FL_BACKGROUND_COLOR)
324    fix_prop.value("prop")
325    fix_prop.clear_visible_focus()
326
327    own_face = Fl_Check_Button(150, 390, 40, 20, "Self")
328    own_face.value(0)
329    own_face.type(FL_TOGGLE_BUTTON)
330    own_face.clear_visible_focus()
331    own_face.callback(own_face_cb)
332    own_face.tooltip("Display font names in their own face")
333
334    dummy = Fl_Box(220, 390, 1, 1)
335
336    choose_btn = Fl_Button(240, 385, 60, 30)
337    choose_btn.label("Select")
338    choose_btn.callback(choose_cb)
339
340    refresh_btn = Fl_Button(310, 385, 60, 30)
341    refresh_btn.label("Refresh")
342    refresh_btn.callback(refresh_cb)
343
344    stat_bar.end()
345    stat_bar.resizable (dummy)
346
347    fnt_chooser_win.end()
348
349
350def make_font_chooser():
351    global sizes, numsizes, font_count
352    # create the widget frame
353    create_font_widget()
354
355    # Load the systems available fonts - ask for everything
356    if sys.platform == 'win32':
357        font_count = Fl.set_fonts("*")
358    elif sys.platform == 'apple':
359        font_count = Fl.set_fonts("*")
360    else:
361        # Load the systems available fonts - ask for everything that claims to be iso10646 compatible
362        #font_count = Fl.set_fonts("-*-*-*-*-*-*-*-*-*-*-*-*-iso10646-1")
363        font_count = Fl.set_fonts("*")
364
365    # allocate space for the sizes and numsizes array, now we know how many entries it needs
366    sizes = [None]*font_count
367    numsizes = [0]*font_count
368
369    # Populate the font list with the names of the fonts found
370    first_free = FL_FREE_FONT;
371    font_idx = first_free
372    while font_idx < font_count:
373        # Find out how many sizes are supported for each font face
374        size_array = Fl.get_font_sizes(font_idx)
375        size_count = len(size_array)
376        numsizes[font_idx-first_free] = size_count
377        if size_count != 0: # if the font has multiple sizes, populate the 2-D sizes array
378            sizes[font_idx-first_free] = size_array
379
380        font_idx = font_idx+1
381    # end of font list filling loop
382
383    # Call this once to get the font browser loaded up
384    own_face_cb(None)
385
386    fontobj.value(1)
387    #	fontobj.textfont(261); # optional hard-coded font for testing - do not use!
388
389    font_cb(fontobj, 0)
390
391    fnt_cnt.value(font_count)
392
393    return font_count
394
395
396
397# Unicode Font display widget
398
399def box_cb(widget):
400    global thescroll
401    if widget.value() == 0:
402        thescroll.box(FL_NO_BOX)
403    else:
404        thescroll.box(FL_DOWN_FRAME)
405    thescroll.redraw()
406
407
408class right_left_input(Fl_Input):
409    def __init__(self, x, y, w, h):
410        Fl_Input.__init__(self,x,y,w,h)
411
412    def draw(self):
413        pass
414        if self.type() == FL_HIDDEN_INPUT:
415            return
416        b = self.box()
417        if self.damage() & FL_DAMAGE_ALL:
418            fl_draw_box(self.box(), self.x(), self.y(), self.w(), self.h(), self.color())
419        self.drawtext(self.x()+Fl.box_dx(b)+3, self.y()+Fl.box_dy(b), self.w()-Fl.box_dw(b)-6, self.h()-Fl.box_dh(b))
420
421    def drawtext(self, X, Y, W, H):
422        fl_color(self.textcolor())
423        fl_font(self.textfont(), self.textsize())
424        fl_rtl_draw(self.value(), len(self.value()), X+W, Y+fl_height()-fl_descent())
425
426
427def i7_cb(i7, i8):
428    i = 0
429    nb = "01234567"
430    buf = ""
431
432    ptr = i7.value()
433
434    for i in range(len(ptr)):
435        if ptr[i] < ' ' or ord(ptr[i]) > 126:
436            buf.append('\\')
437            buf.append("some unicode stuff here")
438        else:
439            if ptr[i] == '\\':
440                buf.append('\\')
441            buf.append(ptr[i])
442
443    buf.append(chr(0));
444    i8.value(buf)
445
446
447class UCharDropBox(Fl_Output):
448    def __init__(self, x, y, w, h, label):
449        Fl_Output.__init__(self,x,y,w,h,label)
450
451    def handle(self, event):
452        if event == FL_DND_ENTER or event == FL_DND_DRAG or event == FL_DND_RELEASE:
453            return 1
454        elif event == FL_PASTE:
455            t = Fl.event_text()
456            n = fl_utf8decode(t, t)
457            if n == 0:
458                self.value("")
459                return 1
460            buffer = ""
461            for i in range(n):
462                buffer.append(t[i])
463            buffer.append(' ')
464            lut = "0123456789abcdef"
465            for i in range(n):
466                buffer.append('\\x')
467                buffer.append(' some lut stuff here')
468            buffer.append(chr(0))
469            self.value(buffer)
470            return 1
471        return Fl_Output.handle(self, event)
472
473
474if __name__ == '__main__':
475        # If this file is saved as a UTF-8, the latin1 text in the comment
476        # below doesn't look right any more!
477        # Store the specific latin-1 byte values here... this should be equivalent to:
478        latin1 = "\x41\x42\x43\x61\x62\x63\xe0\xe8\xe9\xef\xe2\xee\xf6\xfc\xe3\x31\x32\x33";
479        utf8 = ""
480        #l = fl_utf8froma(utf8, len(latin1)*5+1, latin1, len(latin1))
481
482        font_count = make_font_chooser()
483        extra_font = FL_TIMES_BOLD_ITALIC
484        # setup the extra font
485        if sys.platform == 'win32':
486            Fl.set_font(extra_font, " Arial Unicode MS")
487        elif sys.platform == 'darwin':
488            Fl.set_font(extra_font, "Monaco")
489        else:
490            Fl.set_font(extra_font, "-*-*-*-*-*-*-*-*-*-*-*-*-iso10646-1")
491
492        main_win = Fl_Double_Window (200 + 5*75, 400, "Unicode Display Test")
493        main_win.begin()
494
495        i1 = Fl_Input(5, 5, 190, 25)
496        #utf8 = utf8+'\0'
497        utf8 = latin1
498        i1.value(utf8)
499        scroll = Fl_Scroll(200,0,5 * 75,400)
500        off = 2
501        if len(sys.argv) > 1:
502            off = off/16
503
504        y = off
505
506        while y < 0x10000/16:
507            o = 0
508            i = 16*y
509            buf = ""
510            for x in range(16):
511                if sys.version > '3':
512                    buf = buf+chr(i)
513                else:
514                    buf = buf+unichr(i)
515                o = o+1
516                i = i+1
517            buf = buf+'\0'
518            bu = "0x%04lX"%(y * 16)
519            b = Fl_Input(200,(y-off)*25,60,25)
520            b.value(bu)
521            b = Fl_Input(260,(y-off)*25,400,25)
522            b.textfont(extra_font)
523            if sys.version >= '3':
524                if y >= 0xd80 and y <= 0xdff:
525                    # surrogate pairs not allowed
526                    buf = "................"+'\0'
527                b.value(buf)
528            else:
529                b.value(buf.encode("utf-8"))
530            #b.value(buf)
531            y = y+1
532
533        main_win.resizable(scroll)
534        scroll.end()
535
536        thescroll = scroll
537
538        i2 = Fl_Input(5, 35, 190, 25)
539        utf8l = utf8.lower()
540        i2.value(utf8l);
541
542
543        i3 = Fl_Input(5, 65, 190, 25)
544        utf8u = utf8l.upper()
545        i3.value(utf8u)
546
547        ltr_txt = "\\->e\xCC\x82=\xC3\xAA";
548        i4 = Fl_Input(5, 90, 190, 25)
549        i4.value(ltr_txt)
550        i4.textfont(extra_font)
551
552        r_to_l_txt =[ 1610, 1608, 1606, 1604, 1603, 1608, 1583, 0]
553
554        r_l_buf = ""
555        for i in r_to_l_txt:
556            if sys.version >= '3':
557                r_l_buf = r_l_buf+str(chr(i).encode("utf-8"))
558            else:
559                r_l_buf = r_l_buf+unichr(i).encode('utf-8')
560        abuf = r_l_buf
561        i5 = right_left_input(5, 115, 190, 50)
562        i5.textfont(extra_font)
563        i5.textsize(30)
564        i5.value(abuf)
565
566        i7 = Fl_Input(5, 230, 190, 25)
567        i8 = Fl_Input(5, 260, 190, 25)
568        i7.callback(i7_cb, i8)
569        i7.textsize(20)
570        i7.value(abuf)
571        i7.when(FL_WHEN_CHANGED)
572
573        r_to_l_txt1 = [1610, 0x20, 1608, 0x20, 1606, 0x20,  1604, 0x20, 1603, 0x20, 1608, 0x20, 1583, 0]
574
575        r_l_buf = ""
576        for i in r_to_l_txt1:
577            if sys.version >= '3':
578                r_l_buf = r_l_buf+str(chr(i).encode("utf-8"))
579            else:
580                r_l_buf = r_l_buf+unichr(i).encode('utf-8')
581        abuf1 = r_l_buf
582        i6 = right_left_input(5, 175, 190, 50)
583        i6.textfont(extra_font)
584        i6.textsize(30)
585        i6.value(abuf1)
586
587        # Now try Greg Ercolano's Japanese test sequence
588        # SOME JAPANESE UTF8 TEXT
589        # utfstr = "日本語というのは、とても難しい言語です。"
590        utfstr = "\xe4\xbd\x95\xe3\x82\x82\xe8\xa1\x8c\xe3\x82\x8b\xe3\x80\x82";
591        db = UCharDropBox(5, 300, 190, 30, "")
592        db.textsize(16)
593        db.value("unichar drop box")
594
595        o9 = Fl_Output(5, 330, 190, 45)
596        o9.textfont(extra_font)
597        o9.textsize(30)
598        o9.value(utfstr)
599
600        main_win.end()
601        fl_set_status(0, 370, 100, 30)
602
603
604        main_win.show(sys.argv)
605
606        fnt_chooser_win.show()
607
608        Fl.run()
609
610