current commit
260809.1.4 · https://gitlab.com/saalen/highlight/-/work_items/240 emit the IndentGuide style and colour only when guides are enabled
/***************************************************************************
                    odtgenerator.cpp  -  description
                             -------------------
    begin                : Jul 19 2012
    copyright            : (C) 2012-2026 by Andre Simon
    email                : a.simon@mailbox.org
 ***************************************************************************/


/*
This file is part of Highlight.

Highlight is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Highlight is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Highlight.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <sstream>
#include "odtgenerator.h"

using std::ostringstream;
using std::string;

namespace highlight
{

ODTGenerator::ODTGenerator() : CodeGenerator ( ODTFLAT )
{
    newLineTag = "</text:p>\n<text:p text:style-name=\"Standard\">";
    spacer = initialSpacer = "<text:s text:c=\"1\"/>";
    maskWs=true;
    if ( !preFormatter.getReplaceTabs()) { // TODO use <text:tab/>

        preFormatter.setReplaceTabs ( true );
        preFormatter.setNumberSpaces ( 4 );
    }
}

string ODTGenerator::getStyleDefinition()
{
    if ( disableStyleCache || styleDefinitionCache.empty() ) {
        ostringstream os;
        bool fontSizeDigits=getBaseFontSize().find_first_not_of("0123456789.")==string::npos;

        os  << "<office:styles>\n<style:style style:name=\"Standard\" style:family=\"paragraph\" style:class=\"text\">\n"
            << "  <style:text-properties style:font-name=\"" << getBaseFont()
            <<"\" fo:font-size=\"" << getBaseFontSize() << ((fontSizeDigits) ? "pt" : "") << "\"/>"
            << "</style:style>\n</office:styles>"
            << "<office:automatic-styles>\n";

        os << getAttributes ( STY_NAME_STD, docStyle.getDefaultStyle() )
           << getAttributes ( STY_NAME_NUM, docStyle.getNumberStyle() )
           << getAttributes ( STY_NAME_ESC, docStyle.getEscapeCharStyle() )
           << getAttributes ( STY_NAME_STR, docStyle.getStringStyle() )
           << getAttributes ( STY_NAME_DST, docStyle.getPreProcStringStyle() )
           << getAttributes ( STY_NAME_SLC, docStyle.getSingleLineCommentStyle() )
           << getAttributes ( STY_NAME_COM, docStyle.getCommentStyle() )
           << getAttributes ( STY_NAME_DIR, docStyle.getPreProcessorStyle() )
           << getAttributes ( STY_NAME_SYM, docStyle.getOperatorStyle() )
           << getAttributes ( STY_NAME_IPL, docStyle.getInterpolationStyle() )
           << getAttributes ( STY_NAME_LIN, docStyle.getLineStyle() )
           << getAttributes ( STY_NAME_ERR, docStyle.getErrorStyle() )
           << getAttributes ( STY_NAME_ERM, docStyle.getErrorMessageStyle() );

        if ( showIndentGuides ) {
            os << getAttributes ( STY_NAME_GUI, docStyle.getIndentGuideStyle() );
        }

        for ( const auto &[first, second] : docStyle.getKeywordStyles() ) {
            os << getAttributes ( first, second );
        }

        os << "<style:page-layout style:name=\"pm1\">"
           << R"(<style:page-layout-properties fo:page-width="21.001cm" fo:page-height="29.7cm" style:num-format="1")"
           << R"( style:print-orientation="portrait" fo:margin="2cm" fo:margin-top="2cm" fo:margin-bottom="2cm" fo:margin-left="2cm" fo:margin-right="2cm" )"
           << "fo:background-color=\"#"
           << ( docStyle.getBgColour().getRed ( HTML ) )
           << ( docStyle.getBgColour().getGreen ( HTML ) )
           << ( docStyle.getBgColour().getBlue ( HTML ) )
           << "\" style:writing-mode=\"lr-tb\" style:footnote-max-height=\"0cm\">\n"
           << " <style:background-image/>\n"
           << R"( <style:footnote-sep style:width="0.018cm" style:distance-before-sep="0.101cm" style:distance-after-sep="0.101cm" style:line-style="solid")"
           << R"( style:adjustment="left" style:rel-width="25%" style:color="#000000"/>)"
           << "</style:page-layout-properties>\n"
           << "<style:header-style/>\n"
           << "<style:footer-style/>\n"
           << "</style:page-layout>\n"
           << "</office:automatic-styles>\n"
           << "<office:master-styles>\n"
           << " <style:master-page style:name=\"Standard\" style:page-layout-name=\"pm1\"/>\n"
           << "</office:master-styles>\n";

        styleDefinitionCache=os.str();
    }
    return styleDefinitionCache;
}

string ODTGenerator::getAttributes ( const string & elemName,
                                     const ElementStyle & elem )
{
    ostringstream s;
    s << "<style:style style:name=\""<<elemName<<"\" style:family=\"text\">\n"
      << "<style:text-properties fo:color=\"#"
      << ( elem.getColour().getRed ( HTML ) )
      << ( elem.getColour().getGreen ( HTML ) )
      << ( elem.getColour().getBlue ( HTML ) )
      << "\""
      << ( elem.isBold() ?     " fo:font-weight=\"bold\"" :"" )
      << ( elem.isItalic() ?   " fo:font-style=\"italic\"" :"" )
      << ( elem.isUnderline() ? R"( style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color")" :"" )
      <<"/>\n</style:style>\n" ;

    return s.str();
}

string ODTGenerator::getHeader()
{
      string enc = "UTF-8";
     if ( encodingDefined() ) {
        enc = encoding;
    }
    ostringstream header;
    header << R"(<?xml version="1.0" encoding=")"<<enc<<"\"?>\n"\
           "<office:document xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\" xmlns:style=\"urn:oasis:names:tc:opendocument:xmlns:style:1.0\""\
           " xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\" xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\""\
           " xmlns:draw=\"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0\" xmlns:fo=\"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0\""\
           " xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\""\
           " xmlns:number=\"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0\" xmlns:svg=\"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0\""\
           " xmlns:chart=\"urn:oasis:names:tc:opendocument:xmlns:chart:1.0\" xmlns:dr3d=\"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0\""\
           " xmlns:math=\"http://www.w3.org/1998/Math/MathML\" xmlns:form=\"urn:oasis:names:tc:opendocument:xmlns:form:1.0\" xmlns:script=\"urn:oasis:names:tc:opendocument:xmlns:script:1.0\""\
           " xmlns:config=\"urn:oasis:names:tc:opendocument:xmlns:config:1.0\" xmlns:ooo=\"http://openoffice.org/2004/office\" xmlns:ooow=\"http://openoffice.org/2004/writer\""\
           " xmlns:oooc=\"http://openoffice.org/2004/calc\" xmlns:dom=\"http://www.w3.org/2001/xml-events\" xmlns:xforms=\"http://www.w3.org/2002/xforms\""\
           " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:rpt=\"http://openoffice.org/2005/report\""\
           " xmlns:of=\"urn:oasis:names:tc:opendocument:xmlns:of:1.2\" xmlns:xhtml=\"http://www.w3.org/1999/xhtml\" xmlns:grddl=\"http://www.w3.org/2003/g/data-view#\""\
           " xmlns:tableooo=\"http://openoffice.org/2009/table\" xmlns:field=\"urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0\""\
           " xmlns:formx=\"urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0\" xmlns:css3t=\"http://www.w3.org/TR/css3-text/\" office:version=\"1.2\""\
           " office:mimetype=\"application/vnd.oasis.opendocument.text\">\n"\
           " <office:font-face-decls>\n"\
           "  <style:font-face style:name=\""<< getBaseFont()<<"\" svg:font-family=\""<< getBaseFont()<<"\" style:font-family-generic=\"modern\" style:font-pitch=\"fixed\"/>\n"\
           " </office:font-face-decls>\n";

    header << getStyleDefinition();

    header  << " <office:body>\n"
            << "  <office:text>\n"
            << "<text:p text:style-name=\"Standard\">";

    return header.str();
}

void ODTGenerator::printBody()
{
    processRootState();
}

string ODTGenerator::getFooter()
{
    return "</text:p>\n  </office:text>\n </office:body>\n</office:document>";
}

string  ODTGenerator::getOpenTag ( const string& styleName )
{
    return "<text:span text:style-name=\""+styleName+"\">";
}

void ODTGenerator::initOutputTags ()
{
    openTags = {
        "",
        getOpenTag ( STY_NAME_STR ),
        getOpenTag ( STY_NAME_NUM ),
        getOpenTag ( STY_NAME_SLC ),
        getOpenTag ( STY_NAME_COM ),
        getOpenTag ( STY_NAME_ESC ),
        getOpenTag ( STY_NAME_DIR ),
        getOpenTag ( STY_NAME_DST ),
        getOpenTag ( STY_NAME_LIN ),
        getOpenTag ( STY_NAME_SYM ),
        getOpenTag ( STY_NAME_IPL ),
        getOpenTag ( STY_NAME_ERR ),
        getOpenTag ( STY_NAME_ERM )
    };

    closeTags.assign ( NUMBER_BUILTIN_STATES, "</text:span>" );
    closeTags[0] = "";

    guideTagOpen = getOpenTag ( STY_NAME_GUI );
    guideTagClose = "</text:span>";
}

string ODTGenerator::maskCharacter ( unsigned char c )
{
    switch ( c ) {
    case '<' :
        return "&lt;";
    case '>' :
        return "&gt;";
    case '&' :
        return "&amp;";
    case '\"' :
        return "&quot;";
    case ' ':
        return spacer;
    //	case '\t':
    //		return "<text:tab/>";
    //		break;
    default:
        return string ( 1, c );
    }
}

string ODTGenerator::getKeywordOpenTag ( unsigned int styleID )
{
    return getOpenTag ( currentSyntax->getKeywordClasses() [styleID] );
}

string ODTGenerator::getKeywordCloseTag ( unsigned int styleID )
{
    return "</text:span>";
}

}