修改Notepad2的导出HTML功能


文章中经常要贴代码,一般我都是用Notepad2的复制为HTML功能,因为能保持语法高亮,之前贴小段的代码几乎看不出什么问题,但是贴的代码长一些的时候,问题就暴露出来了.第一点是我需要将代码放在pre标签里面,默认生成的是code标签,每次都要手动改成pre,其次默认实现中换行是br表示的,换成pre之后,换行是多余的,还有默认的实现经常会在一些莫名其妙的地方出现缩进的问题.

其他的编辑器也有此功能,例如editplus,但是没有任何字体信息,只有颜色,Notepad2中是保留了字体和字号的.要实现需要的功能,只有修改代码,如下:

void CDocment::OnExportHTMLPRE()
{
    size_t nStart SendMessage(sci::GETSELECTIONSTART);
    size_t nEnd SendMessage(sci::GETSELECTIONEND);
    if (nStart == nEnd)
        return;

    int sbit SendMessage(sci::GETSTYLEBITS);
    ASSERT(sbit >= && sbit sizeof(TBYTE) * 8);
    TBYTE mark = -1;
    mark >>= (sizeof(TCHAR) * sbit);

    util::memory<STYLEPAIRmem(nEnd nStart 10);
    TextRange tr;
    tr.chrg.cpMin nStart;
    tr.chrg.cpMax nEnd;
    tr.lpstrText = (TCHAR *)mem.ptr();
    SendMessage(sci::GETSTYLEDTEXT0, &tr);

    STYLEPAIR p1, * p2;
    p1 p2 mem.ptr();
    STYLE style = { sizeof(STYLE) };
    GetCurStyle(style, (int)(p2->StyleID mark));
    //start
    tstring text;
    text.reserve(nEnd nStart 10);
    text _T("<pre>");
    text += sci::MakeHTMLFontString(style);// <span style="">
    bool bOpenTag true;
    bool afterNewline false;
    bool forceSwitch false;

    while (p2->Char)
    {
        if (' ' == p2->Char)
        {
            text += _T("&nbsp;");
        }
        else if ('\t' == p2->Char)
        {
            for (int i=0i<pApp->d_iTabWidthi++)
                text += _T("&nbsp;");
        }
        else if ('\r' == p2->Char || '\n' == p2->Char)
        {

            if(bOpenTag )
            {
                text += _T("</span>");
                bOpenTag false;
                forceSwitch true;
            }
            text += _T("\r\n"); afterNewline true;
            if ( ('\r' == (p2+1)->Char || '\n' == (p2+1)->Char) && (p2+1)->Char != p2->Char ) {
                    p2++;
            }
        }
        else
        {
            if forceSwitch || (p1->StyleID mark) != (p2->StyleID mark))
            {    //style changed
                STYLE tmp_style = { sizeof(STYLE) };
                GetCurStyle(tmp_style, (int)(p2->StyleID mark));
                // tmp_style是p2的style style是?
                if (forceSwitch || !!memcmp(&tmp_style, &stylesizeof(STYLE)))
                {
                    memcpy(&style, &tmp_stylesizeof(STYLE));
                    if (bOpenTag) {
                        text += _T("</span>");
                        bOpenTag false;
                    }
                    text += sci::MakeHTMLFontString(style); //new style
                    forceSwitch false;
                    bOpenTag true;
                }
                if ((p1->StyleID mark) != (p2->StyleID mark)) p1 p2;
            }
            if ('<' == p2->Chartext += _T("&lt;");
            else if ('>' == p2->Chartext += _T("&gt;");
            else if ('\"' == p2->Chartext += _T("&quot;");
            else if ('&' == p2->Chartext += _T("&amp;");
            else text += p2->Char;
            
        }
        p2++;
    }
    if (bOpenTag)
        text += _T("</span>");
    text += _T("</pre>\r\n");
    CopyToClipboard(text.c_str(), text.length());
}

然后给右键菜单添加一个项目.

之前代码的问题在于,他会将多行的相同style的文本放在同一个span里面,例如多行注释,而主流的浏览器对span中的空格和span外的空格的解释是不同的,同样的缩进量,位于span标签中的时候要短一些.上述修改的作用是强制每一个行都至少用一个span标签来表示,这样所有行的缩进都会在span之外.原来的代码只会在style切换的时候才输出span标签,通过使用一个forceSwitch,每次遇到换行符的时候就设置为true,这会导致输出一个span前导标签.修改之后完全解决了缩进的问题.

附录:Notepad2的默认实现

void CDocment::OnExportHTML()
{
    size_t nStart SendMessage(sci::GETSELECTIONSTART);
    size_t nEnd SendMessage(sci::GETSELECTIONEND);
    if (nStart == nEnd)
        return;

    int sbit SendMessage(sci::GETSTYLEBITS);
    ASSERT(sbit >= && sbit sizeof(TBYTE) * 8);
    TBYTE mark = -1;
    mark >>= (sizeof(TCHAR) * sbit);

    util::memory<STYLEPAIRmem(nEnd nStart 10);
    TextRange tr;
    tr.chrg.cpMin nStart;
    tr.chrg.cpMax nEnd;
    tr.lpstrText = (TCHAR *)mem.ptr();
    SendMessage(sci::GETSTYLEDTEXT0, &tr);

    STYLEPAIR p1, * p2;
    p1 p2 mem.ptr();
    STYLE style = { sizeof(STYLE) };
    GetCurStyle(style, (int)(p2->StyleID mark));
    //start
    tstring text;
    text.reserve(nEnd nStart 10);
    text _T("<code>");
    text += sci::MakeHTMLFontString(style);
    bool bOpenTag true;

    while (p2->Char)
    {
        if (' ' == p2->Char)
        {
            text += _T("&nbsp;");
        }
        else if ('\t' == p2->Char)
        {
            for (int i=0i<pApp->d_iTabWidthi++)
                text += _T("&nbsp;");
        }
        else if ('\r' == p2->Char || '\n' == p2->Char)
        {

            text += _T("<br />\r\n");
            if ( ('\r' == (p2+1)->Char || '\n' == (p2+1)->Char) &&
                (p2+1)->Char != p2->Char ) {
                    p2++;
            }
        }
        else
        {
            if ((p1->StyleID mark) != (p2->StyleID mark))
            {    //style changed
                STYLE tmp_style = { sizeof(STYLE) };
                GetCurStyle(tmp_style, (int)(p2->StyleID mark));
                if (!!memcmp(&tmp_style, &stylesizeof(STYLE)))
                {
                    memcpy(&style, &tmp_stylesizeof(STYLE));
                    if (bOpenTag) {
                        text += _T("</span>");
                        bOpenTag false;
                    }
                    text += sci::MakeHTMLFontString(style); //new style
                    bOpenTag true;
                }
                p1 p2;
            }
            if ('<' == p2->Chartext += _T("&lt;");
            else if ('>' == p2->Chartext += _T("&gt;");
            else if ('\"' == p2->Chartext += _T("&quot;");
            else if ('&' == p2->Chartext += _T("&amp;");
            else text += p2->Char;
        }
        p2++;
    }
    if (bOpenTag)
        text += _T("</span>");
    text += _T("</code>\r\n");
    CopyToClipboard(text.c_str(), text.length());
}