X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Ftext-interface.cc;h=909ab9b099666203e133796e53c78e328fff40a7;hb=47db9a3883d726ca53e2133a3b2298f78dd6a32e;hp=8071df3cb856428af7aacac5aaa87028f9fecbb8;hpb=0e13fe23a0698ae81b9e618b15841b8a88919ed7;p=lilypond.git diff --git a/lily/text-interface.cc b/lily/text-interface.cc index 8071df3cb8..909ab9b099 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--2015 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,26 +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, "")); + } } } @@ -61,10 +76,10 @@ Text_interface::interpret_string (SCM layout_smob, LY_ASSERT_TYPE (scm_is_string, markup, 3); string str = ly_scm2string (markup); - Output_def *layout = unsmob_output_def (layout_smob); + Output_def *layout = Output_def::unsmob (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 @@ -80,6 +95,11 @@ Text_interface::interpret_string (SCM layout_smob, return fm->text_stencil (layout, str, is_music).smobbed_copy (); } +static size_t markup_depth = 0; + +void markup_up_depth (void *) { ++markup_depth; } +void markup_down_depth (void *) { --markup_depth; } + MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Text_interface, interpret_markup, 3, 0, "Convert a text markup into a stencil." " Takes three arguments, @var{layout}, @var{props}, and @var{markup}.\n" @@ -94,18 +114,39 @@ 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); + /* 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); + + // Don't use SCM_F_DYNWIND_REWINDABLE since it may be expensive + // without any obvious use for retaining continuations into + // markup expansion + scm_dynwind_begin ((scm_t_dynwind_flags)0); + // scm_dynwind_rewind_handler (markup_up_depth, 0, SCM_F_WIND_EXPLICITLY); + markup_up_depth (0); + scm_dynwind_unwind_handler (markup_down_depth, 0, SCM_F_WIND_EXPLICITLY); + if (markup_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 (); + } + + SCM retval = scm_apply_2 (func, layout_smob, props, args); + scm_dynwind_end (); + 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 ()); @@ -118,7 +159,7 @@ MAKE_SCHEME_CALLBACK (Text_interface, print, 1); SCM Text_interface::print (SCM grob) { - Grob *me = unsmob_grob (grob); + Grob *me = Grob::unsmob (grob); SCM t = me->get_property ("text"); SCM chain = Font_interface::text_font_alist_chain (me); @@ -129,11 +170,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 @@ -154,8 +198,10 @@ ADD_INTERFACE (Text_interface, /* properties */ "baseline-skip " + "replacement-alist " "text " "word-space " "text-direction " + "flag-style " );