Xournal on Windows

Posted on

Screen­shot of Xournal run­ning na­tively on Win­dows Vista
Screen­shot of Xournal run­ning na­tively on Win­dows Vista

Xournal is a free soft­ware note-taking ap­pli­ca­tion sim­ilar to Mi­crosoft Win­dows Jour­nal. It used to be Lin­ux-only, with MacOS and Win­dows ports on the TODO list. But as you can see it is now pos­sible to run it on Win­dows. Hope­fully a nice in­staller can be cre­ated for it, like the ones for Inkscape and Gimp, but for now the process is rather tricky and in­volves editing and com­piling Xour­nal’s source code.

Up­date: Denis Au­roux, the au­thor of Xour­nal, has gra­ciously in­cor­po­rated my code changes into Xournal and made an of­fi­cial Win­dows re­lease avail­able on the Xournal home­page. Go try it out!

Here’s what I did:

Step 1: Set­ting up the de­vel­op­ment en­vi­ron­ment

We’ll need to set-up a com­piler and in­stall all the li­braries that Xournal uses (di­rectly or in­di­rectly) be­fore we can com­pile its source code. Here are the soft­ware pack­ages I in­stalled with their ver­sion num­bers (newer ver­sions will prob­ably work, sig­nif­i­cantly older ver­sions might not), web­site ad­dresses (I’d pro­vide di­rect down­load links, but they’ll prob­ably end up as dead links as soon as ver­sion num­bers change), and where I in­stalled them (of course, you’ll prob­ably want to use dif­ferent paths, un­less your name is also Dirk):

MinGW + MSys, a min­i­malist Unix-like en­vi­ron­ment for Win­dows, for the C com­piler and other build tools. These can be gotten from MinG­W.org:

  • MinGW 5.1.4 (into c:/​Users/​Dirk/​Programming/​MinGW)

  • MSys 1.0.11 (into c:/​Users/​Dirk/​Programming/​msys)

  • perl 5.6.1_2-1 (into c:/​Users/​Dirk/​Programming/​msys/​1.​0)

  • libcrypt 1.1_1-2 (into c:/​Users/​Dirk/​Programming/​msys/​1.​0)

  • au­to­conf 2.63-1 (into c:/​Users/​Dirk/​Programming/​msys/​1.​0)

  • au­tomake 1.11-1 (into c:/​Users/​Dirk/​Programming/​msys/​1.​0)

Some Gtk+ and GNOME li­braries that Xournal uses for its GUI. These can be gotten from Gtk.org:

  • Gtk+ 2.16.5-20090731 de­vel­op­ment bundle (into c:/​/MinGW)

  • Cairo 1.8.8-2 bi­na­ry+dev package (into c:/​/MinGW, over­writing the one from the bundle be­cause the latter is ap­par­ently built without FreeType sup­port)

  • FreeType 2.3.9-1 bi­na­ry+dev package (into c:/​/MinGW)

  • font­config 2.7.1-2 bi­na­ry+dev package (into c:/​/MinGW)

  • win_i­conv 20080320 bi­na­ry+dev package (into c:/​/MinGW)

GNOME.org:

  • libg­nome­canvas 2.20.1 bi­na­ry+dev package (into c:/​/MinGW)

  • libg­nomeprintui 2.12.1 bi­na­ry+dev package (into c:/​/MinGW)

  • libg­nomeprint 2.12.1 bi­na­ry+dev package (into c:/​/MinGW)

  • libart_lgpl 2.3.20 bi­na­ry+dev package (into c:/​/MinGW)

and more GNOME.org:

  • libxml 2.7.3 bi­na­ry+dev package (into c:/​/MinGW)

  • expat 2.0.1-1 bi­nary package (into c:/​/MinGW)

We have to make a small change in the file C:/​Users/​Dirk/​Programming/​MinGW/​lib/​pkgconfig/​libgnomeprint-2.​2.​pc, re­placing “pango” by “pan­goft2” on the “Re­quires:” line so that in now reads:

Requires: libart-2.0 glib-2.0 gmodule-2.0 gobject-2.0 libxml-2.0 pangoft2

Step 2: Building libpop­pler from source

The above gave us all the li­braries Xournal needs, ex­cept for Pop­pler. I haven’t seen a pre-com­piled bi­nary ver­sion for Win­dows, so we’ll have to build it our­selves. We simply down­load the Pop­pler source code, start MSys, cd into the di­rec­tory where we un­packed the source code, and type:

$ export ACLOCAL_FLAGS=-I/c/Users/Dirk/Programming/MinGW/share/aclocal/
$ ./configure --prefix=/c/Users/Dirk/Programming/MinGW/
$ make
$ make install

(Don’t type the dollar signs, they just in­di­cate the MSys prompt.)

Step 3: Re­moving Xour­nal’s screen­shot func­tion

