2 pango-font.cc -- implement Pango_font
4 source file of the GNU LilyPond music typesetter
6 (c) 2004--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
9 #define PANGO_ENABLE_BACKEND // ugh, why necessary?
10 #include <pango/pangoft2.h>
11 #include <freetype/ftxf86.h>
18 #include "pango-font.hh"
19 #include "dimensions.hh"
20 #include "file-name.hh"
21 #include "international.hh"
22 #include "lookup.hh" // debugging
24 #include "string-convert.hh"
26 #include "all-font-metrics.hh"
27 #include "program-option.hh"
32 Pango_font::Pango_font (PangoFT2FontMap
* /* fontmap */,
33 PangoFontDescription
const *description
,
36 physical_font_tab_
= scm_c_make_hash_table (11);
37 PangoDirection pango_dir
= PANGO_DIRECTION_LTR
;
38 context_
= pango_ft2_get_context (PANGO_RESOLUTION
,
41 pango_description_
= pango_font_description_copy (description
);
42 attribute_list_
= pango_attr_list_new ();
44 // urgh. I don't understand this. Why isn't this 1/(scale *
45 // resolution * output_scale)
48 output_scale_
= output_scale
;
50 / (Real (PANGO_SCALE
) * Real (PANGO_RESOLUTION
) * output_scale
);
52 // ugh. Should make this configurable.
53 pango_context_set_language (context_
, pango_language_from_string ("en_US"));
54 pango_context_set_base_dir (context_
, pango_dir
);
55 pango_context_set_font_description (context_
, description
);
58 Pango_font::~Pango_font ()
60 pango_font_description_free (pango_description_
);
61 g_object_unref (context_
);
62 pango_attr_list_unref (attribute_list_
);
66 Pango_font::register_font_file (string filename
,
70 scm_hash_set_x (physical_font_tab_
,
71 ly_string2scm (ps_name
),
72 scm_list_2 (ly_string2scm (filename
),
73 scm_from_int (face_index
)));
77 Pango_font::derived_mark () const
79 scm_gc_mark (physical_font_tab_
);
83 get_glyph_index_name (char *s
,
86 sprintf (s
, "glyphIndex%lX", code
);
90 get_unicode_name (char *s
,
94 sprintf (s
, "u%lX", code
);
96 sprintf (s
, "uni%04lX", code
);
100 Pango_font::pango_item_string_stencil (PangoItem
const *item
,
102 bool tight_bbox
) const
104 const int GLYPH_NAME_LEN
= 256;
105 char glyph_name
[GLYPH_NAME_LEN
];
106 PangoAnalysis
const *pa
= &(item
->analysis
);
107 PangoGlyphString
*pgs
= pango_glyph_string_new ();
109 pango_shape (str
.c_str () + item
->offset
,
110 item
->length
, (PangoAnalysis
*) pa
, pgs
);
112 PangoRectangle logical_rect
;
113 PangoRectangle ink_rect
;
114 pango_glyph_string_extents (pgs
, pa
->font
, &ink_rect
, &logical_rect
);
116 PangoFcFont
*fcfont
= G_TYPE_CHECK_INSTANCE_CAST (pa
->font
,
120 FT_Face ftface
= pango_fc_font_lock_face (fcfont
);
122 PangoRectangle
const *which_rect
= tight_bbox
? &ink_rect
125 Box
b (Interval (PANGO_LBEARING (logical_rect
),
126 PANGO_RBEARING (logical_rect
)),
127 Interval (-PANGO_DESCENT (*which_rect
),
128 PANGO_ASCENT (*which_rect
)));
131 char const *ps_name_str0
= FT_Get_Postscript_Name (ftface
);
132 FcPattern
*fcpat
= fcfont
->font_pattern
;
134 FcChar8
*file_name_as_ptr
= 0;
135 FcPatternGetString (fcpat
, FC_FILE
, 0, &file_name_as_ptr
);
137 // due to a bug in FreeType 2.3.7 and earlier we can't use
138 // ftface->face_index; it is always zero for some font formats,
139 // in particular TTCs which we are interested in
141 FcPatternGetInteger (fcpat
, FC_INDEX
, 0, &face_index
);
144 if (file_name_as_ptr
)
145 // Normalize file name.
146 file_name
= File_name ((char const *)file_name_as_ptr
).to_string ();
148 SCM glyph_exprs
= SCM_EOL
;
149 SCM
*tail
= &glyph_exprs
;
151 Index_to_charcode_map
const *cmap
= 0;
152 bool has_glyph_names
= ftface
->face_flags
& FT_FACE_FLAG_GLYPH_NAMES
;
153 if (!has_glyph_names
)
154 cmap
= all_fonts_global
->get_index_to_charcode_map (
155 file_name
, face_index
, ftface
);
157 bool is_ttf
= string (FT_Get_X11_Font_Format (ftface
)) == "TrueType";
158 bool cid_keyed
= false;
160 for (int i
= 0; i
< pgs
->num_glyphs
; i
++)
162 PangoGlyphInfo
*pgi
= pgs
->glyphs
+ i
;
164 PangoGlyph pg
= pgi
->glyph
;
165 PangoGlyphGeometry ggeo
= pgi
->geometry
;
167 glyph_name
[0] = '\0';
170 int errorcode
= FT_Get_Glyph_Name (ftface
, pg
, glyph_name
,
174 _f ("FT_Get_Glyph_Name () error: %s",
175 freetype_error_string (errorcode
).c_str ()));
178 SCM char_id
= SCM_EOL
;
179 if (glyph_name
[0] == '\0'
182 && cmap
->find (pg
) != cmap
->end ())
184 FT_ULong char_code
= cmap
->find (pg
)->second
;
185 get_unicode_name (glyph_name
, char_code
);
188 if (glyph_name
[0] == '\0' && has_glyph_names
)
191 _f ("Glyph has no name, but font supports glyph naming.\n"
192 "Skipping glyph U+%0X, file %s",
193 pg
, file_name
.c_str ()));
197 if (glyph_name
== string (".notdef") && is_ttf
)
198 glyph_name
[0] = '\0';
200 if (glyph_name
[0] == '\0' && is_ttf
)
201 // Access by glyph index directly.
202 get_glyph_index_name (glyph_name
, pg
);
204 if (glyph_name
[0] == '\0')
208 char_id
= scm_from_uint32 (pg
);
211 char_id
= scm_from_locale_string (glyph_name
);
213 *tail
= scm_cons (scm_list_4 (scm_from_double (ggeo
.width
* scale_
),
214 scm_from_double (ggeo
.x_offset
* scale_
),
215 scm_from_double (ggeo
.y_offset
* scale_
),
218 tail
= SCM_CDRLOC (*tail
);
221 pango_glyph_string_free (pgs
);
223 PangoFontDescription
*descr
= pango_font_describe (pa
->font
);
224 Real size
= pango_font_description_get_size (descr
)
225 / (Real (PANGO_SCALE
));
228 warning (_f ("no PostScript font name for font `%s'", file_name
));
233 && (file_name
.find (".otf") != NPOS
234 || file_name
.find (".cff") != NPOS
))
236 // UGH: kludge a PS name for OTF/CFF fonts.
237 string name
= file_name
;
238 ssize idx
= file_name
.find (".otf");
240 idx
= file_name
.find (".cff");
242 name
= name
.substr (0, idx
);
244 ssize slash_idx
= name
.rfind ('/');
245 if (slash_idx
!= NPOS
)
248 name
= name
.substr (slash_idx
,
249 name
.length () - slash_idx
);
252 string initial
= name
.substr (0, 1);
253 initial
= String_convert::to_upper (initial
);
254 name
= name
.substr (1, name
.length () - 1);
255 name
= String_convert::to_lower (name
);
256 ps_name
= initial
+ name
;
258 else if (ps_name_str0
)
259 ps_name
= ps_name_str0
;
261 if (ps_name
.length ())
263 ((Pango_font
*) this)->register_font_file (file_name
,
266 pango_fc_font_unlock_face (fcfont
);
268 SCM expr
= scm_list_5 (ly_symbol2scm ("glyph-string"),
269 ly_string2scm (ps_name
),
270 scm_from_double (size
),
271 scm_from_bool (cid_keyed
),
272 ly_quote_scm (glyph_exprs
));
274 return Stencil (b
, expr
);
277 warning (_ ("FreeType face has no PostScript font name"));
282 Pango_font::physical_font_tab () const
284 return physical_font_tab_
;
288 Pango_font::word_stencil (string str
, bool feta
) const
290 return text_stencil (str
, feta
, true);
294 Pango_font::text_stencil (string str
, bool feta
) const
296 return text_stencil (str
, feta
, false);
300 Pango_font::text_stencil (string str
,
305 = pango_itemize (context_
,
307 0, str
.length (), attribute_list_
,
314 Direction text_dir
= RIGHT
;
315 for (GList
*p
= items
; p
; p
= p
->next
)
317 PangoItem
*item
= (PangoItem
*) p
->data
;
318 if (item
->analysis
.level
== PANGO_DIRECTION_RTL
)
322 for (GList
*ptr
= items
; ptr
; ptr
= ptr
->next
)
324 PangoItem
*item
= (PangoItem
*) ptr
->data
;
326 Stencil item_stencil
= pango_item_string_stencil (item
, str
, tight
);
328 if (text_dir
== RIGHT
)
330 item_stencil
.translate_axis (last_x
, X_AXIS
);
331 last_x
= item_stencil
.extent (X_AXIS
)[RIGHT
];
333 else if (text_dir
== LEFT
)
334 dest
.translate_axis (item_stencil
.extent (X_AXIS
)[RIGHT
], X_AXIS
);
336 #if 0 // Check extents.
337 if (!item_stencil
.extent_box ()[X_AXIS
].is_empty ())
339 Stencil frame
= Lookup::frame (item_stencil
.extent_box (), 0.1, 0.1);
342 Stencil
dimless_frame (empty
, frame
.expr ());
343 dest
.add_stencil (frame
);
347 dest
.add_stencil (item_stencil
);
350 string name
= get_output_backend_name ();
351 string output_mod
= "scm output-" + name
;
352 SCM mod
= scm_c_resolve_module (output_mod
.c_str ());
354 bool has_utf8_string
= false;
356 if (ly_is_module (mod
))
358 SCM utf8_string
= ly_module_lookup (mod
, ly_symbol2scm ("utf-8-string"));
360 has_utf8_string should only be true when utf8_string is a
361 variable that is bound to a *named* procedure.
363 if (utf8_string
!= SCM_BOOL_F
364 && scm_procedure_name (SCM_VARIABLE_REF (utf8_string
)) != SCM_BOOL_F
)
365 has_utf8_string
= true;
369 The SVG backend only uses utf-8-string for the non-music
370 fonts, hence the check here. --pmccarty
372 TODO: use a program option (-dmusic-strings-to-paths) here
373 instead that is enabled only when -dbackend=svg.
375 if ((name
== "svg" && !feta
) || (name
!= "svg" && has_utf8_string
))
377 // For Pango based backends, we take a shortcut.
378 SCM exp
= scm_list_3 (ly_symbol2scm ("utf-8-string"),
379 ly_string2scm (description_string ()),
380 ly_string2scm (str
));
382 Box
b (Interval (0, 0), Interval (0, 0));
383 b
.unite (dest
.extent_box ());
384 return Stencil (b
, exp
);
393 Pango_font::description_string () const
395 char *descr_string
= pango_font_description_to_string (pango_description_
);
396 string
s (descr_string
);
397 g_free (descr_string
);
402 Pango_font::font_file_name () const
407 #endif // HAVE_PANGO_FT2