1 /* Copyright (C) 2003-2012 by George Williams */
2 /*
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are met:
5
6 * Redistributions of source code must retain the above copyright notice, this
7 * list of conditions and the following disclaimer.
8
9 * Redistributions in binary form must reproduce the above copyright notice,
10 * this list of conditions and the following disclaimer in the documentation
11 * and/or other materials provided with the distribution.
12
13 * The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include <fontforge-config.h>
29
30 #include "autohint.h"
31 #include "dumppfa.h"
32 #include "ffglib.h"
33 #include "fontforgeui.h"
34 #include "gkeysym.h"
35 #include "psfont.h"
36 #include "splineutil.h"
37 #include "ustring.h"
38 #include "utype.h"
39
40 #include <math.h>
41
42 /* This operations are designed to work on a single font. NOT a CID collection*/
43 /* A CID collection must be treated one sub-font at a time */
44
45 struct hentry {
46 int cnt, sum;
47 int char_cnt, max;
48 SplineChar **chars;
49 };
50
51 typedef struct histdata {
52 int low, high;
53 struct hentry *hist; /* array of high-low+1 elements */
54 int tot, max;
55 } HistData;
56
HistDataFree(HistData * h)57 static void HistDataFree(HistData *h) {
58 int i;
59
60 for ( i=h->low; i<=h->high; ++i )
61 free(h->hist[i-h->low].chars);
62 free(h->hist);
63 free(h);
64 }
65
HistFindBlues(SplineFont * sf,int layer,uint8 * selected,EncMap * map)66 static HistData *HistFindBlues(SplineFont *sf,int layer, uint8 *selected, EncMap *map) {
67 int i, gid, low,high, top,bottom;
68 SplineChar *sc;
69 DBounds b;
70 HistData *hist;
71 struct hentry *h;
72
73 hist = calloc(1,sizeof(HistData));
74 hist->hist = calloc(sf->ascent+sf->descent+1,sizeof(struct hentry));
75 hist->low = sf->ascent; hist->high = -sf->descent;
76 low = -sf->descent; high = sf->ascent;
77
78 for ( i=0; i<(selected==NULL?sf->glyphcnt:map->enccount); ++i ) {
79 gid = selected==NULL ? i : map->map[i];
80 if ( gid!=-1 && (sc = sf->glyphs[gid])!=NULL &&
81 sc->layers[ly_fore].splines!=NULL &&
82 sc->layers[ly_fore].refs==NULL &&
83 (selected==NULL || selected[i])) {
84 SplineCharLayerFindBounds(sc,layer,&b);
85 bottom = rint(b.miny);
86 top = rint(b.maxy);
87 if ( top==bottom )
88 continue;
89 if ( top>hist->high ) {
90 hist->high = top;
91 if ( top>high ) {
92 hist->hist = realloc(hist->hist,(top+10-low)*sizeof(struct hentry));
93 memset(hist->hist + high-low+1,0,(top+10-high-1)*sizeof(struct hentry));
94 high = top+10 -1;
95 }
96 }
97 ++ hist->hist[top-low].cnt;
98 if ( hist->hist[top-low].char_cnt >= hist->hist[top-low].max ) {
99 if ( hist->hist[top-low].max==0 )
100 hist->hist[top-low].chars = malloc(10*sizeof(SplineChar *));
101 else
102 hist->hist[top-low].chars = realloc(hist->hist[top-low].chars,(hist->hist[top-low].max+10)*sizeof(SplineChar *));
103 hist->hist[top-low].max += 10;
104 }
105 hist->hist[top-low].chars[hist->hist[top-low].char_cnt++] = sc;
106
107 if ( bottom<hist->low ) {
108 hist->low = bottom;
109 if ( bottom<low ) {
110 h = calloc((high-bottom+10),sizeof( struct hentry ));
111 memcpy(h+low-(bottom-10+1),hist->hist,(high+1-low)*sizeof(struct hentry));
112 low = bottom-10+1;
113 free( hist->hist );
114 hist->hist = h;
115 }
116 }
117 ++ hist->hist[bottom-low].cnt;
118 if ( hist->hist[bottom-low].char_cnt >= hist->hist[bottom-low].max ) {
119 if ( hist->hist[bottom-low].max==0 )
120 hist->hist[bottom-low].chars = malloc(10*sizeof(SplineChar *));
121 else
122 hist->hist[bottom-low].chars = realloc(hist->hist[bottom-low].chars,(hist->hist[bottom-low].max+10)*sizeof(SplineChar *));
123 hist->hist[bottom-low].max += 10;
124 }
125 hist->hist[bottom-low].chars[hist->hist[bottom-low].char_cnt++] = sc;
126 }
127 hist->tot += 2;
128 }
129 if ( hist->low>hist->high ) { /* Found nothing */
130 hist->low = hist->high = 0;
131 }
132 if ( low!=hist->low || high!=hist->high ) {
133 h = malloc((hist->high-hist->low+1)*sizeof(struct hentry));
134 memcpy(h,hist->hist + hist->low-low,(hist->high-hist->low+1)*sizeof(struct hentry));
135 free(hist->hist);
136 hist->hist = h;
137 }
138 return( hist );
139 }
140
HistFindStemWidths(SplineFont * sf,int layer,uint8 * selected,EncMap * map,int hor)141 static HistData *HistFindStemWidths(SplineFont *sf,int layer, uint8 *selected,EncMap *map,int hor) {
142 int i, gid, low,high, val;
143 SplineChar *sc;
144 HistData *hist;
145 struct hentry *h;
146 StemInfo *stem;
147
148 hist = calloc(1,sizeof(HistData));
149 hist->hist = calloc(sf->ascent+sf->descent+1,sizeof(struct hentry));
150 hist->low = sf->ascent+sf->descent;
151 low = 0; high = sf->ascent+sf->descent;
152
153 for ( i=0; i<(selected==NULL?sf->glyphcnt:map->enccount); ++i ) {
154 gid = selected==NULL ? i : map->map[i];
155 if ( gid!=-1 && (sc = sf->glyphs[gid])!=NULL &&
156 sc->layers[ly_fore].splines!=NULL &&
157 sc->layers[ly_fore].refs==NULL &&
158 (selected==NULL || selected[i])) {
159 if ( autohint_before_generate && sc->changedsincelasthinted && !sc->manualhints )
160 SplineCharAutoHint(sc,layer,NULL);
161 for ( stem = hor ? sc->hstem : sc->vstem ; stem!=NULL; stem = stem->next ) {
162 if ( stem->ghost )
163 continue;
164 val = rint(stem->width);
165 if ( val<=0 )
166 val = -val;
167 if ( val>hist->high ) {
168 hist->high = val;
169 if ( val>high ) {
170 hist->hist = realloc(hist->hist,(val+10-low)*sizeof(struct hentry));
171 memset(hist->hist + high-low+1,0,(val+10-high-1)*sizeof(struct hentry));
172 high = val+10 -1;
173 }
174 }
175 if ( val<hist->low )
176 hist->low = val;
177 ++ hist->hist[val-low].cnt;
178 if ( hist->hist[val-low].char_cnt==0 ||
179 hist->hist[val-low].chars[hist->hist[val-low].char_cnt-1]!=sc ) {
180 if ( hist->hist[val-low].char_cnt >= hist->hist[val-low].max ) {
181 if ( hist->hist[val-low].max==0 )
182 hist->hist[val-low].chars = malloc(10*sizeof(SplineChar *));
183 else
184 hist->hist[val-low].chars = realloc(hist->hist[val-low].chars,(hist->hist[val-low].max+10)*sizeof(SplineChar *));
185 hist->hist[val-low].max += 10;
186 }
187 hist->hist[val-low].chars[hist->hist[val-low].char_cnt++] = sc;
188 }
189 ++ hist->tot;
190 }
191 }
192 }
193 if ( hist->low>hist->high ) { /* Found nothing */
194 hist->low = hist->high = 0;
195 }
196 if ( low!=hist->low || high!=hist->high ) {
197 h = malloc((hist->high-hist->low+1)*sizeof(struct hentry));
198 memcpy(h,hist->hist + hist->low-low,(hist->high-hist->low+1)*sizeof(struct hentry));
199 free(hist->hist);
200 hist->hist = h;
201 }
202 return( hist );
203 }
204
HistFindHStemWidths(SplineFont * sf,int layer,uint8 * selected,EncMap * map)205 static HistData *HistFindHStemWidths(SplineFont *sf,int layer, uint8 *selected,EncMap *map) {
206 return( HistFindStemWidths(sf,layer,selected,map,true) );
207 }
208
HistFindVStemWidths(SplineFont * sf,int layer,uint8 * selected,EncMap * map)209 static HistData *HistFindVStemWidths(SplineFont *sf,int layer, uint8 *selected,EncMap *map) {
210 return( HistFindStemWidths(sf,layer,selected,map,false) );
211 }
212
HistFindMax(HistData * h,int sum_around)213 static void HistFindMax(HistData *h, int sum_around) {
214 int i, j, m=1;
215 int c;
216
217 if ( sum_around<0 ) sum_around = 0;
218 for ( i = h->low; i<=h->high; ++i ) {
219 c = 0;
220 for ( j=i-sum_around; j<=i+sum_around; ++j )
221 if ( j>=h->low && j<=h->high )
222 c += h->hist[j-h->low].cnt;
223 h->hist[i-h->low].sum = c;
224 if ( c>m )
225 m = c;
226 }
227 h->max = m;
228 }
229
230 #define CID_ScrollBar 1000
231 #define CID_MainVal 1001
232 #define CID_SecondaryVal 1002
233
234 #define CID_SumAround 1003
235 #define CID_BarWidth 1004
236
237 #define CID_MainValL 2001
238 #define CID_SecondaryValL 2002
239 #define CID_SumAroundL 2003
240 #define CID_BarWidthL 2004
241 #define CID_Group 2005
242 #define CID_BlueMsg 2006
243
244 #define CID_OK 3001
245 #define CID_Cancel 3002
246
247 #define CID_LeftSide 4001
248 #define CID_Histogram 4002
249 #define CID_RightSide 4003
250
251 struct hist_dlg {
252 enum hist_type which;
253 SplineFont *sf;
254 int layer;
255 struct psdict *private;
256 uint8 *selected;
257 HistData *h;
258
259 int pending_blue;
260 int is_pending;
261
262 int sum_around, barwidth;
263 int hoff;
264
265 int x,y;
266 int hwidth, hheight;
267 int yoff;
268
269 GWindow gw;
270 GFont *font;
271 int fh, as;
272 int done;
273 };
274
HistPopup(struct hist_dlg * hist,GEvent * e)275 static void HistPopup(struct hist_dlg *hist,GEvent *e) {
276 int x = e->u.mouse.x;
277 struct hentry *h;
278 static char buffer[300];
279 char *end = buffer + sizeof(buffer)/sizeof(buffer[0]), *pt, *line;
280 int i;
281
282 x /= hist->barwidth;
283 if ( x + hist->hoff > hist->h->high || x + hist->hoff - hist->h->low<0 )
284 return;
285
286 h = &hist->h->hist[x + hist->hoff - hist->h->low];
287 if ( hist->sum_around==0 ) {
288 if ( hist->which == hist_blues )
289 snprintf(buffer,end-buffer,
290 _("Position: %d\nCount: %d\n"),
291 x + hist->hoff,
292 h->sum);
293 else
294 snprintf(buffer,end-buffer,
295 _("Width: %d\nCount: %d\nPercentage of Max: %d%%\n"),
296 x + hist->hoff,
297 h->sum, (int) rint(h->sum*100.0/hist->h->max));
298 } else {
299 if ( hist->which == hist_blues )
300 snprintf(buffer,end-buffer,
301 _("Position: %d-%d (%d)\nCount: %d (%d)\n"),
302 x+hist->hoff-hist->sum_around, x+hist->hoff+hist->sum_around, x + hist->hoff,
303 h->sum, h->cnt);
304 else
305 snprintf(buffer,end-buffer,
306 _("Width: %d-%d (%d)\nCount: %d (%d)\nPercentage of Max: %d%%\n"),
307 x+hist->hoff-hist->sum_around, x+hist->hoff+hist->sum_around, x + hist->hoff,
308 h->sum, h->cnt, (int) rint(h->sum*100.0/hist->h->max));
309 }
310 pt = buffer+strlen(buffer);
311 line = pt;
312 for ( i = 0; i<h->char_cnt; ++i ) {
313 if ( pt+strlen(h->chars[i]->name)+4>end ) {
314 strcpy(pt,"...");
315 break;
316 }
317 strcpy(pt,h->chars[i]->name);
318 pt += strlen(pt);
319 if ( pt-line>70 ) {
320 *pt++ = '\n';
321 line = pt;
322 } else
323 *pt++ = ' ';
324 *pt = '\0';
325 }
326 GGadgetPreparePopup8(hist->gw,buffer);
327 }
328
ArrayOrder(char * old,int args,int val1,int val2)329 static char *ArrayOrder(char *old,int args,int val1,int val2) {
330 char *end;
331 double array[40];
332 int i,j,k;
333 GString *new;
334
335 if ( *old=='[' ) ++old;
336
337 for ( i=0; i<40 && *old!=']' && *old!='\0'; ++i ) {
338 array[i] = strtod(old,&end);
339 if ( old==end )
340 break;
341 old = end;
342 while ( *old==' ' ) ++old;
343 }
344 if (i<40)
345 array[i++] = val1;
346 if (i<40) {
347 if ( args==2 )
348 array[i++] = val2;
349 }
350 for ( j=0; j<i; ++j ) for ( k=j+1; k<i; ++k ) if ( array[j]>array[k] ) {
351 double temp = array[j];
352 array[j] = array[k];
353 array[k] = temp;
354 }
355
356 new = g_string_new( "[" );
357 for ( k=0; k<i; ++k ) {
358 if (k == i-1)
359 g_string_append_printf( new, "%g]", array[k] );
360 else
361 g_string_append_printf( new, "%g ", array[k] );
362 }
363
364 return( (char *) g_string_free( new, FALSE ) );
365 }
366
367 /* Handle clicks on histogram chart and update text fields below accordingly */
HistPress(struct hist_dlg * hist,GEvent * e)368 static void HistPress(struct hist_dlg *hist,GEvent *e) {
369 char *old = NULL;
370 char *new = NULL;
371 int x = e->u.mouse.x;
372
373 x /= hist->barwidth;
374 x += hist->hoff;
375 if ( x > hist->h->high || x<hist->h->low )
376 return;
377
378 if ( hist->which==hist_blues ) {
379 if ( hist->is_pending ) {
380 if ( x<hist->pending_blue )
381 ff_post_error(_("Bad Value"),_("The smaller number must be selected first in a pair of bluevalues"));
382 else if ( x<0 ) { /* OtherBlues */
383 old = GGadgetGetTitle8( GWidgetGetControl( hist->gw, CID_SecondaryVal ));
384 new = ArrayOrder( old, 2, hist->pending_blue, x );
385 GGadgetSetTitle8( GWidgetGetControl( hist->gw, CID_SecondaryVal ), new );
386 } else {
387 old = GGadgetGetTitle8( GWidgetGetControl( hist->gw, CID_MainVal ));
388 new = ArrayOrder( old, 2, hist->pending_blue, x );
389 GGadgetSetTitle8( GWidgetGetControl( hist->gw, CID_MainVal ), new );
390 }
391 GDrawSetCursor(hist->gw,ct_pointer);
392 hist->is_pending = false;
393 } else {
394 hist->is_pending = true;
395 hist->pending_blue = x;
396 GDrawSetCursor(hist->gw,ct_eyedropper);
397 }
398 GGadgetSetVisible(GWidgetGetControl(hist->gw,CID_MainVal),!hist->is_pending);
399 GGadgetSetVisible(GWidgetGetControl(hist->gw,CID_MainValL),!hist->is_pending);
400 GGadgetSetVisible(GWidgetGetControl(hist->gw,CID_BlueMsg),hist->is_pending);
401 } else { /* HStem and VStem */
402 if ( !( e->u.mouse.state&ksm_shift )) {
403 new = smprintf( "[%d]", x );
404 GGadgetSetTitle8( GWidgetGetControl( hist->gw, CID_MainVal ), new );
405 GGadgetSetTitle8( GWidgetGetControl( hist->gw, CID_SecondaryVal ), new );
406 } else {
407 old = GGadgetGetTitle8( GWidgetGetControl( hist->gw, CID_SecondaryVal ));
408 new = ArrayOrder( old, 1, x, 0 );
409 GGadgetSetTitle8( GWidgetGetControl( hist->gw, CID_SecondaryVal ), new );
410 }
411 }
412 free( old );
413 free( new );
414 }
415
HistExpose(GWindow pixmap,struct hist_dlg * hist)416 static void HistExpose(GWindow pixmap, struct hist_dlg *hist) {
417 GRect r,old;
418 int height;
419 double yscale;
420 int i;
421 char buf[20];
422 GRect size;
423 GDrawGetSize(GDrawableGetWindow(GWidgetGetControl(hist->gw,CID_Histogram)),&size);
424
425 height = size.height-hist->fh-2;
426 yscale = (4*height/5.0)/(hist->h->max-0);
427
428 GDrawSetLineWidth(pixmap,0);
429 r.x = 0; r.y = 0;
430 r.width = size.width-1; r.height = height-1;
431 GDrawDrawRect(pixmap,&r,0x000000);
432
433 ++r.x; r.width--;
434 ++r.y; r.height--;
435 GDrawPushClip(pixmap,&r,&old);
436
437 for ( i=hist->hoff; (i-hist->hoff)*hist->barwidth<size.width-2 && i<=hist->h->high; ++i ) {
438 r.x = (i-hist->hoff)*hist->barwidth+1; r.width = hist->barwidth;
439 r.height = rint(hist->h->hist[i-hist->h->low].sum * yscale);
440 if ( r.height>=0 ) {
441 r.y = height - r.height;
442 GDrawFillRect(pixmap,&r,0x2020ff);
443 }
444 }
445
446 GDrawPopClip(pixmap,&old);
447
448 GDrawSetFont(pixmap,hist->font);
449 sprintf(buf,"%d",hist->hoff);
450 GDrawDrawText8(pixmap,0,height+2+hist->as, buf,-1,0x000000);
451 sprintf(buf,"%d",hist->hoff+hist->hwidth/hist->barwidth);
452 GDrawDrawText8(pixmap,size.width-GDrawGetText8Width(pixmap,buf,-1),height+2+hist->as,
453 buf,-1,0x000000);
454 }
455
HistRExpose(GWindow pixmap,struct hist_dlg * hist)456 static void HistRExpose(GWindow pixmap, struct hist_dlg *hist) {
457 int height;
458 double yscale;
459 GRect size;
460 char buf[20];
461
462 GDrawGetSize(GDrawableGetWindow(GWidgetGetControl(hist->gw,CID_RightSide)),&size);
463 height = size.height-hist->fh-2;
464 yscale = (4*height/5.0)/(hist->h->max-0);
465
466 sprintf(buf,"%d",hist->h->max);
467 GDrawDrawText8(pixmap,1,height-rint(hist->h->max*yscale),
468 buf,-1,0x000000);
469 }
470
HistLExpose(GWindow pixmap,struct hist_dlg * hist)471 static void HistLExpose(GWindow pixmap, struct hist_dlg *hist) {
472 int height;
473 double yscale;
474 GRect size;
475 char buf[20];
476
477 GDrawGetSize(GDrawableGetWindow(GWidgetGetControl(hist->gw,CID_LeftSide)),&size);
478 height = size.height-hist->fh-2;
479 yscale = (4*height/5.0)/(hist->h->max-0);
480
481 sprintf(buf,"%d",hist->h->max);
482 GDrawDrawText8(pixmap,size.width-GDrawGetText8Width(pixmap,buf,-1)-1,height-rint(hist->h->max*yscale),
483 buf,-1,0x000000);
484 }
485
HistScroll(struct hist_dlg * hist,struct sbevent * sb)486 static void HistScroll(struct hist_dlg *hist,struct sbevent *sb) {
487 int newpos = hist->hoff;
488 int cols;
489 GRect size;
490 GGadget *g = GWidgetGetControl(hist->gw,CID_ScrollBar);
491
492 GGadgetGetSize(g,&size);
493 cols = (size.width-2)/hist->barwidth;
494
495 switch( sb->type ) {
496 case et_sb_top:
497 newpos = 0;
498 break;
499 case et_sb_uppage:
500 newpos -= cols;
501 break;
502 case et_sb_up:
503 --newpos;
504 break;
505 case et_sb_down:
506 ++newpos;
507 break;
508 case et_sb_downpage:
509 newpos += cols;
510 break;
511 case et_sb_bottom:
512 newpos = (hist->h->high+1-hist->h->low)-cols;
513 break;
514 case et_sb_thumb:
515 case et_sb_thumbrelease:
516 newpos = sb->pos;
517 break;
518 }
519 if ( newpos>(hist->h->high+1-hist->h->low)-cols + hist->h->low )
520 newpos = (hist->h->high+1-hist->h->low)-cols + hist->h->low;
521 if ( newpos<hist->h->low ) newpos = hist->h->low;
522 if ( newpos!=hist->hoff ) {
523 /*int diff = newpos-hist->hoff;*/
524 hist->hoff = newpos;
525 GScrollBarSetPos(g,hist->hoff);
526 GDrawRequestExpose(GDrawableGetWindow(GWidgetGetControl(hist->gw,CID_Histogram)),NULL,false);
527 }
528 }
529
HistRefigureSB(struct hist_dlg * hist)530 static void HistRefigureSB(struct hist_dlg *hist) {
531 GGadget *g = GWidgetGetControl(hist->gw,CID_ScrollBar);
532 int width, hoff, cols;
533 GRect size;
534
535 GGadgetGetSize(g,&size);
536 width = size.width-2;
537 cols = width/hist->barwidth;
538
539 GScrollBarSetBounds(g,hist->h->low,hist->h->high+1,cols);
540 if ( hist->hoff+cols >hist->h->high ) {
541 hoff = hist->h->high-cols;
542 if ( hoff<0 ) hoff = 0;
543 if ( hoff!=hist->hoff ) {
544 hist->hoff = hoff;
545 GScrollBarSetPos(g,hoff);
546 }
547 }
548 }
549
HistResize(struct hist_dlg * hist)550 static void HistResize(struct hist_dlg *hist) {
551
552 HistRefigureSB(hist);
553 GDrawRequestExpose(hist->gw,NULL,false);
554 }
555
HistSet(struct hist_dlg * hist)556 static void HistSet(struct hist_dlg *hist) {
557 char *primary, *secondary;
558 char *temp;
559 struct psdict *p = hist->private ? hist->private : hist->sf->private;
560 const unichar_t *ret1, *ret2;
561
562 switch ( hist->which ) {
563 case hist_hstem:
564 primary = "StdHW"; secondary = "StemSnapH";
565 break;
566 case hist_vstem:
567 primary = "StdVW"; secondary = "StemSnapV";
568 break;
569 case hist_blues:
570 primary = "BlueValues"; secondary = "OtherBlues";
571 break;
572 }
573 ret1 = GGadgetGetTitle(GWidgetGetControl(hist->gw,CID_MainVal));
574 ret2 = GGadgetGetTitle(GWidgetGetControl(hist->gw,CID_SecondaryVal));
575 hist->done = true;
576 if ( (*ret1=='\0' || uc_strcmp(ret1,"[]")==0 ) &&
577 (*ret2=='\0' || uc_strcmp(ret2,"[]")==0 ) && p==NULL )
578 return;
579 if ( p==NULL ) {
580 hist->sf->private = p = calloc(1,sizeof(struct psdict));
581 p->cnt = 10;
582 p->keys = calloc(10,sizeof(char *));
583 p->values = calloc(10,sizeof(char *));
584 }
585 PSDictChangeEntry(p,primary,temp=cu_copy(ret1)); free(temp);
586 PSDictChangeEntry(p,secondary,temp=cu_copy(ret2)); free(temp);
587 }
588
leftside_e_h(GWindow gw,GEvent * event)589 static int leftside_e_h(GWindow gw, GEvent *event) {
590 struct hist_dlg *hist = GDrawGetUserData(gw);
591
592 switch ( event->type ) {
593 case et_char:
594 if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
595 help("ui/dialogs/histogram.html", NULL);
596 return( true );
597 }
598 return( false );
599 break;
600 case et_expose:
601 HistLExpose(gw,hist);
602 break;
603 case et_mousemove:
604 case et_mousedown:
605 GGadgetEndPopup();
606 break;
607 }
608 return( true );
609 }
610
rightside_e_h(GWindow gw,GEvent * event)611 static int rightside_e_h(GWindow gw, GEvent *event) {
612 struct hist_dlg *hist = GDrawGetUserData(gw);
613
614 switch ( event->type ) {
615 case et_char:
616 if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
617 help("ui/dialogs/histogram.html", NULL);
618 return( true );
619 }
620 return( false );
621 break;
622 case et_expose:
623 HistRExpose(gw,hist);
624 break;
625 case et_mousemove:
626 case et_mousedown:
627 GGadgetEndPopup();
628 break;
629 }
630 return( true );
631 }
632
histogram_e_h(GWindow gw,GEvent * event)633 static int histogram_e_h(GWindow gw, GEvent *event) {
634 struct hist_dlg *hist = GDrawGetUserData(gw);
635
636 switch ( event->type ) {
637 case et_char:
638 if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
639 help("ui/dialogs/histogram.html", NULL);
640 return( true );
641 }
642 return( false );
643 break;
644 case et_expose:
645 HistExpose(gw,hist);
646 break;
647 case et_mousemove:
648 GGadgetEndPopup();
649 HistPopup(hist,event);
650 break;
651 case et_mousedown:
652 GGadgetEndPopup();
653 HistPress(hist,event);
654 break;
655 }
656 return( true );
657 }
658
hist_e_h(GWindow gw,GEvent * event)659 static int hist_e_h(GWindow gw, GEvent *event) {
660 struct hist_dlg *hist = GDrawGetUserData(gw);
661 int temp;
662 const unichar_t *ret;
663 unichar_t *end;
664
665 if ( event->type==et_close ) {
666 hist->done = true;
667 } else if ( event->type==et_char ) {
668 if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
669 help("ui/dialogs/histogram.html", NULL);
670 return( true );
671 }
672 return( false );
673 } else if ( event->type==et_resize ) {
674 HistResize(hist);;
675 } else if ( event->type==et_mousemove ) {
676 GGadgetEndPopup();
677 } else if ( event->type==et_mousedown ) {
678 GGadgetEndPopup();
679 } else if ( event->type==et_controlevent ) {
680 switch ( event->u.control.subtype ) {
681 case et_scrollbarchange:
682 HistScroll(hist,&event->u.control.u.sb);
683 break;
684 case et_textchanged:
685 switch( GGadgetGetCid(event->u.control.g)) {
686 case CID_SumAround: case CID_BarWidth:
687 ret = _GGadgetGetTitle(event->u.control.g);
688 temp = u_strtol(ret,&end,10);
689 if ( temp<0 || *end )
690 break;
691 if ( GGadgetGetCid(event->u.control.g)==CID_SumAround ) {
692 hist->sum_around = temp;
693 HistFindMax(hist->h,temp);
694 } else if ( temp==0 )
695 break;
696 else {
697 hist->barwidth = temp;
698 HistRefigureSB(hist);
699 }
700 GDrawRequestExpose(GDrawableGetWindow(GWidgetGetControl(gw,CID_Histogram)),NULL,false);
701 GDrawRequestExpose(GDrawableGetWindow(GWidgetGetControl(gw,CID_LeftSide)),NULL,false);
702 GDrawRequestExpose(GDrawableGetWindow(GWidgetGetControl(gw,CID_RightSide)),NULL,false);
703 break;
704 }
705 break;
706 case et_buttonactivate:
707 if ( GGadgetGetCid(event->u.control.g)==CID_OK ) {
708 HistSet(hist);
709 } else
710 hist->done = true;
711 break;
712 }
713 }
714 return( true );
715 }
716
CheckSmallSelection(uint8 * selected,EncMap * map,SplineFont * sf)717 static void CheckSmallSelection(uint8 *selected,EncMap *map,SplineFont *sf) {
718 int i, cnt, tot;
719
720 for ( i=cnt=tot=0; i<map->enccount; ++i ) {
721 int gid = map->map[i];
722 if ( gid!=-1 && sf->glyphs[gid]!=NULL ) {
723 ++tot;
724 if ( selected[i] )
725 ++cnt;
726 }
727 }
728 if ( (cnt==1 && tot>1) || (cnt<8 && tot>30) )
729 ff_post_notice(_("Tiny Selection"),_("There are so few glyphs selected that it seems unlikely to me that you will get a representative sample of this aspect of your font. If you deselect everything the command will apply to all glyphs in the font"));
730 }
731
SFHistogram(SplineFont * sf,int layer,struct psdict * private,uint8 * selected,EncMap * map,enum hist_type which)732 void SFHistogram(SplineFont *sf,int layer, struct psdict *private, uint8 *selected,
733 EncMap *map,enum hist_type which) {
734 struct hist_dlg hist;
735 GWindow gw;
736 GRect pos;
737 GWindowAttrs wattrs;
738 GGadgetCreateData gcd[17], boxes[6], *hv[4][2], *butarray[9], *hvctls[5][5], *hvbody[3][4];
739 GTextInfo label[17];
740 int i,j;
741 char binsize[20], barwidth[20], *primary, *secondary;
742 FontRequest rq;
743 int as, ds, ld;
744 static unichar_t n9999[] = { '9', '9', '9', '9', 0 };
745 static GFont *font = NULL;
746
747 memset(&hist,0,sizeof(hist));
748 hist.sf = sf;
749 hist.layer = layer;
750 hist.private = private;
751 if ( private==NULL ) private = sf->private;
752 hist.selected = selected;
753 hist.which = which;
754 hist.barwidth = 6;
755 hist.sum_around = 0;
756 switch ( which ) {
757 case hist_hstem:
758 hist.h = HistFindHStemWidths(sf,layer,selected,map);
759 break;
760 case hist_vstem:
761 hist.h = HistFindVStemWidths(sf,layer,selected,map);
762 break;
763 case hist_blues:
764 hist.h = HistFindBlues(sf,layer,selected,map);
765 break;
766 }
767 HistFindMax(hist.h,hist.sum_around);
768
769 if ( selected!=NULL )
770 CheckSmallSelection(selected,map,sf);
771
772 memset(&wattrs,0,sizeof(wattrs));
773 wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
774 wattrs.event_masks = ~(1<<et_charup);
775 wattrs.restrict_input_to_me = 1;
776 wattrs.undercursor = 1;
777 wattrs.cursor = ct_pointer;
778 wattrs.utf8_window_title = which==hist_hstem?_("HStem") :
779 which==hist_vstem?_("VStem"):
780 _("Blues");
781 wattrs.is_dlg = true;
782 pos.x = pos.y = 0;
783 pos.width = GGadgetScale(GDrawPointsToPixels(NULL,210));
784 hist.yoff = GDrawPointsToPixels(NULL,120);
785 pos.height = pos.width + hist.yoff;
786 hist.gw = gw = GDrawCreateTopWindow(NULL,&pos,hist_e_h,&hist,&wattrs);
787
788 if ( font == NULL ) {
789 memset(&rq,0,sizeof(rq));
790 rq.utf8_family_name = SANS_UI_FAMILIES;
791 rq.point_size = 10;
792 rq.weight = 400;
793 font = GDrawInstanciateFont(NULL,&rq);
794 font = GResourceFindFont("Histogram.Font",font);
795 }
796 hist.font = font;
797 GDrawWindowFontMetrics(gw,hist.font,&as,&ds,&ld);
798 hist.fh = as+ds; hist.as = as;
799
800 GDrawSetFont(gw,hist.font);
801 hist.x = 10+GDrawGetTextWidth(gw,n9999,-1);
802 hist.hwidth = pos.width - 2*hist.x;
803 hist.y = 10; hist.hheight = pos.width-20;
804
805 memset(&gcd,0,sizeof(gcd));
806 memset(&label,0,sizeof(label));
807 memset(&boxes,0,sizeof(boxes));
808
809 i=0;
810 gcd[i].gd.pos.width = hist.x; gcd[i].gd.pos.height = 200;
811 gcd[i].gd.flags = gg_enabled|gg_visible|gg_pos_in_pixels;
812 gcd[i].gd.cid = CID_LeftSide;
813 gcd[i].gd.u.drawable_e_h = leftside_e_h;
814 gcd[i++].creator = GDrawableCreate;
815 hvbody[0][0] = &gcd[i-1];
816
817 gcd[i].gd.pos.width = hist.hwidth+1; gcd[i].gd.pos.height = 200;
818 gcd[i].gd.flags = gg_enabled|gg_visible|gg_pos_in_pixels;
819 gcd[i].gd.cid = CID_Histogram;
820 gcd[i].gd.u.drawable_e_h = histogram_e_h;
821 gcd[i++].creator = GDrawableCreate;
822 hvbody[0][1] = &gcd[i-1];
823
824 gcd[i].gd.pos.width = hist.x; gcd[i].gd.pos.height = 200;
825 gcd[i].gd.flags = gg_enabled|gg_visible|gg_pos_in_pixels;
826 gcd[i].gd.cid = CID_RightSide;
827 gcd[i].gd.u.drawable_e_h = rightside_e_h;
828 gcd[i++].creator = GDrawableCreate;
829 hvbody[0][2] = &gcd[i-1];
830 hvbody[0][3] = NULL;
831
832 hvbody[1][0] = GCD_Glue;
833 gcd[i].gd.pos.width = hist.hwidth+1;
834 gcd[i].gd.flags = gg_enabled|gg_visible|gg_pos_in_pixels;
835 gcd[i].gd.cid = CID_ScrollBar;
836 gcd[i++].creator = GScrollBarCreate;
837 hvbody[1][1] = &gcd[i-1];
838 hvbody[1][2] = GCD_Glue;
839 hvbody[1][3] = NULL;
840 hvbody[2][0] = NULL;
841
842 boxes[2].gd.flags = gg_enabled|gg_visible;
843 boxes[2].gd.u.boxelements = &hvbody[0][0];
844 boxes[2].creator = GHVBoxCreate;
845
846 label[i].text = (unichar_t *) _("Sum Around:");
847 label[i].text_is_1byte = true;
848 gcd[i].gd.label = &label[i];
849 gcd[i].gd.flags = gg_enabled|gg_visible;
850 gcd[i].gd.cid = CID_SumAroundL;
851 gcd[i++].creator = GLabelCreate;
852 hvctls[0][0] = &gcd[i-1];
853
854 sprintf(binsize,"%d", hist.sum_around);
855 label[i].text = (unichar_t *) binsize;
856 label[i].text_is_1byte = true;
857 gcd[i].gd.label = &label[i];
858 gcd[i].gd.pos.width = 30;
859 gcd[i].gd.flags = gg_enabled|gg_visible;
860 gcd[i].gd.cid = CID_SumAround;
861 gcd[i++].creator = GTextFieldCreate;
862 hvctls[0][1] = &gcd[i-1];
863
864 label[i].text = (unichar_t *) _("Bar Width:");
865 label[i].text_is_1byte = true;
866 gcd[i].gd.label = &label[i];
867 gcd[i].gd.flags = gg_enabled|gg_visible;
868 gcd[i].gd.cid = CID_BarWidthL;
869 gcd[i++].creator = GLabelCreate;
870 hvctls[0][2] = &gcd[i-1];
871
872 sprintf(barwidth,"%d", hist.barwidth);
873 label[i].text = (unichar_t *) barwidth;
874 label[i].text_is_1byte = true;
875 gcd[i].gd.label = &label[i];
876 gcd[i].gd.pos.width = 30;
877 gcd[i].gd.flags = gg_enabled|gg_visible;
878 gcd[i].gd.cid = CID_BarWidth;
879 gcd[i++].creator = GTextFieldCreate;
880 hvctls[0][3] = &gcd[i-1];
881 hvctls[0][4] = NULL;
882
883 label[i].text = (unichar_t *) _("BlueValues come in pairs. Select another.");
884 label[i].text_is_1byte = true;
885 label[i].fg = 0xff0000;
886 label[i].bg = GDrawGetDefaultBackground(NULL);
887 gcd[i].gd.label = &label[i];
888 gcd[i].gd.flags = gg_enabled;
889 gcd[i].gd.cid = CID_BlueMsg;
890 gcd[i++].creator = GLabelCreate;
891 hvctls[1][0] = &gcd[i-1];
892 hvctls[1][1] = hvctls[1][2] = hvctls[1][3] = GCD_ColSpan;
893 hvctls[1][4] = NULL;
894
895 switch ( which ) {
896 case hist_hstem:
897 label[i].text = (unichar_t *) "StdHW:";
898 label[i+2].text = (unichar_t *) "StemSnapH:";
899 primary = "StdHW"; secondary = "StemSnapH";
900 break;
901 case hist_vstem:
902 label[i].text = (unichar_t *) "StdVW:";
903 label[i+2].text = (unichar_t *) "StemSnapV:";
904 primary = "StdVW"; secondary = "StemSnapV";
905 break;
906 case hist_blues:
907 label[i].text = (unichar_t *) "BlueValues:";
908 label[i+2].text = (unichar_t *) "OtherBlues:";
909 primary = "BlueValues"; secondary = "OtherBlues";
910 break;
911 }
912 label[i].text_is_1byte = true;
913 gcd[i].gd.label = &label[i];
914 gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = gcd[i-2].gd.pos.y+28;
915 gcd[i].gd.flags = gg_enabled|gg_visible;
916 gcd[i].gd.cid = CID_MainValL;
917 gcd[i++].creator = GLabelCreate;
918 hvctls[2][0] = &gcd[i-1];
919
920 if ( private!=NULL && (j=PSDictFindEntry(private,primary))!=-1 ) {
921 label[i].text = (unichar_t *) private->values[j];
922 label[i].text_is_1byte = true;
923 gcd[i].gd.label = &label[i];
924 }
925 gcd[i].gd.pos.x = 64; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y-4;
926 gcd[i].gd.pos.width = 140;
927 gcd[i].gd.flags = gg_enabled|gg_visible;
928 gcd[i].gd.cid = CID_MainVal;
929 gcd[i++].creator = GTextFieldCreate;
930 hvctls[2][1] = &gcd[i-1];
931 hvctls[2][2] = hvctls[2][3] = GCD_ColSpan;
932 hvctls[2][4] = NULL;
933
934 label[i].text_is_1byte = true;
935 gcd[i].gd.label = &label[i];
936 gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y+28;
937 gcd[i].gd.flags = gg_enabled|gg_visible;
938 gcd[i].gd.cid = CID_SecondaryValL;
939 gcd[i++].creator = GLabelCreate;
940 hvctls[3][0] = &gcd[i-1];
941
942 if ( private!=NULL && (j=PSDictFindEntry(private,secondary))!=-1 ) {
943 label[i].text = (unichar_t *) private->values[j];
944 label[i].text_is_1byte = true;
945 gcd[i].gd.label = &label[i];
946 }
947 gcd[i].gd.pos.x = 64; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y-4;
948 gcd[i].gd.pos.width = 140;
949 gcd[i].gd.flags = gg_enabled|gg_visible;
950 gcd[i].gd.cid = CID_SecondaryVal;
951 gcd[i++].creator = GTextFieldCreate;
952 hvctls[3][1] = &gcd[i-1];
953 hvctls[3][2] = hvctls[3][3] = GCD_ColSpan;
954 hvctls[3][4] = NULL;
955 hvctls[4][0] = NULL;
956
957 boxes[3].gd.flags = gg_enabled|gg_visible;
958 boxes[3].gd.u.boxelements = &hvctls[0][0];
959 boxes[3].creator = GHVBoxCreate;
960
961 gcd[i].gd.flags = gg_visible | gg_enabled | gg_but_default;
962 label[i].text = (unichar_t *) _("_OK");
963 label[i].text_is_1byte = true;
964 label[i].text_in_resource = true;
965 gcd[i].gd.label = &label[i];
966 gcd[i].gd.cid = CID_OK;
967 gcd[i++].creator = GButtonCreate;
968 butarray[0] = GCD_Glue; butarray[1] = &gcd[i-1]; butarray[2] = GCD_Glue; butarray[3] = GCD_Glue;
969
970 gcd[i].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
971 label[i].text = (unichar_t *) _("_Cancel");
972 label[i].text_is_1byte = true;
973 label[i].text_in_resource = true;
974 gcd[i].gd.label = &label[i];
975 gcd[i].gd.mnemonic = 'C';
976 gcd[i].gd.cid = CID_Cancel;
977 gcd[i++].creator = GButtonCreate;
978 butarray[4] = GCD_Glue; butarray[5] = &gcd[i-1]; butarray[6] = GCD_Glue; butarray[7] = NULL;
979
980 boxes[4].gd.flags = gg_enabled|gg_visible;
981 boxes[4].gd.u.boxelements = &butarray[0];
982 boxes[4].creator = GHBoxCreate;
983
984 hv[0][0] = &boxes[2]; hv[0][1] = NULL;
985 hv[1][0] = &boxes[3]; hv[1][1] = NULL;
986 hv[2][0] = &boxes[4]; hv[2][1] = NULL; hv[3][0] = NULL;
987
988 boxes[0].gd.pos.x = boxes[0].gd.pos.y = 2;
989 boxes[0].gd.flags = gg_enabled|gg_visible;
990 boxes[0].gd.u.boxelements = &hv[0][0];
991 boxes[0].creator = GHVGroupCreate;
992
993 GGadgetsCreate(gw,boxes);
994
995 GHVBoxSetExpandableRow(boxes[0].ret,0);
996 GHVBoxSetExpandableCol(boxes[2].ret,1);
997 GHVBoxSetExpandableRow(boxes[2].ret,0);
998 GHVBoxSetExpandableCol(boxes[3].ret,1);
999 GHVBoxSetExpandableCol(boxes[4].ret,gb_expandglue);
1000
1001 hist.hoff = 0;
1002 if ( hist.h->low>0 )
1003 hist.hoff = hist.h->low;
1004 GScrollBarSetPos(GWidgetGetControl(hist.gw,CID_ScrollBar),hist.hoff);
1005 HistRefigureSB(&hist);
1006
1007 GHVBoxFitWindow(boxes[0].ret);
1008 GDrawSetVisible(gw,true);
1009 while ( !hist.done )
1010 GDrawProcessOneEvent(NULL);
1011 GDrawDestroyWindow(gw);
1012
1013 HistDataFree(hist.h);
1014 }
1015