1 /* Copyright (C) 2000-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 #include <fontforge-version-extras.h>
30
31 #include "autosave.h"
32 #include "bitmapchar.h"
33 #include "clipnoui.h"
34 #include "encoding.h"
35 #include "ffgdk.h"
36 #include "ffglib.h"
37 #include "fontforgeui.h"
38 #include "gfile.h"
39 #include "gresource.h"
40 #include "hotkeys.h"
41 #include "lookups.h"
42 #include "prefs.h"
43 #include "start.h"
44 #include "ustring.h"
45
46 #include <locale.h>
47 #include <stdlib.h> /* getenv,setenv */
48 #include <sys/stat.h>
49 #include <sys/time.h>
50 #include <sys/types.h>
51 #include <time.h>
52 #include <unistd.h>
53
54 #if defined(__MINGW32__)
55 #include <windows.h>
56 #define sleep(n) Sleep(1000 * (n))
57 #endif
58
59 #ifndef _NO_LIBUNICODENAMES
60 #include <libunicodenames.h> /* need to open a database when we start */
61 extern uninm_names_db names_db; /* Unicode character names and annotations database */
62 extern uninm_blocks_db blocks_db;
63 #endif
64
65 #ifdef __Mac
66 extern void setup_cocoa_app();
67 #endif
68
69 #ifdef _NO_LIBPNG
70 # define PNGLIBNAME "libpng"
71 #else
72 # include <png.h> /* for version number to find up shared image name */
73 # if !defined(PNG_LIBPNG_VER_MAJOR) || (PNG_LIBPNG_VER_MAJOR==1 && PNG_LIBPNG_VER_MINOR<2)
74 # define PNGLIBNAME "libpng"
75 # else
76 # define xstr(s) str(s)
77 # define str(s) #s
78 # define PNGLIBNAME "libpng" xstr(PNG_LIBPNG_VER_MAJOR) xstr(PNG_LIBPNG_VER_MINOR)
79 # endif
80 #endif
81 #ifdef __Mac
82 # include "carbon.h"
83 /* For reasons obscure to me RunApplicationEventLoop is not defined in */
84 /* the mac header files if we are in 64 bit mode. Strangely it seems to */
85 /* be in the libraries and functional */
86 /*
87 * It was found in Dec 2014 that using RunApplicationEventLoop() could induce strange
88 * and extremely frustrating pausing issues on osx. The main generic event handling
89 * seems to work just fine, so there doesn't seem to be a need for this specialized
90 * Application Event Loop.
91 *
92 * See this issue bringing back Breakpad usage and the issues linked in comments 2,3
93 * by adrientetar:
94 * https://github.com/fontforge/fontforge/issues/2120
95 */
96 //# if __LP64__
97 //extern void RunApplicationEventLoop(void);
98 //# endif
99 #endif
100
101 // Must be included after png.h because it messes with setjmp
102 #include "scripting.h"
103
104 extern int AutoSaveFrequency;
105 int splash = 1;
106 static int localsplash;
107 static int unique = 0;
108
109 /**
110 * In osx versions prior to 10.9.x a special -psn_ flag was supplied
111 * when fontforge was run by osx in some cases. For opening an sfd
112 * file from finder we need to register the openWith event in order to
113 * get the name of the file to open. So it makes sense to always
114 * register for Apple events on OSX so that we can get those file
115 * names as they come through.
116 */
117 #if defined(__Mac)
118 static int listen_to_apple_events = true; // This was once true, but Apple broke it.
119 #else
120 static int listen_to_apple_events = false;
121 #endif
122 static bool ProcessPythonInitFiles = 1;
123
_dousage(void)124 static void _dousage(void) {
125 printf( "fontforge [options] [fontfiles]\n" );
126 printf( "\t-new\t\t\t (creates a new font)\n" );
127 printf( "\t-last\t\t\t (loads the last sfd file closed)\n" );
128 #if HANYANG
129 printf( "\t-newkorean\t\t (creates a new korean font)\n" );
130 #endif
131 printf( "\t-recover none|auto|inquire|clean (control error recovery)\n" );
132 printf( "\t-allglyphs\t\t (load all glyphs in the 'glyf' table\n\t\t\t of a truetype collection)\n" );
133 printf( "\t-nosplash\t\t (no splash screen)\n" );
134 printf( "\t-quiet\t\t\t (don't print non-essential information to stderr)\n" );
135 printf( "\t-unique\t\t\t (if a fontforge is already running open\n\t\t\t all arguments in it and have this process exit)\n" );
136 printf( "\t-display display-name\t (sets the X display)\n" );
137 printf( "\t-depth val\t\t (sets the display depth if possible)\n" );
138 printf( "\t-vc val\t\t\t (sets the visual class if possible)\n" );
139 printf( "\t-cmap current|copy|private\t (sets the type of colormap)\n" );
140 printf( "\t-dontopenxdevices\t (in case that fails)\n" );
141 printf( "\t-sync\t\t\t (syncs the display, debugging)\n" );
142 printf( "\t-keyboard ibm|mac|sun|ppc (generates appropriate hotkeys in menus)\n" );
143 #if MyMemory
144 printf( "\t-memory\t\t\t (turns on memory checks, debugging)\n" );
145 #endif
146 #ifndef _NO_LIBCAIRO
147 printf( "\t-usecairo=yes|no Use (or not) the cairo library for drawing\n" );
148 #endif
149 printf( "\t-help\t\t\t (displays this message, and exits)\n" );
150 printf( "\t-docs\t\t\t (displays this message, invokes a browser)\n\t\t\t\t (Using the BROWSER environment variable)\n" );
151 printf( "\t-version\t\t (prints the version of fontforge and exits)\n" );
152 #ifndef _NO_PYTHON
153 printf( "\t-lang=py\t\t use python for scripts (may precede -script)\n" );
154 #endif
155 #ifndef _NO_FFSCRIPT
156 printf( "\t-lang=ff\t\t use fontforge's legacy scripting language\n" );
157 #endif
158 printf( "\t-script scriptfile\t (executes scriptfile)\n" );
159 printf( "\t\tmust be the first option (or follow -lang).\n" );
160 printf( "\t\tAll others passed to scriptfile.\n" );
161 printf( "\t-dry scriptfile\t\t (syntax checks scriptfile)\n" );
162 printf( "\t\tmust be the first option. All others passed to scriptfile.\n" );
163 printf( "\t\tOnly for fontforge's own scripting language, not python.\n" );
164 printf( "\t-c script-string\t (executes argument as scripting cmds)\n" );
165 printf( "\t\tmust be the first option. All others passed to the script.\n" );
166 printf( "\n" );
167 printf( "FontForge will read postscript (pfa, pfb, ps, cid), opentype (otf),\n" );
168 printf( "\ttruetype (ttf,ttc), macintosh resource fonts (dfont,bin,hqx),\n" );
169 printf( "\tand bdf and pcf fonts. It will also read its own format --\n" );
170 printf( "\tsfd files.\n" );
171 printf( "If no fontfiles are specified (and -new is not either and there's nothing\n" );
172 printf( "\tto recover) then fontforge will produce an open font dlg.\n" );
173 printf( "If a scriptfile is specified then FontForge will not open the X display\n" );
174 printf( "\tnor will it process any additional arguments. It will execute the\n" );
175 printf( "\tscriptfile and give it any remaining arguments\n" );
176 printf( "If the first argument is an executable filename, and that file's first\n" );
177 printf( "\tline contains \"fontforge\" then it will be treated as a scriptfile.\n\n" );
178 printf( "For more information see:\n\thttp://fontforge.sourceforge.net/\n" );
179 printf( "Send bug reports to:\tfontforge-devel@lists.sourceforge.net\n" );
180 }
181
dousage(void)182 static void dousage(void) {
183 _dousage();
184 exit(0);
185 }
186
dohelp(void)187 static void dohelp(void) {
188 _dousage();
189 help("index.html", NULL);
190 exit(0);
191 }
192
193 struct delayed_event {
194 void *data;
195 void (*func)(void *);
196 };
197
BuildCharHook(GDisplay * gd)198 static void BuildCharHook(GDisplay *gd) {
199 GWidgetCreateInsChar();
200 }
201
InsCharHook(GDisplay * gd,unichar_t ch)202 static void InsCharHook(GDisplay *gd,unichar_t ch) {
203 GInsCharSetChar(ch);
204 }
205
206 extern GImage splashimage_legacy;
207 static GImage *splashimagep;
208 static GWindow splashw;
209 static GTimer *autosave_timer, *splasht;
210 static GFont *splash_font, *splash_italic, *splash_mono;
211 static int as,fh, linecnt;
212 static unichar_t msg[546];
213 static unichar_t *lines[32], *is, *ie;
214
SplashImageInit()215 static void SplashImageInit() {
216 if (splashimagep == NULL)
217 splashimagep = GGadgetImageCache("splash2020.png");
218 if (splashimagep == NULL)
219 splashimagep = &splashimage_legacy;
220 return;
221 }
222
ShowAboutScreen(void)223 void ShowAboutScreen(void) {
224 static int first=1;
225
226 if ( first ) {
227 GDrawResize(splashw,splashimagep->u.image->width,splashimagep->u.image->height+linecnt*fh);
228 first = false;
229 }
230
231 if ( splasht!=NULL )
232 GDrawCancelTimer(splasht);
233 splasht=NULL;
234
235 GDrawSetVisible(splashw,true);
236 }
237
SplashLayout()238 static void SplashLayout() {
239 unichar_t *start, *pt, *lastspace;
240
241 SplashImageInit();
242
243 u_strcpy(msg, utf82u_copy("As he drew closer to completing his book on Renaissance printing (The Craft of Printing and the Publication of Shakespeare’s Works), George Williams IV suggested that his son, George Williams V, write a chapter on computer typography. FontForge—previously called PfaEdit—was his response."));
244
245 GDrawSetFont(splashw,splash_font);
246 linecnt = 0;
247 lines[linecnt++] = msg-1;
248 for ( start = msg; *start!='\0'; start = pt ) {
249 lastspace = NULL;
250 for ( pt=start; ; ++pt ) {
251 if ( *pt==' ' || *pt=='\0' ) {
252 if ( GDrawGetTextWidth(splashw,start,pt-start)<splashimagep->u.image->width-10 )
253 lastspace = pt;
254 else
255 break;
256 if ( *pt=='\0' )
257 break;
258 }
259 }
260 if ( lastspace!=NULL )
261 pt = lastspace;
262 lines[linecnt++] = pt;
263 if ( *pt ) ++pt;
264 }
265
266 uc_strcpy(pt," ");
267 pt += u_strlen(pt);
268 lines[linecnt++] = pt;
269
270 uc_strcpy(pt, " As of 2012 FontForge development continues on GitHub.");
271 pt += u_strlen(pt);
272 lines[linecnt++] = pt;
273
274 uc_strcpy(pt," ");
275 pt += u_strlen(pt);
276 lines[linecnt++] = pt;
277
278 uc_strcpy(pt," Version:");
279 uc_strcat(pt,FONTFORGE_VERSION);
280 pt += u_strlen(pt);
281 lines[linecnt++] = pt;
282
283 // Can be empty if e.g. building from a tarball
284 if (FONTFORGE_GIT_VERSION[0] != '\0') {
285 uc_strcpy(pt," ");
286 uc_strcat(pt, FONTFORGE_GIT_VERSION);
287 pt += u_strlen(pt);
288 lines[linecnt++] = pt;
289 }
290
291 uc_strcat(pt," Built: ");
292 uc_strcat(pt,FONTFORGE_MODTIME_STR);
293 uc_strcat(pt,"-ML");
294 #ifdef FREETYPE_HAS_DEBUGGER
295 uc_strcat(pt,"-TtfDb");
296 #endif
297 #ifdef _NO_PYTHON
298 uc_strcat(pt,"-NoPython");
299 #endif
300 #ifdef FONTFORGE_CONFIG_USE_DOUBLE
301 uc_strcat(pt,"-D");
302 #endif
303 #ifdef FONTFORGE_CAN_USE_GDK
304 uc_strcat(pt, "-GDK3");
305 #else
306 uc_strcat(pt,"-X11");
307 #endif
308 pt += u_strlen(pt);
309 lines[linecnt++] = pt;
310 lines[linecnt] = NULL;
311 is = u_strchr(msg,'(')+1;
312 ie = u_strchr(msg,')');
313 }
314
DelayEvent(void (* func)(void *),void * data)315 void DelayEvent(void (*func)(void *), void *data) {
316 struct delayed_event *info = calloc(1,sizeof(struct delayed_event));
317
318 info->data = data;
319 info->func = func;
320 GDrawRequestTimer(splashw,100,0,info);
321 }
322
DoDelayedEvents(GEvent * event)323 static void DoDelayedEvents(GEvent *event) {
324 GTimer *t = event->u.timer.timer;
325 struct delayed_event *info = (struct delayed_event *) (event->u.timer.userdata);
326
327 if ( info!=NULL ) {
328 (info->func)(info->data);
329 free(info);
330 }
331 GDrawCancelTimer(t);
332 }
333
334 struct argsstruct {
335 int next;
336 int argc;
337 char **argv;
338 int any;
339 };
340
SendNextArg(struct argsstruct * args)341 static void SendNextArg(struct argsstruct *args) {
342 int i;
343 char *msg;
344 static GTimer *timeout;
345
346 if ( timeout!=NULL ) {
347 GDrawCancelTimer(timeout);
348 timeout = NULL;
349 }
350
351 for ( i=args->next; i<args->argc; ++i ) {
352 if ( *args->argv[i]!='-' ||
353 strcmp(args->argv[i],"-quit")==0 || strcmp(args->argv[i],"--quit")==0 ||
354 strcmp(args->argv[i],"-new")==0 || strcmp(args->argv[i],"--new")==0 )
355 break;
356 }
357 if ( i>=args->argc ) {
358 if ( args->any )
359 exit(0); /* Sent everything */
360 msg = "-open";
361 } else
362 msg = args->argv[i];
363 args->next = i+1;
364 args->any = true;
365
366 GDrawGrabSelection(splashw,sn_user1);
367 GDrawAddSelectionType(splashw,sn_user1,"STRING",
368 copy(msg),strlen(msg),1,
369 NULL,NULL);
370
371 /* If we just sent the other fontforge a request to die, it will never*/
372 /* take the selection back. So we should just die quietly */
373 /* But we can't die instantly, or it will never get our death threat */
374 /* (it won't have a chance to ask us for the selection if we're dead)*/
375 timeout = GDrawRequestTimer(splashw,1000,0,NULL);
376 }
377
378 /* When we want to send filenames to another running fontforge we want a */
379 /* different event handler. We won't have a splash window in that case, */
380 /* just an invisible utility window on which we perform a little selection */
381 /* dance */
request_e_h(GWindow gw,GEvent * event)382 static int request_e_h(GWindow gw, GEvent *event) {
383
384 if ( event->type == et_selclear ) {
385 SendNextArg( GDrawGetUserData(gw));
386 } else if ( event->type == et_timer )
387 exit( 0 );
388
389 return( true );
390 }
391
PingOtherFontForge(int argc,char ** argv)392 static void PingOtherFontForge(int argc, char **argv) {
393 struct argsstruct args;
394
395 args.next = 1;
396 args.argc = argc;
397 args.argv = argv;
398 args.any = false;
399 GDrawSetUserData(splashw,&args);
400 SendNextArg(&args);
401 GDrawEventLoop(NULL);
402 exit( 0 ); /* But the event loop should never return */
403 }
404
start_splash_screen(void)405 static void start_splash_screen(void){
406 GDrawSetVisible(splashw,true);
407 GDrawSync(NULL);
408 GDrawProcessPendingEvents(NULL);
409 GDrawProcessPendingEvents(NULL);
410
411 splasht = GDrawRequestTimer(splashw,7000,1000,NULL);
412
413 localsplash = false;
414 }
415
416 #if defined(__Mac)
417 static FILE *logfile;
418
419 /* These are the four apple events to which we currently respond */
OpenApplicationAE(const AppleEvent * theAppleEvent,AppleEvent * reply,SRefCon handlerRefcon)420 static pascal OSErr OpenApplicationAE( const AppleEvent * theAppleEvent,
421 AppleEvent * reply, SRefCon handlerRefcon) {
422 fprintf( logfile, "OPENAPP event received.\n" ); fflush( logfile );
423 if ( localsplash )
424 start_splash_screen();
425 #ifndef FONTFORGE_CAN_USE_GDK
426 system( "DYLD_LIBRARY_PATH=\"\"; osascript -e 'tell application \"X11\" to activate'" );
427 #endif // FONTFORGE_CAN_USE_GDK
428 if ( fv_list==NULL )
429 _FVMenuOpen(NULL);
430 fprintf( logfile, " event processed %d.\n", noErr ); fflush( logfile );
431 return( noErr );
432 }
433
ReopenApplicationAE(const AppleEvent * theAppleEvent,AppleEvent * reply,SRefCon handlerRefcon)434 static pascal OSErr ReopenApplicationAE( const AppleEvent * theAppleEvent,
435 AppleEvent * reply, SRefCon handlerRefcon) {
436 fprintf( logfile, "ReOPEN event received.\n" ); fflush( logfile );
437 if ( localsplash )
438 start_splash_screen();
439 #ifndef FONTFORGE_CAN_USE_GDK
440 system( "DYLD_LIBRARY_PATH=\"\"; osascript -e 'tell application \"X11\" to activate'" );
441 #endif // FONTFORGE_CAN_USE_GDK
442 if ( fv_list==NULL )
443 _FVMenuOpen(NULL);
444 fprintf( logfile, " event processed %d.\n", noErr ); fflush( logfile );
445 return( noErr );
446 }
447
ShowPreferencesAE(const AppleEvent * theAppleEvent,AppleEvent * reply,SRefCon handlerRefcon)448 static pascal OSErr ShowPreferencesAE( const AppleEvent * theAppleEvent,
449 AppleEvent * reply, SRefCon handlerRefcon) {
450 fprintf( logfile, "PREFS event received.\n" ); fflush( logfile );
451 if ( localsplash )
452 start_splash_screen();
453 #ifndef FONTFORGE_CAN_USE_GDK
454 system( "DYLD_LIBRARY_PATH=\"\"; osascript -e 'tell application \"X11\" to activate'" );
455 #endif // FONTFORGE_CAN_USE_GDK
456 DoPrefs();
457 fprintf( logfile, " event processed %d.\n", noErr ); fflush( logfile );
458 return( noErr );
459 }
460
OpenDocumentsAE(const AppleEvent * theAppleEvent,AppleEvent * reply,SRefCon handlerRefcon)461 static pascal OSErr OpenDocumentsAE( const AppleEvent * theAppleEvent,
462 AppleEvent * reply, SRefCon handlerRefcon) {
463 AEDescList docList;
464 long index;
465 long count = 0;
466 OSErr err;
467 char buffer[2048];
468
469 fprintf( logfile, "OPEN event received.\n" ); fflush( logfile );
470 if ( localsplash )
471 start_splash_screen();
472
473 err = AEGetParamDesc(theAppleEvent, keyDirectObject,
474 typeAEList, &docList);
475 err = AECountItems(&docList, &count);
476 for(index = 1; index <= count; index++) {
477 AEDesc aDoc;
478 size_t bytecount;
479 void *pathPtr;
480 CFURLRef url;
481 err = AEGetNthDesc(&docList, index, typeFileURL, NULL, &aDoc);
482 if (err != noErr) {
483 continue;
484 }
485 bytecount = AEGetDescDataSize(&aDoc);
486 pathPtr = malloc(bytecount);
487 err = AEGetDescData(&aDoc, pathPtr, bytecount);
488 if (err != noErr) {
489 free(pathPtr);
490 continue;
491 }
492 url = CFURLCreateWithBytes(nil, pathPtr, bytecount,
493 kCFStringEncodingUTF8, nil);
494 free(pathPtr);
495 CFURLGetFileSystemRepresentation(url, true, (UInt8*)buffer, sizeof(buffer));
496 CFRelease(url);
497 ViewPostScriptFont(buffer,0);
498 fprintf( logfile, " file: %s\n", buffer );
499 }
500 #ifndef FONTFORGE_CAN_USE_GDK
501 system( "DYLD_LIBRARY_PATH=\"\"; osascript -e 'tell application \"X11\" to activate'" );
502 #endif // FONTFORGE_CAN_USE_GDK
503 AEDisposeDesc(&docList);
504 fprintf( logfile, " event processed %d.\n", err ); fflush( logfile );
505
506 return( err );
507 }
508
AttachErrorCode(AppleEvent * event,OSStatus err)509 static void AttachErrorCode(AppleEvent *event,OSStatus err) {
510 OSStatus returnVal;
511
512 if ( event==NULL )
513 return;
514
515 if (event->descriptorType != typeNull) {
516 /* Check there isn't already an error attached */
517 returnVal = AESizeOfParam(event, keyErrorNumber, NULL, NULL);
518 if (returnVal != noErr ) { /* Add success if no previous error */
519 AEPutParamPtr(event, keyErrorNumber,
520 typeSInt32, &err, sizeof(err));
521 }
522 }
523 }
524
525 static AppleEvent *quit_event = NULL;
we_are_dead(void)526 static void we_are_dead(void) {
527 AttachErrorCode(quit_event,noErr);
528 /* Send the reply (I hope) */
529 AESendMessage(quit_event,NULL, kAENoReply, kAEDefaultTimeout);
530 AEDisposeDesc(quit_event);
531 /* fall off the end of the world and die */
532 fprintf( logfile, " event succeded.\n"); fflush( logfile );
533 }
534
QuitApplicationAE(const AppleEvent * theAppleEvent,AppleEvent * reply,SInt32 handlerRefcon)535 static pascal OSErr QuitApplicationAE( const AppleEvent * theAppleEvent,
536 AppleEvent * reply, SInt32 handlerRefcon) {
537 static int first_time = true;
538
539 fprintf( logfile, "QUIT event received.\n" ); fflush( logfile );
540 quit_event = reply;
541 if ( first_time ) {
542 atexit( we_are_dead );
543 first_time = false;
544 }
545 MenuExit(NULL,NULL,NULL);
546 /* if we get here, they canceled the quit, so we return a failure */
547 quit_event = NULL;
548 fprintf( logfile, " event failed %d.\n", errAEEventFailed ); fflush( logfile );
549 return(errAEEventFailed);
550 }
551
552 /* Install event handlers for the Apple Events we care about */
install_apple_event_handlers(void)553 static OSErr install_apple_event_handlers(void) {
554 OSErr err;
555
556 err = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication,
557 NewAEEventHandlerUPP(OpenApplicationAE), 0, false);
558 __Require_noErr(err, CantInstallAppleEventHandler);
559
560 err = AEInstallEventHandler(kCoreEventClass, kAEReopenApplication,
561 NewAEEventHandlerUPP(ReopenApplicationAE), 0, false);
562 __Require_noErr(err, CantInstallAppleEventHandler);
563
564 err = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments,
565 NewAEEventHandlerUPP(OpenDocumentsAE), 0, false);
566 __Require_noErr(err, CantInstallAppleEventHandler);
567
568 err = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication,
569 NewAEEventHandlerUPP(QuitApplicationAE), 0, false);
570 __Require_noErr(err, CantInstallAppleEventHandler);
571
572 err = AEInstallEventHandler(kCoreEventClass, kAEShowPreferences,
573 NewAEEventHandlerUPP(ShowPreferencesAE), 0, false);
574 __Require_noErr(err, CantInstallAppleEventHandler);
575
576 /* some debugging code, for now */
577 if ( getenv("HOME")!=NULL ) {
578 char buffer[1024];
579 sprintf( buffer, "%s/.FontForge-LogFile.txt", getenv("HOME"));
580 logfile = fopen("/tmp/LogFile.txt","w");
581 }
582 if ( logfile==NULL )
583 logfile = stderr;
584
585 CantInstallAppleEventHandler:
586 return err;
587
588 }
589
DoRealStuff(EventLoopTimerRef timer,void * ignored_data)590 static pascal void DoRealStuff(EventLoopTimerRef timer,void *ignored_data) {
591 GDrawProcessPendingEvents(NULL);
592 }
593
install_mac_timer(void)594 static void install_mac_timer(void) {
595 EventLoopTimerRef timer;
596
597 InstallEventLoopTimer(GetMainEventLoop(),
598 .001*kEventDurationSecond,.001*kEventDurationSecond,
599 NewEventLoopTimerUPP(DoRealStuff), NULL,
600 &timer);
601 }
602 #endif
603
splash_e_h(GWindow gw,GEvent * event)604 static int splash_e_h(GWindow gw, GEvent *event) {
605 GRect old;
606 int i, y, x;
607 static char *foolishness[] = {
608 /* GT: These strings are for fun. If they are offensive or incomprehensible */
609 /* GT: simply translate them as something dull like: "FontForge" */
610 /* GT: This is a spoof of political slogans, designed to point out how foolish they are */
611 N_("A free press discriminates\nagainst the illiterate."),
612 N_("A free press discriminates\nagainst the illiterate."),
613 /* GT: This is a pun on the old latin drinking song "Gaudeamus igature!" */
614 N_("Gaudeamus Ligature!"),
615 N_("Gaudeamus Ligature!"),
616 /* GT: Spoof on the bible */
617 N_("In the beginning was the letter..."),
618 /* GT: Some wit at MIT came up with this ("ontology recapitulates phylogony" is the original) */
619 N_("fontology recapitulates file-ogeny")
620 };
621
622 switch ( event->type ) {
623 case et_create:
624 GDrawGrabSelection(gw,sn_user1);
625 break;
626 case et_expose:
627 GDrawPushClip(gw,&event->u.expose.rect,&old);
628 GDrawDrawImage(gw,splashimagep,NULL,0,0);
629 if ((event->u.expose.rect.y+event->u.expose.rect.height) > splashimagep->u.image->height) {
630 GDrawSetFont(gw,splash_font);
631 y = splashimagep->u.image->height + as + fh/2;
632 for ( i=1; i<linecnt; ++i ) {
633 // The number 10 comes from lines[linecnt] created in the function SplashLayout. It refers
634 // to the line at which we want to make the font monospace. If you add or remove a line,
635 // you will need to change this.
636 if (i == 10) {
637 x = 8+GDrawDrawText(gw,8,y,lines[i-1]+1,0,0x000000);
638 GDrawSetFont(gw,splash_mono);
639 GDrawDrawText(gw,8,y,lines[i-1]+1,lines[i]-lines[i-1]-1,0x000000);
640 } else if ( is>=lines[i-1]+1 && is<lines[i] ) {
641 x = 8+GDrawDrawText(gw,8,y,lines[i-1]+1,is-lines[i-1]-1,0x000000);
642 GDrawSetFont(gw,splash_italic);
643 GDrawDrawText(gw,x,y,is,lines[i]-is,0x000000);
644 } else if ( ie>=lines[i-1]+1 && ie<lines[i] ) {
645 x = 8+GDrawDrawText(gw,8,y,lines[i-1]+1,ie-lines[i-1]-1,0x000000);
646 GDrawSetFont(gw,splash_font);
647 GDrawDrawText(gw,x,y,ie,lines[i]-ie,0x000000);
648 } else
649 GDrawDrawText(gw,8,y,lines[i-1]+1,lines[i]-lines[i-1]-1,0x000000);
650 y += fh;
651 }
652 }
653 GDrawPopClip(gw,&old);
654 break;
655 case et_map:
656 // The splash screen used to gradually resize the longer it was displayed.
657 // This was removed. But there is a gxdraw bug which prevents
658 // the splash from being displayed properly unless a resize occurs.
659 // So this forces a resize to make it display properly...
660 GDrawGetSize(gw, &old);
661 if (old.height < splashimagep->u.image->height) {
662 GDrawResize(gw,splashimagep->u.image->width,splashimagep->u.image->height);
663 }
664 break;
665 case et_timer:
666 if ( event->u.timer.timer==autosave_timer ) {
667 DoAutoSaves();
668 } else if ( event->u.timer.timer==splasht ) {
669 GGadgetEndPopup();
670 GDrawSetVisible(gw,false);
671 GDrawCancelTimer(splasht);
672 splasht = NULL;
673 } else {
674 DoDelayedEvents(event);
675 }
676 break;
677 case et_char:
678 case et_mousedown:
679 case et_close:
680 GGadgetEndPopup();
681 GDrawSetVisible(gw,false);
682 break;
683 case et_mousemove:
684 GGadgetPreparePopup8(gw,_(foolishness[rand()%(sizeof(foolishness)/sizeof(foolishness[0]))]) );
685 break;
686 case et_selclear:
687 /* If this happens, it means someone wants to send us a message with a*/
688 /* filename to open. So we need to ask for it, process it, and then */
689 /* take the selection back again */
690 if ( event->u.selclear.sel == sn_user1 ) {
691 int len;
692 char *arg;
693 arg = GDrawRequestSelection(splashw,sn_user1,"STRING",&len);
694 if ( arg==NULL )
695 return( true );
696 if ( strcmp(arg,"-new")==0 || strcmp(arg,"--new")==0 )
697 FontNew();
698 else if ( strcmp(arg,"-open")==0 || strcmp(arg,"--open")==0 )
699 _FVMenuOpen(NULL);
700 else if ( strcmp(arg,"-quit")==0 || strcmp(arg,"--quit")==0 )
701 MenuExit(NULL,NULL,NULL);
702 else
703 ViewPostScriptFont(arg,0);
704 free(arg);
705 GDrawGrabSelection(splashw,sn_user1);
706 }
707 break;
708 case et_destroy:
709 IError("Who killed the splash screen?");
710 break;
711 }
712 return( true );
713 }
714
AddR(char * program_name,char * window_name,char * cmndline_val)715 static void AddR(char *program_name, char *window_name, char *cmndline_val) {
716 /* Add this command line value to this GUI resource. */
717 /* These are the command line options expected when using this routine: */
718 /* -depth, -vc,-cmap or -colormap,-dontopenxdevices, -keyboard */
719 char *full;
720 if ((full = malloc(strlen(window_name)+strlen(cmndline_val)+4))!=NULL) {
721 strcpy(full,window_name);
722 strcat(full,": ");
723 strcat(full,cmndline_val);
724 GResourceAddResourceString(full,program_name);
725 free(full);
726 }
727 }
728
ReopenLastFonts(void)729 static int ReopenLastFonts(void) {
730 char buffer[1024];
731 char *ffdir = getFontForgeUserDir(Config);
732 FILE *old;
733 int any = 0;
734
735 if ( ffdir==NULL ) return false;
736
737 sprintf( buffer, "%s/FontsOpenAtLastQuit", ffdir );
738 old = fopen(buffer,"r");
739 if ( old==NULL ) {
740 free(ffdir);
741 return false;
742 }
743 while ( fgets(buffer,sizeof(buffer),old)!=NULL ) {
744 if ( ViewPostScriptFont(g_strchomp(buffer),0)!=0 )
745 any = 1;
746 }
747 fclose(old);
748 free(ffdir);
749 return any;
750 }
751
752 #if defined(__Mac)
753 /* Read a property from the x11 properties files */
754 /* At the moment we want to know if we get the command key, or if the menubar */
755 /* eats it */
get_mac_x11_prop(char * keystr)756 static int get_mac_x11_prop(char *keystr) {
757 CFPropertyListRef ret;
758 CFStringRef key, appID;
759 int val;
760
761 appID = CFSTR("com.apple.x11");
762 key = CFStringCreateWithBytes(NULL,(uint8 *) keystr,strlen(keystr), kCFStringEncodingISOLatin1, 0);
763 ret = CFPreferencesCopyAppValue(key,appID);
764 if ( ret==NULL ) {
765 /* Sigh. Apple uses a different preference file under 10.5.6 I really */
766 /* wish they'd stop making stupid, unnecessary changes */
767 appID = CFSTR("org.x.X11");
768 ret = CFPreferencesCopyAppValue(key,appID);
769 }
770 CFRelease(key);
771 if ( ret==NULL )
772 return( -1 );
773 if ( CFGetTypeID(ret)!=CFBooleanGetTypeID()) {
774 CFRelease(ret);
775 return( -2 );
776 }
777 val = CFBooleanGetValue(ret);
778 CFRelease(ret);
779 return( val );
780 }
781
uses_local_x(int argc,char ** argv)782 static int uses_local_x(int argc,char **argv) {
783 int i;
784 char *arg;
785
786 for ( i=1; i<argc; ++i ) {
787 arg = argv[i];
788 if ( *arg=='-' ) {
789 if ( arg[0]=='-' && arg[1]=='-' && arg[2]!='\0')
790 ++arg;
791 if ( strcmp(arg,"-display")==0 )
792 return( i+1<argc && strcmp(argv[i+1],":0")!=0 && strcmp(argv[i+1],":0.0")!=0? 2 : 0 );
793 if ( strcmp(argv[i],"-c")==0 )
794 return( false ); /* we use a script string, no x display at all */
795 if ( strcmp(arg,"-script")==0 )
796 return( false ); /* we use a script, no x display at all */
797 if ( strcmp(argv[i],"-")==0 )
798 return( false ); /* script on stdin */
799 } else {
800 /* Is this argument a script file ? */
801 FILE *temp = fopen(argv[i],"r");
802 char buffer[200];
803 if ( temp==NULL )
804 return( true ); /* not a script file, so need local local X */
805 buffer[0] = '\0';
806 fgets(buffer,sizeof(buffer),temp);
807 fclose(temp);
808 if ( buffer[0]=='#' && buffer[1]=='!' &&
809 (strstr(buffer,"pfaedit")!=NULL || strstr(buffer,"fontforge")!=NULL )) {
810 return( false ); /* is a script file, so no need for X */
811
812 return( true ); /* not a script, so needs X */
813 }
814 }
815 }
816 return( true );
817 }
818 #endif
819
820
821 #if defined(__Mac)
hasquit(int argc,char ** argv)822 static int hasquit( int argc, char **argv ) {
823 int i;
824
825 for ( i=1; i<argc; ++i )
826 if ( strcmp(argv[i],"-quit")==0 || strcmp(argv[i],"--quit")==0 )
827 return( true );
828
829 return( false );
830 }
831 #endif
832
GrokNavigationMask(void)833 static void GrokNavigationMask(void) {
834 extern int navigation_mask;
835
836 navigation_mask = GMenuItemParseMask(H_("NavigationMask|None"));
837 }
838
839 /**
840 * Create the directory basedir/dirname with the given mode.
841 * Silently ignore any errors that might happen.
842 */
ffensuredir(const char * basedir,const char * dirname,mode_t mode)843 static void ffensuredir( const char* basedir, const char* dirname, mode_t mode ) {
844 const int buffersz = PATH_MAX;
845 char buffer[buffersz+1];
846
847 snprintf(buffer,buffersz,"%s/%s", basedir, dirname );
848 // ignore errors, this is just to help the user aftre all.
849 GFileMkDir( buffer, mode );
850 }
851
ensureDotFontForgeIsSetup()852 static void ensureDotFontForgeIsSetup() {
853 char *basedir = getFontForgeUserDir(Config);
854 if ( !basedir ) {
855 return;
856 }
857 ffensuredir( basedir, "", S_IRWXU );
858 ffensuredir( basedir, "python", S_IRWXU );
859 free(basedir);
860 }
861
DoAutoRecoveryPostRecover_PromptUserGraphically(SplineFont * sf)862 static void DoAutoRecoveryPostRecover_PromptUserGraphically(SplineFont *sf)
863 {
864 /* Ask user to save-as file */
865 char *buts[4];
866 buts[0] = _("_OK");
867 buts[1] = 0;
868 gwwv_ask( _("Recovery Complete"),(const char **) buts,0,1,_("Your file %s has been recovered.\nYou must now Save your file to continue working on it."), sf->filename );
869 _FVMenuSaveAs( (FontView*)sf->fv );
870 }
871
872 #if defined(__MINGW32__) && !defined(_NO_LIBCAIRO)
873 /**
874 * \brief Load fonts from the specified folder for the UI to use.
875 * This should only be used if Cairo is used on Windows, which defaults to the
876 * Win32 font backend.
877 * This is an ANSI version, so files which contain characters outside of the
878 * user's locale will fail to be loaded.
879 * \param prefix The folder to read fonts from. Currently the pixmaps folder
880 * and the folder 'ui-fonts' in the FontForge preferences folder.
881 */
WinLoadUserFonts(const char * prefix)882 static void WinLoadUserFonts(const char *prefix) {
883 HANDLE fileHandle;
884 WIN32_FIND_DATA fileData;
885 char path[MAX_PATH], *ext;
886 HRESULT ret;
887 int i;
888
889 if (prefix == NULL) {
890 return;
891 }
892 ret = snprintf(path, MAX_PATH, "%s/*.???", prefix);
893 if (ret <= 0 || ret >= MAX_PATH) {
894 return;
895 }
896
897 fileHandle = FindFirstFileA(path, &fileData);
898 if (fileHandle != INVALID_HANDLE_VALUE) do {
899 ext = strrchr(fileData.cFileName, '.');
900 if (!ext || (strcasecmp(ext, ".ttf") && strcasecmp(ext, ".ttc") &&
901 strcasecmp(ext,".otf")))
902 {
903 continue;
904 }
905 ret = snprintf(path, MAX_PATH, "%s/%s", prefix, fileData.cFileName);
906 if (ret > 0 && ret < MAX_PATH) {
907 //printf("WIN32-FONT-TEST: %s\n", path);
908 ret = AddFontResourceExA(path, FR_PRIVATE, NULL);
909 //if (ret > 0) {
910 // printf("\tLOADED FONT OK!\n");
911 //}
912 }
913 } while (FindNextFileA(fileHandle, &fileData) != 0);
914 }
915 #endif
916
917
fontforge_main(int argc,char ** argv)918 int fontforge_main( int argc, char **argv ) {
919 const char *load_prefs = getenv("FONTFORGE_LOADPREFS");
920 int i;
921 int recover=2;
922 int any;
923 int next_recent=0;
924 GRect pos;
925 GWindowAttrs wattrs;
926 char *display = NULL;
927 FontRequest rq;
928 int ds, ld;
929 int openflags=0;
930 int doopen=0, quit_request=0;
931 bool use_cairo = true;
932
933 #if !(GLIB_CHECK_VERSION(2, 35, 0))
934 g_type_init();
935 #endif
936
937 /* Must be done before we cache the current directory */
938 /* Change to HOME dir if specified on the commandline */
939 for ( i=1; i<argc; ++i ) {
940 char *pt = argv[i];
941 if ( pt[0]=='-' && pt[1]=='-' ) ++pt;
942 if (strcmp(pt,"-home")==0 || strncmp(pt,"-psn_",5)==0) {
943 /* OK, I don't know what _-psn_ means, but to GW it means */
944 /* we've been started on the mac from the FontForge.app */
945 /* structure, and the current directory is (shudder) "/" */
946 if (getenv("HOME")!=NULL) chdir(getenv("HOME"));
947 break; /* Done - Unnecessary to check more arguments */
948 }
949 if (strcmp(pt,"-quiet")==0)
950 quiet = 1;
951 }
952
953 if (!quiet) {
954 time_t tm = FONTFORGE_MODTIME_RAW;
955 struct tm* modtime = gmtime(&tm);
956 fprintf( stderr, "Copyright (c) 2000-%d. See AUTHORS for Contributors.\n", modtime->tm_year+1900 );
957 fprintf( stderr, " License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n" );
958 fprintf( stderr, " with many parts BSD <http://fontforge.org/license.html>. Please read LICENSE.\n" );
959 fprintf( stderr, " Version: %s\n", FONTFORGE_VERSION );
960 fprintf( stderr, " Based on sources from %s"
961 "-ML"
962 #ifdef FREETYPE_HAS_DEBUGGER
963 "-TtfDb"
964 #endif
965 #ifdef _NO_PYTHON
966 "-NoPython"
967 #endif
968 #ifdef FONTFORGE_CONFIG_USE_DOUBLE
969 "-D"
970 #endif
971 #ifdef FONTFORGE_CAN_USE_GDK
972 "-GDK3"
973 #endif
974 #ifdef BUILT_WITH_XORG
975 "-Xorg"
976 #endif
977 ".\n",
978 FONTFORGE_MODTIME_STR );
979 // Can be empty if e.g. building from a tarball
980 if (FONTFORGE_GIT_VERSION[0] != '\0') {
981 fprintf( stderr, " Based on source from git with hash: %s\n", FONTFORGE_GIT_VERSION );
982 }
983 }
984
985 #if defined(__Mac) && !defined(FONTFORGE_CAN_USE_GDK)
986 /* Start X if they haven't already done so. Well... try anyway */
987 /* Must be before we change DYLD_LIBRARY_PATH or X won't start */
988 /* (osascript depends on a libjpeg which isn't found if we look in /sw/lib first */
989 int local_x = uses_local_x(argc,argv);
990 if ( local_x==1 && getenv("DISPLAY")==NULL ) {
991 /* Don't start X if we're just going to quit. */
992 /* if X exists, it isn't needed. If X doesn't exist it's wrong */
993 if ( !hasquit(argc,argv)) {
994 /* This sequence is supposed to bring up an app without a window */
995 /* but X still opens an xterm */
996 system( "osascript -e 'tell application \"X11\" to launch'" );
997 system( "osascript -e 'tell application \"X11\" to activate'" );
998 }
999 setenv("DISPLAY",":0.0",0);
1000 } else if ( local_x==1 && *getenv("DISPLAY")!='/' && strcmp(getenv("DISPLAY"),":0.0")!=0 && strcmp(getenv("DISPLAY"),":0")!=0 )
1001 /* 10.5.7 uses a named socket or something "/tmp/launch-01ftWX:0" */
1002 local_x = 0;
1003 #endif
1004
1005 #if defined(__MINGW32__)
1006 if( getenv("DISPLAY")==NULL ) {
1007 putenv("DISPLAY=127.0.0.1:0.0");
1008 }
1009 if( getenv("LC_ALL")==NULL ){
1010 char lang[8];
1011 char env[32];
1012 if( GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, lang, 8) > 0 ){
1013 strcpy(env, "LC_ALL=");
1014 strcat(env, lang);
1015 putenv(env);
1016 }
1017 }
1018 #endif
1019
1020 FF_SetUiInterface(&gdraw_ui_interface);
1021 FF_SetPrefsInterface(&gdraw_prefs_interface);
1022 FF_SetSCInterface(&gdraw_sc_interface);
1023 FF_SetCVInterface(&gdraw_cv_interface);
1024 FF_SetBCInterface(&gdraw_bc_interface);
1025 FF_SetFVInterface(&gdraw_fv_interface);
1026 FF_SetFIInterface(&gdraw_fi_interface);
1027 FF_SetMVInterface(&gdraw_mv_interface);
1028 FF_SetClipInterface(&gdraw_clip_interface);
1029 #ifndef _NO_PYTHON
1030 PythonUI_Init();
1031 #endif
1032
1033 FindProgDir(argv[0]);
1034 InitSimpleStuff();
1035
1036 #if defined(__MINGW32__)
1037 {
1038 char path[MAX_PATH];
1039 unsigned int len = GetModuleFileNameA(NULL, path, MAX_PATH);
1040 path[len] = '\0';
1041
1042 //The '.exe' must be removed as resources presumes it's not there.
1043 GResourceSetProg(GFileRemoveExtension(GFileNormalizePath(path)));
1044 }
1045 #else
1046 GResourceSetProg(argv[0]);
1047 #endif
1048
1049 #if defined(__Mac)
1050 /* The mac seems to default to the "C" locale, LANG and LC_MESSAGES are not*/
1051 /* defined. This means that gettext will not bother to look up any message*/
1052 /* files -- even if we have a "C" or "POSIX" entry in the locale diretory */
1053 /* Now if X11 gives us the command key, I want to force a rebinding to use */
1054 /* Cmd rather than Control key -- more mac-like. But I can't do that if */
1055 /* there is no locale. So I force a locale if there is none specified */
1056 /* I force the US English locale, because that's the what the messages are */
1057 /* by default so I'm changing as little as I can. I think. */
1058 /* Now the locale command will treat a LANG which is "" as undefined, but */
1059 /* gettext will not. So I don't bother to check for null strings or "C" */
1060 /* or "POSIX". If they've mucked with the locale perhaps they know what */
1061 /* they are doing */
1062 {
1063 #ifndef FONTFORGE_CAN_USE_GDK
1064 int useCommandKey = get_mac_x11_prop("enable_key_equivalents") <= 0;
1065
1066 if ( local_x && useCommandKey )
1067 #endif // FONTFORGE_CAN_USE_GDK
1068 {
1069 hotkeySystemSetCanUseMacCommand( 1 );
1070
1071 /* Ok, we get the command key */
1072 if ( getenv("LANG")==NULL && getenv("LC_MESSAGES")==NULL ) {
1073 setenv("LC_MESSAGES","en_US.UTF-8",0);
1074 }
1075 }
1076 }
1077 #endif // defined(__Mac)
1078
1079 GMenuSetShortcutDomain("FontForge-MenuShortCuts");
1080 bind_textdomain_codeset("FontForge-MenuShortCuts","UTF-8");
1081 bindtextdomain("FontForge-MenuShortCuts", getLocaleDir());
1082
1083 bind_textdomain_codeset("FontForge","UTF-8");
1084 bindtextdomain("FontForge", getLocaleDir());
1085 textdomain("FontForge");
1086 GResourceUseGetText();
1087 {
1088 char path[PATH_MAX];
1089 snprintf(path, PATH_MAX, "%s%s", getShareDir(), "/pixmaps" );
1090 GGadgetSetImageDir( path );
1091
1092 snprintf(path, PATH_MAX, "%s%s", getShareDir(), "/resources/fontforge.resource" );
1093 GResourceAddResourceFile(path, GResourceProgramName,false);
1094 }
1095 hotkeysLoad();
1096 // loadPrefsFiles();
1097 Prefs_LoadDefaultPreferences();
1098
1099 if ( load_prefs!=NULL && strcasecmp(load_prefs,"Always")==0 )
1100 LoadPrefs();
1101 if ( default_encoding==NULL )
1102 default_encoding=FindOrMakeEncoding("ISO8859-1");
1103 if ( default_encoding==NULL )
1104 default_encoding=&custom; /* In case iconv is broken */
1105
1106 // This no longer starts embedded Python unless control passes to the Python executors,
1107 // which exit independently rather than returning here.
1108 CheckIsScript(argc,argv); /* Will run the script and exit if it is a script */
1109 /* If there is no UI, there is always a script */
1110 /* and we will never return from the above */
1111 if ( load_prefs==NULL ||
1112 (strcasecmp(load_prefs,"Always")!=0 && /* Already loaded */
1113 strcasecmp(load_prefs,"Never")!=0 ))
1114 LoadPrefs();
1115 GrokNavigationMask();
1116 for ( i=1; i<argc; ++i ) {
1117 char *pt = argv[i];
1118 if ( pt[0]=='-' && pt[1]=='-' )
1119 ++pt;
1120 if ( strcmp(pt,"-sync")==0 )
1121 GResourceAddResourceString("Gdraw.Synchronize: true",argv[0]);
1122 else if ( strcmp(pt,"-depth")==0 && i<argc-1 )
1123 AddR(argv[0],"Gdraw.Depth", argv[++i]);
1124 else if ( strcmp(pt,"-vc")==0 && i<argc-1 )
1125 AddR(argv[0],"Gdraw.VisualClass", argv[++i]);
1126 else if ( (strcmp(pt,"-cmap")==0 || strcmp(pt,"-colormap")==0) && i<argc-1 )
1127 AddR(argv[0],"Gdraw.Colormap", argv[++i]);
1128 else if ( (strcmp(pt,"-dontopenxdevices")==0) )
1129 AddR(argv[0],"Gdraw.DontOpenXDevices", "true");
1130 else if ( strcmp(pt,"-keyboard")==0 && i<argc-1 )
1131 AddR(argv[0],"Gdraw.Keyboard", argv[++i]);
1132 else if ( strcmp(pt,"-display")==0 && i<argc-1 )
1133 display = argv[++i];
1134 # if MyMemory
1135 else if ( strcmp(pt,"-memory")==0 )
1136 __malloc_debug(5);
1137 # endif
1138 else if ( strncmp(pt,"-usecairo",strlen("-usecairo"))==0 ) {
1139 if ( strcmp(pt,"-usecairo=no")==0 )
1140 use_cairo = false;
1141 else
1142 use_cairo = true;
1143 GDrawEnableCairo(use_cairo);
1144 } else if ( strcmp(pt,"-nosplash")==0 )
1145 splash = 0;
1146 else if ( strcmp(pt,"-quiet")==0 )
1147 /* already checked for this earlier, no need to do it again */;
1148 else if ( strcmp(pt,"-unique")==0 )
1149 unique = 1;
1150 else if ( strcmp(pt,"-recover")==0 && i<argc-1 ) {
1151 ++i;
1152 if ( strcmp(argv[i],"none")==0 )
1153 recover=0;
1154 else if ( strcmp(argv[i],"clean")==0 )
1155 recover= -1;
1156 else if ( strcmp(argv[i],"auto")==0 )
1157 recover= 1;
1158 else if ( strcmp(argv[i],"inquire")==0 )
1159 recover= 2;
1160 else {
1161 fprintf( stderr, "Invalid argument to -recover, must be none, auto, inquire or clean\n" );
1162 dousage();
1163 }
1164 } else if ( strcmp(pt,"-recover=none")==0 ) {
1165 recover = 0;
1166 } else if ( strcmp(pt,"-recover=clean")==0 ) {
1167 recover = -1;
1168 } else if ( strcmp(pt,"-recover=auto")==0 ) {
1169 recover = 1;
1170 } else if ( strcmp(pt,"-recover=inquire")==0 ) {
1171 recover = 2;
1172 } else if ( strcmp(pt,"-docs")==0 )
1173 dohelp();
1174 else if ( strcmp(pt,"-help")==0 )
1175 dousage();
1176 else if ( strcmp(pt,"-version")==0 || strcmp(pt,"-v")==0 || strcmp(pt,"-V")==0 )
1177 doversion(FONTFORGE_VERSION);
1178 else if ( strcmp(pt,"-quit")==0 )
1179 quit_request = true;
1180 else if ( strcmp(pt,"-home")==0 )
1181 /* already did a chdir earlier, don't need to do it again */;
1182 #if defined(__Mac)
1183 else if ( strncmp(pt,"-psn_",5)==0 ) {
1184 /* OK, I don't know what _-psn_ means, but to GW it means */
1185 /* we've been started on the mac from the FontForge.app */
1186 /* structure, and the current directory was (shudder) "/" */
1187 /* (however, we changed to HOME earlier in main routine). */
1188 unique = 1;
1189 listen_to_apple_events = true; // This has been problematic on Mavericks and later.
1190 }
1191 #endif
1192 }
1193 #ifdef FONTFORGE_CAN_USE_GDK
1194 gdk_set_allowed_backends("win32,quartz,x11");
1195 gdk_init(&argc, &argv);
1196 #endif
1197 ensureDotFontForgeIsSetup();
1198 #if defined(__MINGW32__) && !defined(_NO_LIBCAIRO)
1199 //Load any custom fonts for the user interface
1200 if (use_cairo) {
1201 char *system_load = getShareDir();
1202 char *user_load = getFontForgeUserDir(Data);
1203 char lbuf[MAX_PATH];
1204 int lret;
1205
1206 if (system_load != NULL) {
1207 //Follow the FontConfig APPSHAREFONTDIR location
1208 lret = snprintf(lbuf, MAX_PATH, "%s/../fonts", system_load);
1209 if (lret > 0 && lret < MAX_PATH) {
1210 WinLoadUserFonts(lbuf);
1211 }
1212 }
1213 if (user_load != NULL) {
1214 lret = snprintf(lbuf, MAX_PATH, "%s/%s", user_load, "ui-fonts");
1215 if (lret > 0 && lret < MAX_PATH) {
1216 WinLoadUserFonts(lbuf);
1217 }
1218 free(user_load);
1219 }
1220 }
1221 #endif
1222 InitImageCache(); // This is in gtextinfo.c. It zeroes imagecache for us.
1223 atexit(&ClearImageCache); // We register the destructor, which is also in gtextinfo.c.
1224 GDrawCreateDisplays(display,argv[0]);
1225 atexit(&GDrawDestroyDisplays); // We register the destructor so that it runs even if we call exit without finishing this function.
1226 default_background = GDrawGetDefaultBackground(screen_display);
1227 InitToolIconClut(default_background);
1228 InitToolIcons();
1229 InitCursors();
1230
1231 /**
1232 * we have to do a quick sniff of argv[] here to see if the user
1233 * wanted to skip loading these python init files.
1234 */
1235 for ( i=1; i<argc; ++i ) {
1236 char *pt = argv[i];
1237
1238 if ( !strcmp(pt,"-SkipPythonInitFiles")) {
1239 ProcessPythonInitFiles = 0;
1240 }
1241 }
1242
1243 #ifndef _NO_PYTHON
1244 /*# ifndef GWW_TEST*/
1245 FontForge_InitializeEmbeddedPython(); /* !!!!!! debug (valgrind doesn't like python) */
1246 /*# endif*/
1247 #endif
1248
1249 #ifndef _NO_PYTHON
1250 if( ProcessPythonInitFiles )
1251 PyFF_ProcessInitFiles();
1252 #endif
1253
1254 /* the splash screen used not to have a title bar (wam_nodecor) */
1255 /* but I found I needed to know how much the window manager moved */
1256 /* the window around, which I can determine if I have a positioned */
1257 /* decorated window created at the begining */
1258 /* Actually I don't care any more */
1259 wattrs.mask = wam_events|wam_cursor|wam_bordwidth|wam_backcol|wam_positioned|wam_utf8_wtitle|wam_isdlg;
1260 wattrs.event_masks = ~(1<<et_charup);
1261 wattrs.positioned = 1;
1262 wattrs.cursor = ct_pointer;
1263 wattrs.utf8_window_title = "FontForge";
1264 wattrs.border_width = 2;
1265 wattrs.background_color = 0xffffff;
1266 #ifdef FONTFORGE_CAN_USE_GDK
1267 wattrs.is_dlg = true;
1268 #else
1269 wattrs.is_dlg = !listen_to_apple_events;
1270 #endif
1271 pos.x = pos.y = 200;
1272 SplashImageInit();
1273 pos.width = splashimagep->u.image->width;
1274 pos.height = splashimagep->u.image->height-1; // See splash_e_h:et_map
1275 GDrawBindSelection(NULL,sn_user1,"FontForge");
1276 if ( unique && GDrawSelectionOwned(NULL,sn_user1)) {
1277 /* Different event handler, not a dialog */
1278 wattrs.is_dlg = false;
1279 splashw = GDrawCreateTopWindow(NULL,&pos,request_e_h,NULL,&wattrs);
1280 PingOtherFontForge(argc,argv);
1281 } else {
1282 if ( quit_request )
1283 exit( 0 );
1284 splashw = GDrawCreateTopWindow(NULL,&pos,splash_e_h,NULL,&wattrs);
1285 }
1286
1287 memset(&rq,0,sizeof(rq));
1288 rq.utf8_family_name = SERIF_UI_FAMILIES;
1289 rq.point_size = 10;
1290 rq.weight = 400;
1291 splash_font = GDrawInstanciateFont(NULL,&rq);
1292 splash_font = GResourceFindFont("Splash.Font",splash_font);
1293 GDrawDecomposeFont(splash_font, &rq);
1294 splash_mono = GDrawInstanciateFont(NULL,&rq);
1295 splash_mono = GResourceFindFont("GTextField.Font",splash_mono);
1296 rq.style = fs_italic;
1297 splash_italic = GDrawInstanciateFont(NULL,&rq);
1298 splash_italic = GResourceFindFont("Splash.ItalicFont",splash_italic);
1299 GDrawSetFont(splashw,splash_font);
1300
1301 SplashLayout();
1302 localsplash = splash;
1303
1304 if ( localsplash && !listen_to_apple_events )
1305 start_splash_screen();
1306
1307 //
1308 // The below call will initialize the fontconfig cache if required.
1309 // That can take a while the first time it happens.
1310 //
1311 GDrawWindowFontMetrics(splashw,splash_font,&as,&ds,&ld);
1312 fh = as+ds+ld;
1313
1314 if ( AutoSaveFrequency>0 )
1315 autosave_timer=GDrawRequestTimer(splashw,2*AutoSaveFrequency*1000,AutoSaveFrequency*1000,NULL);
1316
1317 GDrawProcessPendingEvents(NULL);
1318 GDrawSetBuildCharHooks(BuildCharHook,InsCharHook);
1319
1320 any = 0;
1321 if ( recover==-1 )
1322 CleanAutoRecovery();
1323 else if ( recover )
1324 any = DoAutoRecoveryExtended( recover-1 );
1325
1326 openflags = 0;
1327 for ( i=1; i<argc; ++i ) {
1328 char buffer[1025];
1329 char *pt = argv[i];
1330
1331 GDrawProcessPendingEvents(NULL);
1332 if ( pt[0]=='-' && pt[1]=='-' && pt[2]!='\0')
1333 ++pt;
1334 if ( strcmp(pt,"-new")==0 ) {
1335 FontNew();
1336 any = 1;
1337 # if HANYANG
1338 } else if ( strcmp(pt,"-newkorean")==0 ) {
1339 MenuNewComposition(NULL,NULL,NULL);
1340 any = 1;
1341 # endif
1342 } else if ( !strcmp(pt,"-SkipPythonInitFiles")) {
1343 // already handled above.
1344 } else if ( strcmp(pt,"-last")==0 ) {
1345 if ( next_recent<RECENT_MAX && RecentFiles[next_recent]!=NULL )
1346 if ( ViewPostScriptFont(RecentFiles[next_recent++],openflags))
1347 any = 1;
1348 } else if ( strcmp(pt,"-sync")==0 || strcmp(pt,"-memory")==0 ||
1349 strcmp(pt,"-nosplash")==0 || strcmp(pt,"-recover=none")==0 ||
1350 strcmp(pt,"-recover=clean")==0 || strcmp(pt,"-recover=auto")==0 ||
1351 strcmp(pt,"-dontopenxdevices")==0 || strcmp(pt,"-unique")==0 ||
1352 strncmp(pt,"-usecairo",strlen("-usecairo"))==0 ||
1353 strcmp(pt,"-home")==0 || strcmp(pt,"-quiet")==0
1354 || strcmp(pt,"-forceuihidden")==0 )
1355 /* Already done, needed to be before display opened */;
1356 else if ( strncmp(pt,"-psn_",5)==0 )
1357 /* Already done */;
1358 else if ( (strcmp(pt,"-depth")==0 || strcmp(pt,"-vc")==0 ||
1359 strcmp(pt,"-cmap")==0 || strcmp(pt,"-colormap")==0 ||
1360 strcmp(pt,"-keyboard")==0 ||
1361 strcmp(pt,"-display")==0 || strcmp(pt,"-recover")==0 ) &&
1362 i<argc-1 )
1363 ++i; /* Already done, needed to be before display opened */
1364 else if ( strcmp(pt,"-allglyphs")==0 )
1365 openflags |= of_all_glyphs_in_ttc;
1366 else if ( strcmp(pt,"-open")==0 )
1367 doopen = true;
1368 else {
1369 // printf("else argv[i]:%s\n", argv[i] );
1370 GFileGetAbsoluteName(argv[i],buffer,sizeof(buffer));
1371 if ( GFileIsDir(buffer) ) {
1372 char *fname;
1373 fname = malloc(strlen(buffer)+strlen("/glyphs/contents.plist")+1);
1374 strcpy(fname,buffer); strcat(fname,"/glyphs/contents.plist");
1375 if ( GFileExists(fname)) {
1376 /* It's probably a Unified Font Object directory */
1377 free(fname);
1378 if ( ViewPostScriptFont(buffer,openflags) )
1379 any = 1;
1380 } else {
1381 strcpy(fname,buffer); strcat(fname,"/font.props");
1382 if ( GFileExists(fname)) {
1383 /* It's probably a sf dir collection */
1384 free(fname);
1385 if ( ViewPostScriptFont(buffer,openflags) )
1386 any = 1;
1387 } else {
1388 free(fname);
1389 if ( buffer[strlen(buffer)-1]!='/' ) {
1390 /* If dirname doesn't end in "/" we'll be looking in parent dir */
1391 buffer[strlen(buffer)+1]='\0';
1392 buffer[strlen(buffer)] = '/';
1393 }
1394 fname = GetPostScriptFontName(buffer,false,false);
1395 if ( fname!=NULL )
1396 ViewPostScriptFont(fname,openflags);
1397 any = 1; /* Even if we didn't get a font, don't bring up dlg again */
1398 free(fname);
1399 }
1400 }
1401 } else if ( ViewPostScriptFont(buffer,openflags)!=0 )
1402 any = 1;
1403 }
1404 }
1405 if ( !any && !doopen )
1406 any = ReopenLastFonts();
1407
1408 #if defined(__Mac)
1409 if ( listen_to_apple_events ) {
1410 install_apple_event_handlers();
1411 #ifndef FONTFORGE_CAN_USE_GDK
1412 install_mac_timer();
1413 setup_cocoa_app();
1414
1415 // WARNING: See declaration of RunApplicationEventLoop() above as to
1416 // why you might not want to call that function anymore.
1417 // RunApplicationEventLoop();
1418 } else
1419 #else
1420 }
1421 #endif // FONTFORGE_CAN_USE_GDK
1422 #endif // __Mac
1423 if ( doopen || !any )
1424 _FVMenuOpen(NULL);
1425 GDrawEventLoop(NULL);
1426 GDrawDestroyDisplays();
1427
1428 #ifndef _NO_PYTHON
1429 /*# ifndef GWW_TEST*/
1430 FontForge_FinalizeEmbeddedPython(); /* !!!!!! debug (valgrind doesn't like python) */
1431 /*# endif*/
1432 #endif
1433
1434 // These free menu translations, mostly.
1435 BitmapViewFinishNonStatic();
1436 MetricsViewFinishNonStatic();
1437 CharViewFinishNonStatic();
1438 FontViewFinishNonStatic();
1439
1440 ClearImageCache(); // This frees the contents of imagecache.
1441 // hotkeysSave();
1442 LastFonts_Save();
1443
1444 #ifndef _NO_LIBUNICODENAMES
1445 uninm_names_db_close(names_db); /* close this database before exiting */
1446 uninm_blocks_db_close(blocks_db);
1447 #endif
1448
1449 return( 0 );
1450 }
1451