From: Damjan Jovanovic Subject: winex11.drv: import X11's "text/html" as "HTML Format" (try 2) Message-Id: Date: Thu, 24 Jul 2014 20:07:26 +0200 Implements proper importing of "text/html" into Windows's "HTML Format". Does conversion from (Firefox's) UTF16-LE with BOM into UTF-8 w/o BOM if necessary, rewrites the HTML with libxml2 to generate a complete HTML document with and tags, finds the location of the tag and uses its contents surrounded by and comments for the StartFragment/EndFragment addresses. Fixes pasting rich text into a large number of apps and closes #7372. Try 2 doesn't use C99 features, correctly calculates lengths of unsigned ints in the HTML Format description as 10 instead of 8 bytes, and has a better worded error message if libxml2 wasn't available at compile time. Feedback is appreciated. Damjan Jovanovic --- dlls/winex11.drv/Makefile.in | 4 +- dlls/winex11.drv/clipboard.c | 157 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 158 insertions(+), 3 deletions(-) diff --git a/dlls/winex11.drv/Makefile.in b/dlls/winex11.drv/Makefile.in index 463eefd..97ac5ff 100644 --- a/dlls/winex11.drv/Makefile.in +++ b/dlls/winex11.drv/Makefile.in @@ -1,8 +1,8 @@ MODULE = winex11.drv IMPORTS = uuid user32 gdi32 advapi32 DELAYIMPORTS = comctl32 ole32 shell32 imm32 -EXTRAINCL = $(X_CFLAGS) -EXTRALIBS = $(X_LIBS) $(X_EXTRA_LIBS) +EXTRAINCL = $(X_CFLAGS) $(XML2_CFLAGS) +EXTRALIBS = $(X_LIBS) $(X_EXTRA_LIBS) $(XML2_LIBS) C_SRCS = \ bitblt.c \ diff --git a/dlls/winex11.drv/clipboard.c b/dlls/winex11.drv/clipboard.c index b2705b4..05fe41b 100644 --- a/dlls/winex11.drv/clipboard.c +++ b/dlls/winex11.drv/clipboard.c @@ -72,6 +72,10 @@ #ifdef HAVE_UNISTD_H # include #endif +#ifdef HAVE_LIBXML2 +# include +# include +#endif #include #include #include @@ -148,6 +152,7 @@ static HANDLE X11DRV_CLIPBOARD_ImportImageBmp(Display *d, Window w, Atom prop); static HANDLE X11DRV_CLIPBOARD_ImportXAString(Display *d, Window w, Atom prop); static HANDLE X11DRV_CLIPBOARD_ImportUTF8(Display *d, Window w, Atom prop); static HANDLE X11DRV_CLIPBOARD_ImportCompoundText(Display *d, Window w, Atom prop); +static HANDLE X11DRV_CLIPBOARD_ImportTextHtml(Display *display, Window w, Atom prop); static HANDLE X11DRV_CLIPBOARD_ImportTextUriList(Display *display, Window w, Atom prop); static HANDLE X11DRV_CLIPBOARD_ExportClipboardData(Display *display, Window requestor, Atom aTarget, Atom rprop, LPWINE_CLIPDATA lpData, LPDWORD lpBytes); @@ -343,10 +348,11 @@ void X11DRV_InitClipboard(void) X11DRV_CLIPBOARD_InsertClipboardFormat( RegisterClipboardFormatW(PropertyFormatMap[i].lpszFormat), GET_ATOM(PropertyFormatMap[i].prop)); - /* Set up a conversion function from "HTML Format" to "text/html" */ + /* Set up a conversion function between "HTML Format" and "text/html" */ format = X11DRV_CLIPBOARD_InsertClipboardFormat( RegisterClipboardFormatW(wszHTMLFormat), GET_ATOM(XATOM_text_html)); format->lpDrvExportFunc = X11DRV_CLIPBOARD_ExportTextHtml; + format->lpDrvImportFunc = X11DRV_CLIPBOARD_ImportTextHtml; } @@ -1568,6 +1574,155 @@ static HANDLE X11DRV_CLIPBOARD_ImportEnhMetaFile(Display *display, Window w, Ato } +static char* read_and_standardize_text_html(Display *display, Window w, Atom prop) +{ +#ifndef HAVE_LIBXML2 + ERR("sorry Wine was compiled without libxml2 support, cannot paste text/html\n"); + return NULL; +#else + char *textHtml; + unsigned long textHtmlLen; + htmlDocPtr libxml2HtmlDocument = NULL; + char *rewrittenHtml = NULL; + int rewrittenHtmlLen; + + if (!X11DRV_CLIPBOARD_ReadProperty(display, w, prop, (LPBYTE*)&textHtml, &textHtmlLen)) + return 0; + + /* Firefox uses UTF-16LE with byte order mark. Convert to UTF-8 without the BOM. */ + if (textHtmlLen >= 2 && ((BYTE*)textHtml)[0] == 0xff && ((BYTE*)textHtml)[1] == 0xfe) + { + char *textHtmlUtf8; + INT size = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)&textHtml[2], -1, NULL, 0, NULL, NULL); + textHtmlUtf8 = HeapAlloc(GetProcessHeap(), 0, size); + if (textHtmlUtf8) + { + WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)&textHtml[2], -1, textHtmlUtf8, size, NULL, NULL); + HeapFree(GetProcessHeap(), 0, textHtml); + textHtml = textHtmlUtf8; + textHtmlLen = strlen(textHtml); + } + else + { + ERR("out of memory\n"); + goto end; + } + } + + /* use libxml2 to read and rewrite it, which will add and if missing */ + libxml2HtmlDocument = htmlReadMemory(textHtml, textHtmlLen, "file:///", NULL, + HTML_PARSE_RECOVER | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING | HTML_PARSE_NONET); + if (libxml2HtmlDocument == NULL) + { + ERR("error parsing pasted text/html\n"); + goto end; + } + htmlDocDumpMemory(libxml2HtmlDocument, (xmlChar**)&rewrittenHtml, &rewrittenHtmlLen); + if (rewrittenHtml == NULL) + ERR("out of memory\n"); + +end: + if (libxml2HtmlDocument) + xmlFreeDoc(libxml2HtmlDocument); + HeapFree(GetProcessHeap(), 0, textHtml); + return rewrittenHtml; +#endif +} + + +/************************************************************************** + * X11DRV_CLIPBOARD_ImportTextHtml + * + * Import text/html into "HTML Format". + */ +static HANDLE X11DRV_CLIPBOARD_ImportTextHtml(Display *display, Window w, Atom prop) +{ + static const char *startFragment = ""; + static const char *endFragment = ""; + char *textHtml = NULL; + int bodyStart = -1; + int bodyEnd = -1; + char description[256]; + HGLOBAL hClipData = NULL; + int i; + + textHtml = read_and_standardize_text_html(display, w, prop); + if (textHtml == NULL) + goto end; + + for (i = 0; textHtml[i]; i++) + { + if (strncasecmp(&textHtml[i], "", 6) == 0) + { + bodyStart = i + 6; + break; + } + } + if (bodyStart < 0) + { + ERR("HTML doesn't have \n"); + goto end; + } + + for (i = strlen(textHtml) - 1; i >= bodyStart; i--) + { + if (strncasecmp(&textHtml[i], "", 7) == 0) + { + bodyEnd = i; + break; + } + } + if (bodyEnd < 0) + { + ERR("HTML doesn't have \n"); + goto end; + } + + snprintf(description, sizeof(description), + "Version:0.9\n" /* 12 */ + "StartHTML:%010u\n" /* 21 */ + "EndHTML:%010u\n" /* 19 */ + "StartFragment:%010u\n" /* 25 */ + "EndFragment:%010u\n", /* 23 */ + 100, + 100 + (UINT)(strlen(textHtml) + strlen(startFragment) + strlen(endFragment)), + 100 + (UINT)(bodyStart + strlen(startFragment)), + 100 + (UINT)(strlen(startFragment) + bodyEnd)); + hClipData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, + strlen(description) + strlen(textHtml) + strlen(startFragment) + strlen(endFragment) + 1); + if (hClipData) + { + char *htmlFormat; + char *next; + htmlFormat = GlobalLock(hClipData); + next = htmlFormat; + strcpy(next, description); + next += strlen(description); + memcpy(next, textHtml, bodyStart); + next += bodyStart; + memcpy(next, startFragment, strlen(startFragment)); + next += strlen(startFragment); + memcpy(next, &textHtml[bodyStart], bodyEnd - bodyStart); + next += (bodyEnd - bodyStart); + memcpy(next, endFragment, strlen(endFragment)); + next += strlen(endFragment); + memcpy(next, &textHtml[bodyEnd], strlen(textHtml) - bodyEnd); + next += (strlen(textHtml) - bodyEnd); + *next = 0; + GlobalUnlock(hClipData); + } + else + ERR("out of memory\n"); + +end: +#ifdef HAVE_LIBXML2 + if (textHtml) + xmlFree(textHtml); +#endif + return hClipData; +} + + /************************************************************************** * X11DRV_CLIPBOARD_ImportTextUriList *