]> git.donarmstrong.com Git - lilypond.git/blob - lily/text-interface.cc
Move UTF-8 char length routine into separate function.
[lilypond.git] / lily / text-interface.cc
1 /*
2   text-interface.cc -- implement Text_interface
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1998--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
7   Jan Nieuwenhuizen <janneke@gnu.org>
8 */
9
10 #include "text-interface.hh"
11
12 #include "config.hh"
13 #include "font-interface.hh"
14 #include "grob.hh"
15 #include "main.hh"
16 #include "misc.hh"
17 #include "modified-font-metric.hh"
18 #include "output-def.hh"
19 #include "pango-font.hh"
20 #include "warn.hh"
21
22 static void
23 replace_whitespace (string *str)
24 {
25   vsize i = 0;
26   vsize n = str->size ();
27
28   while (i < n)
29     {
30       char cur = (*str)[i];
31
32       // avoid the locale-dependent isspace
33       if (cur == '\n' || cur == '\t' || cur == '\v')
34         (*str)[i] = ' ';
35
36       vsize char_len = utf8_char_len (cur);
37
38       i += char_len;
39     }
40 }
41
42 MAKE_SCHEME_CALLBACK (Text_interface, interpret_string, 3);
43 SCM
44 Text_interface::interpret_string (SCM layout_smob,
45                                   SCM props,
46                                   SCM markup)
47 {
48   LY_ASSERT_SMOB (Output_def, layout_smob, 1);
49   LY_ASSERT_TYPE (scm_is_string, markup, 3);
50
51   string str = ly_scm2string (markup);
52   Output_def *layout = unsmob_output_def (layout_smob);
53   Font_metric *fm = select_encoded_font (layout, props);
54
55   replace_whitespace (&str);
56
57   /*
58     We want to use "glyph-string" in the SVG backend for all
59     music fonts (Emmentaler and Aybabtu) that pass through the
60     text interface.  Here the font encoding is checked to see if
61     it matches one of the music font encodings.  --pmccarty
62   */
63   SCM encoding = ly_chain_assoc_get (ly_symbol2scm ("font-encoding"),
64                                      props,
65                                      SCM_BOOL_F);
66   SCM music_encodings = ly_lily_module_constant ("all-music-font-encodings");
67
68   if (scm_memq (encoding, music_encodings) != SCM_BOOL_F)
69     return fm->word_stencil (str, true).smobbed_copy ();
70   else
71     return fm->word_stencil (str, false).smobbed_copy ();
72 }
73
74 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Text_interface, interpret_markup, 3, 0,
75                                    "Convert a text markup into a stencil."
76 "  Takes three arguments, @var{layout}, @var{props}, and @var{markup}.\n"
77 "\n"
78 "@var{layout} is a @code{\\layout} block; it may be obtained from a grob with"
79 " @code{ly:grob-layout}.  @var{props} is an alist chain, i.e. a list of"
80 "  alists.  This is typically obtained with"
81 " @code{(ly:grob-alist-chain grob (ly:output-def-lookup layout 'text-font-defaults))}."
82 "  @var{markup} is the markup text to be processed.");
83 SCM
84 Text_interface::interpret_markup (SCM layout_smob, SCM props, SCM markup)
85 {
86   if (scm_is_string (markup))
87     return interpret_string (layout_smob, props, markup);
88   else if (scm_is_pair (markup))
89     {
90       SCM func = scm_car (markup);
91       SCM args = scm_cdr (markup);
92       if (!is_markup (markup))
93         programming_error ("markup head has no markup signature");
94
95       return scm_apply_2 (func, layout_smob, props, args);
96     }
97   else
98     {
99       programming_error ("Object is not a markup. ");
100       scm_puts ("This object should be a markup: ", scm_current_error_port ());
101       scm_display (markup, scm_current_error_port ());
102       scm_puts ("\n", scm_current_error_port ());
103
104       Box b;
105       b[X_AXIS].set_empty ();
106       b[Y_AXIS].set_empty ();
107
108       Stencil s (b, SCM_EOL);
109       return s.smobbed_copy ();
110     }
111 }
112
113 MAKE_SCHEME_CALLBACK (Text_interface, print, 1);
114 SCM
115 Text_interface::print (SCM grob)
116 {
117   Grob *me = unsmob_grob (grob);
118
119   SCM t = me->get_property ("text");
120   SCM chain = Font_interface::text_font_alist_chain (me);
121   return interpret_markup (me->layout ()->self_scm (), chain, t);
122 }
123
124 /* Ugh. Duplicated from Scheme.  */
125 bool
126 Text_interface::is_markup (SCM x)
127 {
128   return (scm_is_string (x)
129           || (scm_is_pair (x)
130               && SCM_BOOL_F
131               != scm_object_property (scm_car (x),
132                                       ly_symbol2scm ("markup-signature"))));
133 }
134
135 bool
136 Text_interface::is_markup_list (SCM x)
137 {
138   SCM music_list_p = ly_lily_module_constant ("markup-list?");
139   return scm_is_true (scm_call_1 (music_list_p, x));
140 }
141
142
143 ADD_INTERFACE (Text_interface,
144                "A Scheme markup text, see @ruser{Formatting text} and"
145                " @rextend{New markup command definition}.\n"
146                "\n"
147                "There are two important commands:"
148                " @code{ly:text-interface::print}, which is a"
149                " grob callback, and"
150                " @code{ly:text-interface::interpret-markup}.",
151
152                /* properties */
153                "baseline-skip "
154                "text "
155                "word-space "
156                "text-direction "
157                );
158