/* Dia -- an diagram creation/manipulation program * Copyright (C) 1998 Alexander Larsson * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include "font.h" #include "diapsft2renderer.h" #include "textline.h" #include #include #include /* I'd really rather avoid this */ #include #include #define DPI 300 /* These routines stolen mercilessly from PAPS * http://imagic.weizmann.ac.il/~dov/freesw/paps */ /* Information passed in user data when drawing outlines */ typedef struct _OutlineInfo OutlineInfo; struct _OutlineInfo { FILE *OUT; FT_Vector glyph_origin; int dpi; }; void postscript_contour_headers(FILE *OUT, int dpi_x, int dpi_y); void postscript_draw_contour(DiaPsRenderer *renderer, int dpi_x, PangoLayoutLine *pango_line, double x_pos, double y_pos ); void draw_bezier_outline(DiaPsRenderer *renderer, int dpi_x, FT_Face face, FT_UInt glyph_index, double pos_x, double pos_y ); /* Countour traveling functions */ static int paps_move_to( FT_Vector* to, void *user_data); static int paps_line_to( FT_Vector* to, void *user_data); static int paps_conic_to( FT_Vector* control, FT_Vector* to, void *user_data); static int paps_cubic_to( FT_Vector* control1, FT_Vector* control2, FT_Vector* to, void *user_data); static void dia_ps_ft2_renderer_class_init (DiaPsFt2RendererClass *klass); static void draw_text_line(DiaRenderer *self, TextLine *text_line, Point *pos, Color *color); static gpointer parent_class = NULL; static void set_font(DiaRenderer *self, DiaFont *font, real height) { DiaPsFt2Renderer *renderer = DIA_PS_FT2_RENDERER(self); if (renderer->current_font != font) { if (renderer->current_font != NULL) { dia_font_unref(renderer->current_font); } renderer->current_font = font; /* Dammit! We have a random factor once again! * And not only here but also in dia_font_scaled_build_layout() below ... */ renderer->current_height = height/**4.3*/; dia_font_ref(font); } pango_context_set_font_description(dia_font_get_context(), dia_font_get_description(font)); } /* ********************************************************* */ /* String rendering using PangoFt2 */ /* ********************************************************* */ /* Such a big mark really is a sign that this should go in its own file:) */ /* And so it did. */ /*====================================================================== outline traversing functions. ----------------------------------------------------------------------*/ static int paps_move_to( FT_Vector* to, void *user_data) { OutlineInfo *outline_info = (OutlineInfo*)user_data; fprintf(outline_info->OUT, "%d %d moveto\n", (int)to->x , (int)to->y ); return 0; } static int paps_line_to( FT_Vector* to, void *user_data) { OutlineInfo *outline_info = (OutlineInfo*)user_data; fprintf(outline_info->OUT, "%d %d lineto\n", (int)to->x , (int)to->y ); return 0; } static int paps_conic_to( FT_Vector* control, FT_Vector* to, void *user_data) { OutlineInfo *outline_info = (OutlineInfo*)user_data; fprintf(outline_info->OUT, "%d %d %d %d conicto\n", (int)control->x , (int)control->y , (int)to->x , (int)to->y ); return 0; } static int paps_cubic_to( FT_Vector* control1, FT_Vector* control2, FT_Vector* to, void *user_data) { OutlineInfo *outline_info = (OutlineInfo*)user_data; fprintf(outline_info->OUT, "%d %d %d %d %d %d curveto\n", (int)control1->x , (int)control1->y , (int)control2->x , (int)control2->y , (int)to->x , (int)to->y ); return 0; } /* These must go in the prologue section */ void postscript_contour_headers(FILE *OUT, int dpi_x, int dpi_y) { /* /dpi_x needed for /conicto */ fprintf(OUT, "/dpi_x %d def\n" "/dpi_y %d def\n", dpi_x, dpi_y); /* Outline support */ fprintf(OUT, "/conicto {\n" " /to_y exch def\n" " /to_x exch def\n" " /conic_cntrl_y exch def\n" " /conic_cntrl_x exch def\n" " currentpoint\n" " /p0_y exch def\n" " /p0_x exch def\n" " /p1_x p0_x conic_cntrl_x p0_x sub 2 3 div mul add def\n" " /p1_y p0_y conic_cntrl_y p0_y sub 2 3 div mul add def\n" " /p2_x p1_x to_x p0_x sub 1 3 div mul add def\n" " /p2_y p1_y to_y p0_y sub 1 3 div mul add def\n" " p1_x p1_y p2_x p2_y to_x to_y curveto\n" "} bind def\n" "/start_ol { gsave 1.1 dpi_x div dup scale} bind def\n" "/end_ol { closepath fill grestore } bind def\n" ); } /* postscript_draw_contour() dumps out the information of a line. It shows how to access the ft font information out of the pango font info. */ void postscript_draw_contour(DiaPsRenderer *renderer, int dpi_x, PangoLayoutLine *pango_line, double line_start_pos_x, double line_start_pos_y) { GSList *runs_list; int num_runs = 0; /* First calculate number of runs in text */ runs_list = pango_line->runs; while(runs_list) { num_runs++; runs_list = runs_list->next; } /* Loop over the runs and output font info */ runs_list = pango_line->runs; while(runs_list) { PangoLayoutRun *run = runs_list->data; PangoItem *item = run->item; PangoGlyphString *glyphs = run->glyphs; PangoAnalysis *analysis = &item->analysis; PangoFont *font = analysis->font; FT_Face ft_face; int bidi_level; int num_glyphs; int glyph_idx; if (font == NULL) { fprintf(stderr, "No font found\n"); continue; } ft_face = pango_ft2_font_get_face(font); if (ft_face == NULL) { fprintf(stderr, "Failed to get face for font %s\n", pango_font_description_to_string(pango_font_describe(font))); continue; } /* printf("Got face %s (PS %s) for font %s (diafont %s)\n", ft_face->family_name, FT_Get_Postscript_Name(ft_face), pango_font_description_to_string(pango_font_describe(font)), dia_font_get_family(renderer->current_font)); */ bidi_level = item->analysis.level; num_glyphs = glyphs->num_glyphs; for (glyph_idx=0; glyph_idxglyphs[glyph_idx].geometry; double pos_x; double pos_y; double scale = 2.54/PANGO_SCALE/dpi_x;/*72.0 / PANGO_SCALE / dpi_x;*/ pos_x = line_start_pos_x + 1.0* geometry.x_offset * scale; pos_y = line_start_pos_y - 1.0*geometry.y_offset * scale; line_start_pos_x += 1.0 * geometry.width * scale; /* printf("Drawing glyph %d: index %d at %f, %f (w %d)\n", glyph_idx, glyphs->glyphs[glyph_idx].glyph, pos_x, pos_y, geometry.width); */ draw_bezier_outline(renderer, dpi_x, ft_face, (FT_UInt)(glyphs->glyphs[glyph_idx].glyph), pos_x, pos_y ); } runs_list = runs_list->next; } } void draw_bezier_outline(DiaPsRenderer *renderer, int dpi_x, FT_Face face, FT_UInt glyph_index, double pos_x, double pos_y ) { FT_Int load_flags = FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP; FT_Glyph glyph; FT_Error error; gchar px_buf[G_ASCII_DTOSTR_BUF_SIZE]; gchar py_buf[G_ASCII_DTOSTR_BUF_SIZE]; gchar d1_buf[G_ASCII_DTOSTR_BUF_SIZE]; gchar d2_buf[G_ASCII_DTOSTR_BUF_SIZE]; /* Need to transform */ /* Output outline */ FT_Outline_Funcs outlinefunc = { paps_move_to, paps_line_to, paps_conic_to, paps_cubic_to }; OutlineInfo outline_info; outline_info.glyph_origin.x = pos_x; outline_info.glyph_origin.y = pos_y; outline_info.dpi = dpi_x; outline_info.OUT = renderer->file; fprintf(renderer->file, "gsave %s %s translate %s %s scale\n", g_ascii_formatd(px_buf, sizeof(px_buf), "%f", pos_x), g_ascii_formatd(py_buf, sizeof(py_buf), "%f", pos_y), g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", 2.54/72.0), g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", -2.54/72.0) ); fprintf(renderer->file, "start_ol\n"); if ((error=FT_Load_Glyph(face, glyph_index, load_flags))) { fprintf(stderr, "Can't load glyph: %d\n", error); return; } if ((error=FT_Get_Glyph (face->glyph, &glyph))) { fprintf(stderr, "Can't get glyph: %d\n", error); FT_Done_Glyph (glyph); return; } if (face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) FT_Outline_Decompose (&(((FT_OutlineGlyph)glyph)->outline), &outlinefunc, &outline_info); fprintf(renderer->file, "end_ol grestore \n"); FT_Done_Glyph (glyph); } static void draw_string(DiaRenderer *self, const char *text, Point *pos, Alignment alignment, Color *color) { DiaPsFt2Renderer *renderer = DIA_PS_FT2_RENDERER(self); TextLine *text_line = text_line_new(text, renderer->current_font, renderer->current_height); real width = text_line_get_width(text_line); Point realigned_pos = *pos; switch (alignment) { case ALIGN_LEFT: break; case ALIGN_CENTER: realigned_pos.x -= width/2; break; case ALIGN_RIGHT: realigned_pos.x -= width; break; } draw_text_line(self, text_line, &realigned_pos, color); } static void draw_text_line(DiaRenderer *self, TextLine *text_line, Point *pos, Color *color) { DiaPsFt2Renderer *renderer = DIA_PS_FT2_RENDERER(self); PangoLayout *layout; int line, linecount; double xpos = pos->x, ypos = pos->y; char *text = text_line_get_string(text_line); /* Using the global PangoContext does not allow to have renderer specific * different ones. Or it implies the push/pop _context mess. Anyway just * get rid of warnings for now. But the local code may be resurreted * sooner or later... --hb */ #define USE_GLOBAL_CONTEXT #ifndef USE_GLOBAL_CONTEXT PangoAttrList* list; PangoAttribute* attr; guint length; #endif if ((!text)||(text == (const char *)(1))) return; lazy_setcolor(DIA_PS_RENDERER(renderer),color); #define ANNOYING_SCALE_FACTOR 5.9 /* Make sure the letters aren't too wide. */ #ifdef USE_GLOBAL_CONTEXT layout = dia_font_build_layout(text, text_line_get_font(text_line), text_line_get_height(text_line)*ANNOYING_SCALE_FACTOR); #else /* approximately what would be required but w/o dia_font_get_context() */ dia_font_set_height(text_line_get_font(text_line), text_line_get_height(text_line)); layout = pango_layout_new(dia_font_get_context()); length = text ? strlen(text) : 0; pango_layout_set_text(layout,text,length); list = pango_attr_list_new(); attr = pango_attr_font_desc_new(dia_font_get_description(text_line_get_font(text_line))); attr->start_index = 0; attr->end_index = length; pango_attr_list_insert(list,attr); pango_layout_set_attributes(layout,list); pango_attr_list_unref(list); pango_layout_set_indent(layout,0); pango_layout_set_justify(layout,FALSE); #endif pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); linecount = pango_layout_get_line_count(layout); for (line = 0; line < linecount; line++) { PangoLayoutLine *layoutline = pango_layout_get_line(layout, line); /* Not sure scale is the right one here. */ text_line_adjust_layout_line(text_line, layoutline, ANNOYING_SCALE_FACTOR); postscript_draw_contour(DIA_PS_RENDERER(renderer), DPI, /* dpi_x */ layoutline, xpos, ypos); ypos += 10;/* Some line height thing??? */ } } static void dump_fonts (DiaPsRenderer *renderer) { postscript_contour_headers(renderer->file, DPI, DPI); } GType dia_ps_ft2_renderer_get_type (void) { static GType object_type = 0; if (!object_type) { static const GTypeInfo object_info = { sizeof (DiaPsFt2RendererClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) dia_ps_ft2_renderer_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (DiaPsFt2Renderer), 0, /* n_preallocs */ NULL /* init */ }; object_type = g_type_register_static (DIA_TYPE_PS_RENDERER, "DiaPsFt2Renderer", &object_info, 0); } return object_type; } static void dia_ps_ft2_renderer_finalize (GObject *object) { DiaPsFt2Renderer *renderer = DIA_PS_FT2_RENDERER(object); if (renderer->current_font != NULL) { dia_font_unref(renderer->current_font); renderer->current_font = NULL; } G_OBJECT_CLASS (parent_class)->finalize (object); } static void dia_ps_ft2_renderer_class_init (DiaPsFt2RendererClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); DiaRendererClass *renderer_class = DIA_RENDERER_CLASS (klass); DiaPsRendererClass *ps_renderer_class = DIA_PS_RENDERER_CLASS (klass); parent_class = g_type_class_peek_parent (klass); object_class->finalize = dia_ps_ft2_renderer_finalize; renderer_class->set_font = set_font; renderer_class->draw_string = draw_string; renderer_class->draw_text_line = draw_text_line; /* ps specific */ ps_renderer_class->dump_fonts = dump_fonts; }