X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Ftext-interface.cc;h=c406beaab5436a9a053883651ff02d1b5364ed87;hb=aefc5a4057a0a61f1d7e4411cc1c5061d9aea6b7;hp=7bb4ceadc07c53ded50199d088572280975b1308;hpb=4bb29573149a0ffa1f881c5e38a0fe68e9e76b67;p=lilypond.git diff --git a/lily/text-interface.cc b/lily/text-interface.cc index 7bb4ceadc0..c406beaab5 100644 --- a/lily/text-interface.cc +++ b/lily/text-interface.cc @@ -1,7 +1,7 @@ /* This file is part of LilyPond, the GNU music typesetter. - Copyright (C) 1998--2011 Han-Wen Nienhuys + Copyright (C) 1998--2012 Han-Wen Nienhuys Jan Nieuwenhuizen LilyPond is free software: you can redistribute it and/or modify @@ -19,7 +19,9 @@ */ #include "text-interface.hh" +#include "skyline-pair.hh" +#include "lookup.hh" #include "config.hh" #include "font-interface.hh" #include "grob.hh" @@ -28,25 +30,39 @@ #include "modified-font-metric.hh" #include "output-def.hh" #include "pango-font.hh" +#include "program-option.hh" +#include "international.hh" #include "warn.hh" static void -replace_whitespace (string *str) +replace_special_characters (string &str, SCM props) { - vsize i = 0; - vsize n = str->size (); + SCM replacement_alist = ly_chain_assoc_get (ly_symbol2scm ("replacement-alist"), + props, + SCM_EOL); - while (i < n) + int max_length = 0; + for (SCM s = replacement_alist; scm_is_pair (s); s = scm_cdr (s)) { - char cur = (*str)[i]; - - // avoid the locale-dependent isspace - if (cur == '\n' || cur == '\t' || cur == '\v') - (*str)[i] = ' '; - - vsize char_len = utf8_char_len (cur); + max_length = max (max_length, scm_to_int + (scm_string_length (scm_caar (s)))); + } - i += char_len; + for (vsize i = 0; i < str.size (); i++) + { + /* Don't match in mid-UTF-8 */ + if ((str[i] & 0xc0) == 0x80) + continue; + for (vsize j = max_length + 1; j--;) + { + if (j > str.size () - i) + continue; + string dummy = str.substr (i, j); + SCM ligature = ly_assoc_get (ly_string2scm (dummy), + replacement_alist, SCM_BOOL_F); + if (scm_is_true (ligature)) + str.replace (i, j, robust_scm2string (ligature, "")); + } } } @@ -63,7 +79,7 @@ Text_interface::interpret_string (SCM layout_smob, Output_def *layout = unsmob_output_def (layout_smob); Font_metric *fm = select_encoded_font (layout, props); - replace_whitespace (&str); + replace_special_characters (str, props); /* We want to filter strings with a music font that pass through @@ -93,28 +109,54 @@ Text_interface::interpret_markup (SCM layout_smob, SCM props, SCM markup) { if (scm_is_string (markup)) return interpret_string (layout_smob, props, markup); - else if (scm_is_pair (markup)) + else if (is_markup (markup)) { SCM func = scm_car (markup); SCM args = scm_cdr (markup); - if (!is_markup (markup)) - programming_error ("markup head has no markup signature"); - return scm_apply_2 (func, layout_smob, props, args); + /* Use a hare/tortoise algorithm to detect whether we are in a cycle, + * i.e. whether we have already encountered the same markup in the + * current branch of the markup tree structure. */ + static vector encountered_markups; + size_t depth = encountered_markups.size (); + if (depth > 0) + { + int slow = depth / 2; + if (ly_is_equal (encountered_markups[slow], markup)) + { + string name = ly_symbol2string (scm_procedure_name (func)); + // TODO: Also print the arguments of the markup! + non_fatal_error (_f ("Cyclic markup detected: %s", name)); + return Stencil ().smobbed_copy (); + } + } + + /* Check for non-terminating markups, e.g. recursive calls with + * changing arguments */ + SCM opt_depth = ly_get_option (ly_symbol2scm ("max-markup-depth")); + size_t max_depth = robust_scm2int (opt_depth, 1024); + if (depth > max_depth) + { + string name = ly_symbol2string (scm_procedure_name (func)); + // TODO: Also print the arguments of the markup! + non_fatal_error (_f ("Markup depth exceeds maximal value of %d; " + "Markup: %s", max_depth, name.c_str ())); + return Stencil ().smobbed_copy (); + } + + encountered_markups.push_back (markup); + SCM retval = scm_apply_2 (func, layout_smob, props, args); + encountered_markups.pop_back (); + return retval; } else { - programming_error ("Object is not a markup. "); + programming_error ("Object is not a markup."); scm_puts ("This object should be a markup: ", scm_current_error_port ()); scm_display (markup, scm_current_error_port ()); scm_puts ("\n", scm_current_error_port ()); - Box b; - b[X_AXIS].set_empty (); - b[Y_AXIS].set_empty (); - - Stencil s (b, SCM_EOL); - return s.smobbed_copy (); + return Stencil ().smobbed_copy (); } } @@ -133,11 +175,14 @@ Text_interface::print (SCM grob) bool Text_interface::is_markup (SCM x) { - return (scm_is_string (x) - || (scm_is_pair (x) - && SCM_BOOL_F - != scm_object_property (scm_car (x), - ly_symbol2scm ("markup-signature")))); + return scm_is_string (x) + || (scm_is_pair (x) + && scm_is_true + (scm_object_property (scm_car (x), + ly_symbol2scm ("markup-signature"))) + && scm_is_false + (scm_object_property (scm_car (x), + ly_symbol2scm ("markup-list-command")))); } bool @@ -158,6 +203,7 @@ ADD_INTERFACE (Text_interface, /* properties */ "baseline-skip " + "replacement-alist " "text " "word-space " "text-direction "