Once we’ve down­loaded Xour­nal’s source code from the Xournal web­site, we can’t im­me­di­ately com­pile it for Win­dows. Xournal has one bit of func­tion­ality that’s not im­ple­mented with the cross-plat­form Gtk+ li­brary, but in­stead is im­ple­mented di­rectly using X. This will of course not work on dif­ferent plat­forms such as Win­dows.

The func­tion­ality in ques­tion is in the menu under “Journal > Back­ground screen­shot”. On Linux, it hides the Xournal win­dow, then when you click any­where it’ll bring back the Xournal window with an added screen­shot of what­ever window you clicked on. There is no way to im­ple­ment this “clicking on a window”-me­chanic in the Win­dows API, so a proper fix would be to change the me­chanic to some­thing dif­fer­ent, as the Gimp guys have done. There one drags a crosshair onto a win­dow.

I have not taken the ef­fort to ac­tu­ally im­ple­ment this dif­ferent screen­shot me­chanic, as I don’t use it and it’s a lot easier to just re­move it:

  • In src/​xo-misc.​c in the func­tion hide_​unimplemented at the bottom of the file add the lines

    #ifdef WIN32
      gtk_widget_hide(GET_COMPONENT("journalScreenshot"));  
    #endif
  • In src/​xo-file.​c at the top of the file, dis­able the X-spe­cific #includes under Win­dows:

    #ifndef WIN32
    #  include <gdk/gdkx.h>
    #  include <x11/Xlib.h>
    #endif
  • In the same file, re­place the body of the attempt_​screenshot_​bg func­tion to do nothing on Win­dows:

    struct Background *attempt_screenshot_bg(void)
    {
    #ifndef WIN32
      ... old body here ...
    #else /* WIN32 */
      return NULL;
    #endif
    }

Step 4: Building Xournal from source

To build Xour­nal, we just use MSys as be­fore, and in­side the xournal di­rec­tory type the fol­low­ing:

$ export ACLOCAL_FLAGS=-I/c/Users/Dirk/Programming/MinGW/share/aclocal/
$ ./autogen.sh
$ make

Step 5: Run­ning Xournal

To run Xournal we can type

$ src/xournal

or just dou­ble-click src/​xournal.​exe in Win­dows’s ex­plorer.

If all went well, you’ll see that Xournal “works”, but has some bugs. I’ll de­scribe below how each of these can be fixed by editing Xour­nal’s source code. Re­com­piling (that is, re­peating Step 4) will then give you a truly working Xour­nal. If you don’t want to man­u­ally apply all my fixes, you can just down­load the re­sulting source code.

Bug 1: The cursor is a big black square

Cursor is a black square and load­ing/saving doesn’t work.
Cursor is a black square and load­ing/saving doesn’t work.

For the pen and high­lighter tools, you’ll no­tice that the cursor is al­ways a 16×16 black square, re­gard­less of the se­lected color. This is a de­fect in GDK’s Win­dows backend (Bug 541377). The func­tion gdk_​cursor_​new_​from_​pixmap, which Xournal uses, will only give you mono­chrome cur­sors on Win­dows, and pro­duces strange re­sults when trying to pro­duce a col­ored cur­sor.

While this should be fixed on the GDK end, a workaround is to just use dif­ferent func­tions from GDK to create col­ored cur­sors. This can be done by putting the fol­lowing code near the top in src/​xo-paint.​c:

#ifdef WIN32
gboolean colors_too_similar(const GdkColor *colora,
                            const GdkColor *colorb)
{
  return (abs(colora->red - colorb->red) < 256 &&
          abs(colora->green - colorb->green) < 256 &&
          abs(colora->blue - colorb->blue) < 256);
}

/* gdk_cursor_new_from_pixmap is broken on Windows.
   this is a workaround using gdk_cursor_new_from_pixbuf. */
