winex11.drv: import X11's "text/html" as "HTML Format" (try 3)
Damjan Jovanovic
damjan.jov at gmail.com
Fri Aug 8 13:05:54 CDT 2014
Implements proper importing of "text/html" into Windows's "HTML
Format" which fixes pasting rich text into a large number of apps and
closes #7372.
Try 3 doesn't use libxml as requested by Alexandre (I still prefer it
- much simpler and shorter to use and probably parses more correctly),
correctly deals with both null terminated and non-terminated text/html
data, and correctly calculates the lengths of Firefox's UTF-16LE
strings.
Damjan Jovanovic
-------------- next part --------------
commit 381abb6b6d195c5bbe27bb1308fd507b82743a1d
Author: Damjan Jovanovic <damjan.jov at gmail.com>
Date: Sat Jul 19 14:37:08 2014 +0200
winex11.drv: import X11's "text/html" as "HTML Format"
diff --git a/dlls/winex11.drv/clipboard.c b/dlls/winex11.drv/clipboard.c
index b2705b4..f08eee3 100644
--- a/dlls/winex11.drv/clipboard.c
+++ b/dlls/winex11.drv/clipboard.c
@@ -148,6 +148,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 +344,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 +1570,207 @@ static HANDLE X11DRV_CLIPBOARD_ImportEnhMetaFile(Display *display, Window w, Ato
}
+static char* read_and_standardize_text_html(Display *display, Window w, Atom prop)
+{
+ char *textHtml;
+ unsigned long textHtmlLen;
+ BOOL needHtmlTag;
+ BOOL needBodyTag;
+ int startOfMarkup;
+ char *fullHtml = NULL;
+
+ 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], (textHtmlLen-2)/2, NULL, 0, NULL, NULL);
+ textHtmlUtf8 = HeapAlloc(GetProcessHeap(), 0, size);
+ if (textHtmlUtf8)
+ {
+ WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)&textHtml[2], (textHtmlLen-2)/2, textHtmlUtf8, size, NULL, NULL);
+ HeapFree(GetProcessHeap(), 0, textHtml);
+ textHtml = textHtmlUtf8;
+ textHtmlLen = size;
+ }
+ else
+ {
+ ERR("out of memory\n");
+ goto end;
+ }
+ }
+
+ /* Sometimes textHtml is null terminated. */
+ while (textHtmlLen > 0 && textHtml[textHtmlLen-1] == '\0') {
+ --textHtmlLen;
+ }
+
+ /* While HTML fragments are supposed to be valid in Windows, some apps only want
+ * to paste a complete HTML document. So if we got an HTML fragment, complete it. */
+ needHtmlTag = FALSE;
+ needBodyTag = FALSE;
+ if (textHtmlLen >= 7 && strncasecmp(&textHtml[textHtmlLen - 7], "</HTML>", 7))
+ {
+ int i;
+ needHtmlTag = TRUE;
+ needBodyTag = TRUE;
+ for (i = textHtmlLen - 7; i > 0; i--)
+ {
+ if (!strncasecmp(&textHtml[i], "</BODY>", 7))
+ {
+ needBodyTag = FALSE;
+ break;
+ }
+ }
+ }
+ for (startOfMarkup = 0; startOfMarkup < textHtmlLen; startOfMarkup++)
+ {
+ if (textHtml[startOfMarkup] == '<' &&
+ startOfMarkup + 1 < textHtmlLen &&
+ textHtml[startOfMarkup+1] != '!' && /* <!DOCTYPE ...> or <!-- comment --> */
+ textHtml[startOfMarkup+1] != '/') /* </TAG> */
+ {
+ if (memchr(&textHtml[startOfMarkup+1], '>', textHtmlLen - startOfMarkup - 1))
+ break;
+ }
+ }
+ if (startOfMarkup == textHtmlLen)
+ {
+ ERR("text/html contents invalid\n");
+ goto end;
+ }
+ fullHtml = HeapAlloc(GetProcessHeap(), 0, textHtmlLen +
+ (needBodyTag ? 6 + 7 : 0) + (needHtmlTag ? 6 + 7 : 0) + 1);
+ if (fullHtml)
+ {
+ int next;
+ memcpy(fullHtml, textHtml, startOfMarkup);
+ next = startOfMarkup;
+ if (needHtmlTag)
+ {
+ memcpy(&fullHtml[next], "<HTML>", 6);
+ next += 6;
+ }
+ if (needBodyTag)
+ {
+ memcpy(&fullHtml[next], "<BODY>", 6);
+ next += 6;
+ }
+ memcpy(&fullHtml[next], &textHtml[startOfMarkup], textHtmlLen - startOfMarkup);
+ next += textHtmlLen - startOfMarkup;
+ if (needBodyTag)
+ {
+ memcpy(&fullHtml[next], "</BODY>", 7);
+ next += 7;
+ }
+ if (needHtmlTag)
+ {
+ memcpy(&fullHtml[next], "</HTML>", 7);
+ next += 7;
+ }
+ fullHtml[next] = '\0';
+ }
+ else
+ ERR("out of memory\n");
+
+end:
+ HeapFree(GetProcessHeap(), 0, textHtml);
+ return fullHtml;
+}
+
+
+/**************************************************************************
+ * X11DRV_CLIPBOARD_ImportTextHtml
+ *
+ * Import text/html into "HTML Format".
+ */
+static HANDLE X11DRV_CLIPBOARD_ImportTextHtml(Display *display, Window w, Atom prop)
+{
+ static const char *startFragment = "<!--StartFragment -->";
+ static const char *endFragment = "<!--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], "<BODY>", 6) == 0)
+ {
+ bodyStart = i + 6;
+ break;
+ }
+ }
+ if (bodyStart < 0)
+ {
+ ERR("HTML doesn't have <BODY>\n");
+ goto end;
+ }
+
+ for (i = strlen(textHtml) - 1; i >= bodyStart; i--)
+ {
+ if (strncasecmp(&textHtml[i], "</BODY>", 7) == 0)
+ {
+ bodyEnd = i;
+ break;
+ }
+ }
+ if (bodyEnd < 0)
+ {
+ ERR("HTML doesn't have </BODY>\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:
+ HeapFree(GetProcessHeap(), 0, textHtml);
+ return hClipData;
+}
+
+
/**************************************************************************
* X11DRV_CLIPBOARD_ImportTextUriList
*
More information about the wine-patches
mailing list