Exemple #1
0
        private static unsafe void CopyHtml(Scintilla scintilla, StyleData[] styles, List<ArraySegment<byte>> styledSegments)
        {
            // NppExport -> NppExport.cpp
            // NppExport -> HTMLExporter.cpp
            // http://blogs.msdn.com/b/jmstall/archive/2007/01/21/html-clipboard.aspx
            // http://blogs.msdn.com/b/jmstall/archive/2007/01/21/sample-code-html-clipboard.aspx
            // https://msdn.microsoft.com/en-us/library/windows/desktop/ms649015.aspx

            try
            {
                long pos = 0;
                byte[] bytes;

                // Write HTML
                using (var ms = new NativeMemoryStream(styledSegments.Sum(s => s.Count)))
                using (var tw = new StreamWriter(ms, new UTF8Encoding(false)))
                {
                    const int INDEX_START_HTML = 23;
                    const int INDEX_START_FRAGMENT = 65;
                    const int INDEX_END_FRAGMENT = 87;
                    const int INDEX_END_HTML = 41;

                    tw.WriteLine("Version:0.9");
                    tw.WriteLine("StartHTML:00000000");
                    tw.WriteLine("EndHTML:00000000");
                    tw.WriteLine("StartFragment:00000000");
                    tw.WriteLine("EndFragment:00000000");
                    tw.Flush();

                    // Patch header
                    pos = ms.Position;
                    ms.Seek(INDEX_START_HTML, SeekOrigin.Begin);
                    ms.Write((bytes = Encoding.ASCII.GetBytes(ms.Length.ToString("D8"))), 0, bytes.Length);
                    ms.Seek(pos, SeekOrigin.Begin);

                    tw.WriteLine("<html>");
                    tw.WriteLine("<head>");
                    tw.WriteLine(@"<meta charset=""utf-8"" />");
                    tw.WriteLine(@"<title>ScintillaNET v{0}</title>", scintilla.GetType().Assembly.GetName().Version.ToString(3));
                    tw.WriteLine("</head>");
                    tw.WriteLine("<body>");
                    tw.Flush();

                    // Patch header
                    pos = ms.Position;
                    ms.Seek(INDEX_START_FRAGMENT, SeekOrigin.Begin);
                    ms.Write((bytes = Encoding.ASCII.GetBytes(ms.Length.ToString("D8"))), 0, bytes.Length);
                    ms.Seek(pos, SeekOrigin.Begin);
                    tw.WriteLine("<!--StartFragment -->");

                    // Write the styles.
                    // We're doing the style tag in the body to include it in the "fragment".
                    tw.WriteLine(@"<style type=""text/css"" scoped="""">");
                    tw.Write("div#segments {");
                    tw.Write(" float: left;");
                    tw.Write(" white-space: pre;");
                    tw.Write(" line-height: {0}px;", scintilla.DirectMessage(NativeMethods.SCI_TEXTHEIGHT, new IntPtr(0)).ToInt32());
                    tw.Write(" background-color: #{0:X2}{1:X2}{2:X2};", (styles[Style.Default].BackColor >> 0) & 0xFF, (styles[Style.Default].BackColor >> 8) & 0xFF, (styles[Style.Default].BackColor >> 16) & 0xFF);
                    tw.WriteLine(" }");

                    for (int i = 0; i < styles.Length; i++)
                    {
                        if (!styles[i].Used)
                            continue;

                        tw.Write("span.s{0} {{", i);
                        tw.Write(@" font-family: ""{0}"";", styles[i].FontName);
                        tw.Write(" font-size: {0}pt;", styles[i].SizeF);
                        tw.Write(" font-weight: {0};", styles[i].Weight);
                        if (styles[i].Italic != 0)
                            tw.Write(" font-style: italic;");
                        if (styles[i].Underline != 0)
                            tw.Write(" text-decoration: underline;");
                        tw.Write(" background-color: #{0:X2}{1:X2}{2:X2};", (styles[i].BackColor >> 0) & 0xFF, (styles[i].BackColor >> 8) & 0xFF, (styles[i].BackColor >> 16) & 0xFF);
                        tw.Write(" color: #{0:X2}{1:X2}{2:X2};", (styles[i].ForeColor >> 0) & 0xFF, (styles[i].ForeColor >> 8) & 0xFF, (styles[i].ForeColor >> 16) & 0xFF);
                        switch ((StyleCase)styles[i].Case)
                        {
                            case StyleCase.Upper:
                                tw.Write(" text-transform: uppercase;");
                                break;
                            case StyleCase.Lower:
                                tw.Write(" text-transform: lowercase;");
                                break;
                        }

                        if (styles[i].Visible == 0)
                            tw.Write(" visibility: hidden;");
                        tw.WriteLine(" }");
                    }

                    tw.WriteLine("</style>");
                    tw.Write(@"<div id=""segments""><span class=""s{0}"">", Style.Default);
                    tw.Flush();

                    var tabSize = scintilla.DirectMessage(NativeMethods.SCI_GETTABWIDTH).ToInt32();
                    var tab = new string(' ', tabSize);

                    tw.AutoFlush = true;
                    var lastStyle = Style.Default;
                    var unicodeLineEndings = ((scintilla.DirectMessage(NativeMethods.SCI_GETLINEENDTYPESACTIVE).ToInt32() & NativeMethods.SC_LINE_END_TYPE_UNICODE) > 0);
                    foreach (var seg in styledSegments)
                    {
                        var endOffset = seg.Offset + seg.Count;
                        for (int i = seg.Offset; i < endOffset; i += 2)
                        {
                            var ch = seg.Array[i];
                            var style = seg.Array[i + 1];

                            if (lastStyle != style)
                            {
                                tw.Write(@"</span><span class=""s{0}"">", style);
                                lastStyle = style;
                            }

                            switch (ch)
                            {
                                case (byte)'<':
                                    tw.Write("&lt;");
                                    break;

                                case (byte)'>':
                                    tw.Write("&gt;");
                                    break;

                                case (byte)'&':
                                    tw.Write("&amp;");
                                    break;

                                case (byte)'\t':
                                    tw.Write(tab);
                                    break;

                                case (byte)'\r':
                                    if (i + 2 < endOffset)
                                    {
                                        if (seg.Array[i + 2] == (byte)'\n')
                                            i += 2;
                                    }

                                    // Either way, this is a line break
                                    goto case (byte)'\n';

                                case 0xC2:
                                    if (unicodeLineEndings && i + 2 < endOffset)
                                    {
                                        if (seg.Array[i + 2] == 0x85) // NEL \u0085
                                        {
                                            i += 2;
                                            goto case (byte)'\n';
                                        }
                                    }

                                    // Not a Unicode line break
                                    goto default;

                                case 0xE2:
                                    if (unicodeLineEndings && i + 4 < endOffset)
                                    {
                                        if (seg.Array[i + 2] == 0x80 && seg.Array[i + 4] == 0xA8) // LS \u2028
                                        {
                                            i += 4;
                                            goto case (byte)'\n';
                                        }
                                        else if (seg.Array[i + 2] == 0x80 && seg.Array[i + 4] == 0xA9) // PS \u2029
                                        {
                                            i += 4;
                                            goto case (byte)'\n';
                                        }
                                    }

                                    // Not a Unicode line break
                                    goto default;

                                case (byte)'\n':
                                    // All your line breaks are belong to us
                                    tw.Write("\r\n");
                                    break;

                                default:

                                    if (ch == 0)
                                    {
                                        // Scintilla behavior is to allow control characters except for
                                        // NULL which will cause the Clipboard to truncate the string.
                                        tw.Write(" "); // Replace with space
                                        break;
                                    }

                                    ms.WriteByte(ch);
                                    break;
                            }
                        }
                    }

                    tw.AutoFlush = false;
                    tw.WriteLine("</span></div>");
                    tw.Flush();

                    // Patch header
                    pos = ms.Position;
                    ms.Seek(INDEX_END_FRAGMENT, SeekOrigin.Begin);
                    ms.Write((bytes = Encoding.ASCII.GetBytes(ms.Length.ToString("D8"))), 0, bytes.Length);
                    ms.Seek(pos, SeekOrigin.Begin);
                    tw.WriteLine("<!--EndFragment-->");

                    tw.WriteLine("</body>");
                    tw.WriteLine("</html>");
                    tw.Flush();

                    // Patch header
                    pos = ms.Position;
                    ms.Seek(INDEX_END_HTML, SeekOrigin.Begin);
                    ms.Write((bytes = Encoding.ASCII.GetBytes(ms.Length.ToString("D8"))), 0, bytes.Length);
                    ms.Seek(pos, SeekOrigin.Begin);

                    // Terminator
                    ms.WriteByte(0);

                    var str = GetString(ms.Pointer, (int)ms.Length, Encoding.UTF8);
                    if (NativeMethods.SetClipboardData(CF_HTML, ms.Pointer) != IntPtr.Zero)
                        ms.FreeOnDispose = false; // Clipboard will free memory
                }
            }
            catch (Exception ex)
            {
                // Yes, we swallow any exceptions. That may seem like code smell but this matches
                // the behavior of the Clipboard class, Windows Forms controls, and native Scintilla.
                Debug.Fail(ex.Message, ex.ToString());
            }
        }
Exemple #2
0
        private static unsafe void CopyHtml(Scintilla scintilla, StyleData[] styles, List <ArraySegment <byte> > styledSegments)
        {
            // NppExport -> NppExport.cpp
            // NppExport -> HTMLExporter.cpp
            // http://blogs.msdn.com/b/jmstall/archive/2007/01/21/html-clipboard.aspx
            // http://blogs.msdn.com/b/jmstall/archive/2007/01/21/sample-code-html-clipboard.aspx
            // https://msdn.microsoft.com/en-us/library/windows/desktop/ms649015.aspx

            try
            {
                long   pos = 0;
                byte[] bytes;

                // Write HTML
                using (var ms = new NativeMemoryStream(styledSegments.Sum(s => s.Count)))
                    using (var tw = new StreamWriter(ms, new UTF8Encoding(false)))
                    {
                        const int INDEX_START_HTML     = 23;
                        const int INDEX_START_FRAGMENT = 65;
                        const int INDEX_END_FRAGMENT   = 87;
                        const int INDEX_END_HTML       = 41;

                        tw.WriteLine("Version:0.9");
                        tw.WriteLine("StartHTML:00000000");
                        tw.WriteLine("EndHTML:00000000");
                        tw.WriteLine("StartFragment:00000000");
                        tw.WriteLine("EndFragment:00000000");
                        tw.Flush();

                        // Patch header
                        pos = ms.Position;
                        ms.Seek(INDEX_START_HTML, SeekOrigin.Begin);
                        ms.Write((bytes = Encoding.ASCII.GetBytes(ms.Length.ToString("D8"))), 0, bytes.Length);
                        ms.Seek(pos, SeekOrigin.Begin);

                        tw.WriteLine("<html>");
                        tw.WriteLine("<head>");
                        tw.WriteLine(@"<meta charset=""utf-8"" />");
                        tw.WriteLine(@"<title>ScintillaNET v{0}</title>", scintilla.GetType().Assembly.GetName().Version.ToString(3));
                        tw.WriteLine("</head>");
                        tw.WriteLine("<body>");
                        tw.Flush();

                        // Patch header
                        pos = ms.Position;
                        ms.Seek(INDEX_START_FRAGMENT, SeekOrigin.Begin);
                        ms.Write((bytes = Encoding.ASCII.GetBytes(ms.Length.ToString("D8"))), 0, bytes.Length);
                        ms.Seek(pos, SeekOrigin.Begin);
                        tw.WriteLine("<!--StartFragment -->");

                        // Write the styles.
                        // We're doing the style tag in the body to include it in the "fragment".
                        tw.WriteLine(@"<style type=""text/css"" scoped="""">");
                        tw.Write("div#segments {");
                        tw.Write(" float: left;");
                        tw.Write(" white-space: pre;");
                        tw.Write(" line-height: {0}px;", scintilla.DirectMessage(NativeMethods.SCI_TEXTHEIGHT, new IntPtr(0)).ToInt32());
                        tw.Write(" background-color: #{0:X2}{1:X2}{2:X2};", (styles[Style.Default].BackColor >> 0) & 0xFF, (styles[Style.Default].BackColor >> 8) & 0xFF, (styles[Style.Default].BackColor >> 16) & 0xFF);
                        tw.WriteLine(" }");

                        for (int i = 0; i < styles.Length; i++)
                        {
                            if (!styles[i].Used)
                            {
                                continue;
                            }

                            tw.Write("span.s{0} {{", i);
                            tw.Write(@" font-family: ""{0}"";", styles[i].FontName);
                            tw.Write(" font-size: {0}pt;", styles[i].SizeF);
                            tw.Write(" font-weight: {0};", styles[i].Weight);
                            if (styles[i].Italic != 0)
                            {
                                tw.Write(" font-style: italic;");
                            }
                            if (styles[i].Underline != 0)
                            {
                                tw.Write(" text-decoration: underline;");
                            }
                            tw.Write(" background-color: #{0:X2}{1:X2}{2:X2};", (styles[i].BackColor >> 0) & 0xFF, (styles[i].BackColor >> 8) & 0xFF, (styles[i].BackColor >> 16) & 0xFF);
                            tw.Write(" color: #{0:X2}{1:X2}{2:X2};", (styles[i].ForeColor >> 0) & 0xFF, (styles[i].ForeColor >> 8) & 0xFF, (styles[i].ForeColor >> 16) & 0xFF);
                            switch ((StyleCase)styles[i].Case)
                            {
                            case StyleCase.Upper:
                                tw.Write(" text-transform: uppercase;");
                                break;

                            case StyleCase.Lower:
                                tw.Write(" text-transform: lowercase;");
                                break;
                            }

                            if (styles[i].Visible == 0)
                            {
                                tw.Write(" visibility: hidden;");
                            }
                            tw.WriteLine(" }");
                        }

                        tw.WriteLine("</style>");
                        tw.Write(@"<div id=""segments""><span class=""s{0}"">", Style.Default);
                        tw.Flush();

                        var tabSize = scintilla.DirectMessage(NativeMethods.SCI_GETTABWIDTH).ToInt32();
                        var tab     = new string(' ', tabSize);

                        tw.AutoFlush = true;
                        var lastStyle          = Style.Default;
                        var unicodeLineEndings = ((scintilla.DirectMessage(NativeMethods.SCI_GETLINEENDTYPESACTIVE).ToInt32() & NativeMethods.SC_LINE_END_TYPE_UNICODE) > 0);
                        foreach (var seg in styledSegments)
                        {
                            var endOffset = seg.Offset + seg.Count;
                            for (int i = seg.Offset; i < endOffset; i += 2)
                            {
                                var ch    = seg.Array[i];
                                var style = seg.Array[i + 1];

                                if (lastStyle != style)
                                {
                                    tw.Write(@"</span><span class=""s{0}"">", style);
                                    lastStyle = style;
                                }

                                switch (ch)
                                {
                                case (byte)'<':
                                    tw.Write("&lt;");
                                    break;

                                case (byte)'>':
                                    tw.Write("&gt;");
                                    break;

                                case (byte)'&':
                                    tw.Write("&amp;");
                                    break;

                                case (byte)'\t':
                                    tw.Write(tab);
                                    break;

                                case (byte)'\r':
                                    if (i + 2 < endOffset)
                                    {
                                        if (seg.Array[i + 2] == (byte)'\n')
                                        {
                                            i += 2;
                                        }
                                    }

                                    // Either way, this is a line break
                                    goto case (byte)'\n';

                                case 0xC2:
                                    if (unicodeLineEndings && i + 2 < endOffset)
                                    {
                                        if (seg.Array[i + 2] == 0x85) // NEL \u0085
                                        {
                                            i += 2;
                                            goto case (byte)'\n';
                                        }
                                    }

                                    // Not a Unicode line break
                                    goto default;

                                case 0xE2:
                                    if (unicodeLineEndings && i + 4 < endOffset)
                                    {
                                        if (seg.Array[i + 2] == 0x80 && seg.Array[i + 4] == 0xA8) // LS \u2028
                                        {
                                            i += 4;
                                            goto case (byte)'\n';
                                        }
                                        else if (seg.Array[i + 2] == 0x80 && seg.Array[i + 4] == 0xA9) // PS \u2029
                                        {
                                            i += 4;
                                            goto case (byte)'\n';
                                        }
                                    }

                                    // Not a Unicode line break
                                    goto default;

                                case (byte)'\n':
                                    // All your line breaks are belong to us
                                    tw.Write("\r\n");
                                    break;

                                default:

                                    if (ch == 0)
                                    {
                                        // Scintilla behavior is to allow control characters except for
                                        // NULL which will cause the Clipboard to truncate the string.
                                        tw.Write(" "); // Replace with space
                                        break;
                                    }

                                    ms.WriteByte(ch);
                                    break;
                                }
                            }
                        }

                        tw.AutoFlush = false;
                        tw.WriteLine("</span></div>");
                        tw.Flush();

                        // Patch header
                        pos = ms.Position;
                        ms.Seek(INDEX_END_FRAGMENT, SeekOrigin.Begin);
                        ms.Write((bytes = Encoding.ASCII.GetBytes(ms.Length.ToString("D8"))), 0, bytes.Length);
                        ms.Seek(pos, SeekOrigin.Begin);
                        tw.WriteLine("<!--EndFragment-->");

                        tw.WriteLine("</body>");
                        tw.WriteLine("</html>");
                        tw.Flush();

                        // Patch header
                        pos = ms.Position;
                        ms.Seek(INDEX_END_HTML, SeekOrigin.Begin);
                        ms.Write((bytes = Encoding.ASCII.GetBytes(ms.Length.ToString("D8"))), 0, bytes.Length);
                        ms.Seek(pos, SeekOrigin.Begin);

                        // Terminator
                        ms.WriteByte(0);

                        var str = GetString(ms.Pointer, (int)ms.Length, Encoding.UTF8);
                        if (NativeMethods.SetClipboardData(CF_HTML, ms.Pointer) != IntPtr.Zero)
                        {
                            ms.FreeOnDispose = false; // Clipboard will free memory
                        }
                    }
            }
            catch (Exception ex)
            {
                // Yes, we swallow any exceptions. That may seem like code smell but this matches
                // the behavior of the Clipboard class, Windows Forms controls, and native Scintilla.
                Debug.Fail(ex.Message, ex.ToString());
            }
        }