From: Shaun Ren sren@codeweavers.com
--- dlls/sapi/tests/tts.c | 6 +- dlls/sapi/xml.c | 135 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 133 insertions(+), 8 deletions(-)
diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 49d312fed70..f355ff7c98d 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -998,13 +998,13 @@ static void test_spvoice_ssml(void) reset_engine_params(&test_engine);
hr = ISpVoice_Speak(voice, bad_text1, SPF_IS_XML | SPF_PARSE_SSML, NULL); - todo_wine ok(hr == SPERR_UNSUPPORTED_FORMAT, "got %#lx.\n", hr); + ok(hr == SPERR_UNSUPPORTED_FORMAT, "got %#lx.\n", hr);
hr = ISpVoice_Speak(voice, bad_text2, SPF_IS_XML | SPF_PARSE_SSML, NULL); - todo_wine ok(hr == SPERR_UNSUPPORTED_FORMAT, "got %#lx.\n", hr); + ok(hr == SPERR_UNSUPPORTED_FORMAT, "got %#lx.\n", hr);
hr = ISpVoice_Speak(voice, bad_text3, SPF_IS_XML | SPF_PARSE_SSML, NULL); - todo_wine ok(hr == SPERR_UNSUPPORTED_FORMAT || broken(hr == S_OK) /* win7 */, "got %#lx.\n", hr); + ok(hr == SPERR_UNSUPPORTED_FORMAT || broken(hr == S_OK) /* win7 */, "got %#lx.\n", hr);
reset_engine_params(&test_engine);
diff --git a/dlls/sapi/xml.c b/dlls/sapi/xml.c index 442537fef4c..7ed7505f355 100644 --- a/dlls/sapi/xml.c +++ b/dlls/sapi/xml.c @@ -80,11 +80,32 @@ static inline const char *debugstr_xmlstr(const xmlstr_t *str) return debugstr_wn(str->ptr, str->len); }
+static WCHAR *xmlstrcpyW(WCHAR *dst, const xmlstr_t* src) +{ + memcpy(dst, src->ptr, src->len * sizeof(WCHAR)); + dst[src->len] = 0; + return dst; +} + +static WCHAR *xmlstrdupW(const xmlstr_t* str) +{ + WCHAR *strW; + + if (!(strW = malloc((str->len + 1) * sizeof(WCHAR)))) + return NULL; + return xmlstrcpyW(strW, str); +} + static inline BOOL xmlstr_eq(const xmlstr_t* xmlstr, const WCHAR *str) { return !wcsncmp(xmlstr->ptr, str, xmlstr->len) && !str[xmlstr->len]; }
+static inline BOOL xml_attr_eq(const struct xml_attr *attr, const WCHAR *name) +{ + return xmlstr_eq(&attr->name, name); +} + static inline BOOL xml_elem_eq(const struct xml_elem *elem, const WCHAR *ns, const WCHAR *name) { if (!xmlstr_eq(&elem->name, name)) return FALSE; @@ -367,11 +388,118 @@ static BOOL next_xml_elem(struct xml_parser *parser, struct xml_elem *elem, cons else return set_error(parser); }
-static HRESULT parse_ssml_speak_elem(struct xml_parser *parser, SPVSTATE *state) +static BOOL next_text_or_xml_elem(struct xml_parser *parser, BOOL *is_text, struct xml_elem *elem, + const struct xml_elem *parent) { + if (parser->error) return FALSE; + + while (parser->ptr < parser->end) + { + if (*parser->ptr == '<') + { + *is_text = FALSE; + + if (!next_xml_elem(parser, elem, parent)) + return FALSE; + + if (!is_special_xml_markup(elem)) + return TRUE; + else if (!skip_special_xml_markup(parser, elem)) + return set_error(parser); + } + else + { + *is_text = TRUE; + return TRUE; + } + } + return FALSE; +} + +static HRESULT add_sapi_text_fragment(struct xml_parser *parser, const SPVSTATE *state) +{ + FIXME("stub.\n"); return E_NOTIMPL; }
+static HRESULT parse_ssml_elems(struct xml_parser *parser, const SPVSTATE *state, const struct xml_elem *parent) +{ + struct xml_elem elem; + BOOL is_text; + HRESULT hr = S_OK; + + while (SUCCEEDED(hr) && next_text_or_xml_elem(parser, &is_text, &elem, parent)) + { + if (is_text) + { + hr = add_sapi_text_fragment(parser, state); + } + else + { + FIXME("Unknown element %s.\n", debugstr_xmlstr(&elem.name)); + hr = E_NOTIMPL; + } + } + + if (SUCCEEDED(hr) && parser->error) + return SPERR_UNSUPPORTED_FORMAT; + return hr; +} + +static HRESULT parse_ssml_speak_elem(struct xml_parser *parser, const struct xml_elem *parent, SPVSTATE *state) +{ + struct xml_attr attr; + BOOL end = FALSE; + BOOL has_version = FALSE; + LCID lcid = 0; + + while (next_xml_attr(parser, &attr, &end)) + { + if (xml_attr_eq(&attr, L"version")) + { + if (!xmlstr_eq(&attr.value, L"1.0")) + { + ERR("Invalid SSML version %s.\n", debugstr_xmlstr(&attr.value)); + return SPERR_UNSUPPORTED_FORMAT; + } + + has_version = TRUE; + } + else if (xml_attr_eq(&attr, L"xml:lang")) + { + WCHAR *lang = xmlstrdupW(&attr.value); + + if (!lang) return E_OUTOFMEMORY; + lcid = LocaleNameToLCID(lang, 0); + free(lang); + + if (!lcid) + { + ERR("Invalid xml:lang %s.\n", debugstr_xmlstr(&attr.value)); + return SPERR_UNSUPPORTED_FORMAT; + } + } + } + + if (!has_version) + { + ERR("Missing version attribute.\n"); + return SPERR_UNSUPPORTED_FORMAT; + } + if (!lcid) + { + ERR("Missing xml:lang attribute.\n"); + return SPERR_UNSUPPORTED_FORMAT; + } + + if (end) return S_OK; + + state->eAction = SPVA_Speak; + state->LangID = LANGIDFROMLCID(lcid); + + return parse_ssml_elems(parser, state, parent); +} + static HRESULT parse_ssml_contents(const WCHAR *contents, const WCHAR *end, SPVSTATE *state, SPVTEXTFRAG **frag_list) { struct xml_parser parser = {0}; @@ -404,7 +532,7 @@ static HRESULT parse_ssml_contents(const WCHAR *contents, const WCHAR *end, SPVS
if (!xml_elem_eq(&elem, ssml_ns, L"speak")) return SPERR_UNSUPPORTED_FORMAT; - if (FAILED(hr = parse_ssml_speak_elem(&parser, state))) + if (FAILED(hr = parse_ssml_speak_elem(&parser, &elem, state))) return hr;
if (next_xml_elem(&parser, &elem, &parent)) @@ -413,9 +541,6 @@ static HRESULT parse_ssml_contents(const WCHAR *contents, const WCHAR *end, SPVS return SPERR_UNSUPPORTED_FORMAT; }
- if (parser.error) - return SPERR_UNSUPPORTED_FORMAT; - return S_OK; }