// g++ test_layout.cpp -g -o test_layout `pkg-config --cflags --libs gtk+-2.0` `xft-config --cflags --libs` #include #include #include #include #include #include #include using namespace std; static const int screen_resolution = 96; static const int printer_resolution = 3000; static const float mm_per_inch = 25.4; static const int x_off = 10; static const int y_off = 10; static float cursor_pos = x_off; // double buffer pixmap for the drawing area static GdkPixmap* pixmap = 0; static GdkGC* gc = 0; static GdkGC* white_gc = 0; // Buffer of characters typedef vector TextBuffer; static TextBuffer text_buffer; // Xft related structures static XftDraw* xft_draw = 0; static XftFont* font = 0; static XftColor color; void destroy_cb (GtkWidget *widget, gpointer data) { gtk_main_quit (); } inline static int inches_to_pixels (float inches) { return (int) (inches * 96 + 0.5); } class FaceGetter { public: FaceGetter(XftFont* font) : face_(XftLockFace(font)), font_(font) {} ~FaceGetter() { XftUnlockFace(font_); } FT_Face getFace() { return face_; } private: FaceGetter(const FaceGetter&); FaceGetter& operator= (const FaceGetter&); FT_Face face_; XftFont* font_; }; // that kinda of sucks. I'm calculating everything in the drawing handler, // when all that can be calculated before... static void draw_text() { float dx; const size_t nb_chars = text_buffer.size(); vector chars(nb_chars); XftCharSpec char_spec; XGlyphInfo extents; FaceGetter face_getter(font); FT_Face face = face_getter.getFace(); FT_UInt glyph_index; int error; cursor_pos = x_off; for (size_t i = 0; i < nb_chars; ++i) { FT_Fixed linearHoriAdvance; char_spec.ucs4 = text_buffer[i]; char_spec.x = (short) (cursor_pos + 0.5); char_spec.y = y_off + 20; chars.push_back(char_spec); glyph_index = FT_Get_Char_Index(face, char_spec.ucs4); error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); if (error) { cout << "Error loading glyph" << endl; continue; } linearHoriAdvance = face->glyph->linearHoriAdvance; dx = ((float) linearHoriAdvance) / (1 << 16); cursor_pos += dx; } XftDrawCharSpec (xft_draw, &color, font, &chars[0], chars.size()); XftDrawString32 (xft_draw, &color, font, x_off, y_off + 36, &text_buffer[0], text_buffer.size()); } static void draw_page_borders () { /* the weight and height of a A4 page (210 x 297 mm) */ static const float width_inches = 8.268; static const float height_inches = 11.693; int x0 = x_off; int y0 = y_off; /* the -1 here is just because X sucks */ int xf = x_off + inches_to_pixels (width_inches) - 1; int yf = y_off + inches_to_pixels (height_inches) - 1; assert (pixmap); assert (gc); gdk_draw_rectangle (pixmap, gc, 0, x0, y0, xf, yf); } static void draw () { int width; int height; assert (pixmap); assert (gc); gdk_drawable_get_size (pixmap, &width, &height); gdk_draw_rectangle (pixmap, white_gc, TRUE, 0, 0, width, height); draw_page_borders (); draw_text (); } static gboolean key_press_event (GtkWidget* widget, GdkEventKey* event, gpointer user_data) { FcChar32 unichar; unichar = gdk_keyval_to_unicode (event->keyval); if (unichar) { text_buffer.push_back (unichar); draw (); gtk_widget_queue_draw_area (widget, 0, 0, widget->allocation.width, widget->allocation.height); } return TRUE; } /* Create a new backing pixmap of the appropriate size */ static gint configure_event (GtkWidget* widget, GdkEventConfigure* event) { if (pixmap) gdk_pixmap_unref(pixmap); gc = widget->style->black_gc; white_gc = widget->style->white_gc; pixmap = gdk_pixmap_new(widget->window, widget->allocation.width, widget->allocation.height, -1); if (xft_draw) XftDrawChange(xft_draw, GDK_WINDOW_XWINDOW (pixmap)); else xft_draw = XftDrawCreate (GDK_DISPLAY (), GDK_WINDOW_XWINDOW (pixmap), GDK_VISUAL_XVISUAL (gdk_drawable_get_visual (pixmap)), GDK_COLORMAP_XCOLORMAP (gdk_drawable_get_colormap (pixmap))); draw (); return TRUE; } static gint expose_event (GtkWidget* widget, GdkEventExpose* event) { gdk_draw_pixmap(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE (widget)], pixmap, event->area.x, event->area.y, event->area.x, event->area.y, event->area.width, event->area.height); return FALSE; } int main(int argc, char* argv[]) { GtkDrawingArea* da; GtkWindow* main_window; GdkWindow* win; gtk_init (&argc, &argv); main_window = GTK_WINDOW (gtk_window_new (GTK_WINDOW_TOPLEVEL)); g_signal_connect (G_OBJECT (main_window), "destroy", G_CALLBACK (destroy_cb), NULL); da = GTK_DRAWING_AREA (gtk_drawing_area_new ()); win = GTK_WIDGET (da)->window; GdkColormap* colormap = gdk_rgb_get_cmap(); GdkColor c; c.red = 0; c.green = 0; c.blue = 0; gdk_color_alloc(colormap, &c); color.color.red = c.red; color.color.green = c.green; color.color.blue = c.blue; color.color.alpha = 0xffff; color.pixel = c.pixel; font = XftFontOpenName(GDK_DISPLAY (), DefaultScreen (GDK_DISPLAY ()), "Times-12"); gtk_container_add(GTK_CONTAINER (main_window), GTK_WIDGET (da)); g_signal_connect (G_OBJECT (da), "configure_event", G_CALLBACK (configure_event), NULL); g_signal_connect (G_OBJECT (da), "expose_event", G_CALLBACK (expose_event), NULL); g_signal_connect (G_OBJECT (main_window), "key_press_event", G_CALLBACK (key_press_event), NULL); gtk_widget_show_all (GTK_WIDGET (main_window)); gtk_main (); return 0; }