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