GdkCursor* fixed_gdk_cursor_new_from_pixmap(
    GdkPixmap *source, GdkPixmap *mask, const GdkColor *fg,
    const GdkColor *bg, gint x, gint y) 
{
  GdkPixmap *rgb_pixmap;
  GdkGC *gc;
  GdkPixbuf *rgb_pixbuf, *rgba_pixbuf;
  GdkCursor *cursor;
  int width, height;

  /* HACK!  It seems impossible to work with RGBA pixmaps directly in
     GDK-Win32.  Instead we pick some third color, different from fg
     and bg, and use that as the 'transparent color'.  We do this using
     colors_too_similar (see above) because two colors could be
     unequal in GdkColor's 16-bit/sample, but equal in GdkPixbuf's
     8-bit/sample. */
  GdkColor candidates[3] = {{0,65535,0,0}, {0,0,65535,0}, {0,0,0,65535}};
  GdkColor *trans = &candidates[0];
  if (colors_too_similar(trans, fg) || colors_too_similar(trans, bg)) {
    trans = &candidates[1];
    if (colors_too_similar(trans, fg) || colors_too_similar(trans, bg)) {
      trans = &candidates[2]; 
    }
  } /* trans is now guaranteed to be unique from fg and bg */

  /* create an empty pixmap to hold the cursor image */
  gdk_drawable_get_size(source, &width, &height);
  rgb_pixmap = gdk_pixmap_new(NULL, width, height, 24);

  /* blit the bitmaps defining the cursor on a transparent background */
  gc = gdk_gc_new(rgb_pixmap);
  gdk_gc_set_fill(gc, GDK_SOLID);
  gdk_gc_set_rgb_fg_color(gc, trans);
  gdk_draw_rectangle(rgb_pixmap, gc, TRUE, 0, 0, width, height);
  gdk_gc_set_fill(gc, GDK_OPAQUE_STIPPLED);
  gdk_gc_set_stipple(gc, source);
  gdk_gc_set_clip_mask(gc, mask);
  gdk_gc_set_rgb_fg_color(gc, fg);
  gdk_gc_set_rgb_bg_color(gc, bg);
  gdk_draw_rectangle(rgb_pixmap, gc, TRUE, 0, 0, width, height);
  gdk_gc_unref(gc);

  /* create a cursor out of the created pixmap */
  rgb_pixbuf = gdk_pixbuf_get_from_drawable(
    NULL, rgb_pixmap, gdk_colormap_get_system(),
    0, 0, 0, 0, width, height);
  gdk_pixmap_unref(rgb_pixmap);
  rgba_pixbuf = gdk_pixbuf_add_alpha(
    rgb_pixbuf, TRUE, trans->red, trans->green, trans->blue);
  gdk_pixbuf_unref(rgb_pixbuf);
  cursor = gdk_cursor_new_from_pixbuf(
    gdk_display_get_default(), rgba_pixbuf, x, y);
  gdk_pixbuf_unref(rgba_pixbuf);

  return cursor;
}
#define gdk_cursor_new_from_pixmap fixed_gdk_cursor_new_from_pixmap
#endif

Bug 2: Loading and saving doesn’t work

Using our Win­dows ver­sion of Xournal to try to load a file saved by Xournal on Linux gives an error mes­sage. What’s more, a file saved with our Win­dows ver­sion of Xournal gives an error mes­sage when trying to load it with Xournal on ei­ther plat­form!

It took me quite a while to figure this out, but the cul­prit here is that Xournal uses zlib in the wrong way, namely with tex­t-mode I/O in­stead of bi­nary I/O. This bug doesn’t show up on Linux be­cause there tex­t-mode and bi­nary I/O are pretty much the same thing. On Win­dows, how­ever, tex­t-mode I/O con­verts a '\n' char­acter into "\r\n" on writ­ing, and vice versa on read­ing. This is fine for text doc­u­ments, but not for bi­nary files such as Xour­nal’s!

The fix is sim­ple: re­place text I/O modes ("r" and "w") by bi­nary I/O modes ("rb" and "wb") in all the rel­e­vant gzopen, popen, and fopen calls. That is, all the calls in src/​xo-print.​c and all the calls in src/​xo-file.​c ex­cept for

  f = fopen(ui.mrufile, "w");

in save_​mru_​list, and

  f = fopen(ui.configfile, "w");

in save_​config_​to_​file.

Bug 3: Fullscreen isn’t truly fullscreen

When I tried Xour­nal’s fullscreen mode, I could still see the Win­dows taskbar. This seems to be a bug in the gtk_​window_​fullscreen func­tion on Win­dows. Inkscape’s fullscreen func­tion­ality does work prop­erly, how­ever, and that also uses gtk_​window_​fullscreen. It’ll take some more de­tec­tive work to pin this down prop­erly, but in the mean­time it is fairly easy to work around.

  • In src/​xournal.​h, add two new fields to the UIData struct:

      gint pre_fullscreen_width, pre_fullscreen_height;
  • In src/​xo-callbacks.​c, in the on_​viewFullscreen_​activate func­tion, re­place the two lines

      if (ui.fullscreen) gtk_window_fullscreen(GTK_WINDOW(winMain));
      else gtk_window_unfullscreen(GTK_WINDOW(winMain));

    by the lines

      if (ui.fullscreen) {
        gtk_window_get_size(GTK_WINDOW(winMain),
                            &ui.pre_fullscreen_width,
                            &ui.pre_fullscreen_height);
        gtk_window_fullscreen(GTK_WINDOW(winMain));
        gtk_widget_set_size_request(GTK_WIDGET(winMain),
                                    gdk_screen_width(),
                                    gdk_screen_height());
      } else {
        gtk_widget_set_size_request(GTK_WIDGET(winMain), -1, -1);
        gtk_window_unfullscreen(GTK_WINDOW(winMain));
        gtk_window_resize(GTK_WINDOW(winMain),
                          ui.pre_fullscreen_width,
                          ui.pre_fullscreen_height);
      }

With all of the above fixes, com­piling the re­sulting source code gives a working ver­sion of Xournal on Win­dows (minus the screen­shot func­tion­al­i­ty).