/***************************************************************************
xterm256generator.cpp - description
-------------------
begin : Oct 13 2006
copyright : (C) 2006-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 <cmath>
#include <cstdio>
#include <cstdlib>
#include <sstream>
#include "xterm256generator.h"
#include "charcodes.h"
#include "version.h"
using std::ostringstream;
using std::string;
namespace highlight
{
Xterm256Generator::Xterm256Generator() :
CodeGenerator ( ESC_XTERM256 )
{
newLineTag = "\n";
spacer = initialSpacer = " ";
}
string Xterm256Generator::getHeader()
{
return string();
}
void Xterm256Generator::printBody()
{
processRootState();
}
string Xterm256Generator::getFooter()
{
return string();
}
string Xterm256Generator::maskCharacter ( unsigned char c )
{
switch ( c ) {
case 0x1b :
return "";
case 0x07 :
return "";
default:
return string ( 1, c );
}
}
void Xterm256Generator::initOutputTags ( )
{
if (canvasPadding > 0 ) {
ostringstream bgs;
Colour bg=docStyle.getBgColour();
unsigned char bg_rgb[3];
bg_rgb[0] = ( unsigned char ) strtoll ( bg.getRed ( HTML ).c_str(), nullptr, 16 );
bg_rgb[1] = ( unsigned char ) strtoll ( bg.getGreen ( HTML ).c_str(), nullptr, 16 );
bg_rgb[2] = ( unsigned char ) strtoll ( bg.getBlue ( HTML ).c_str(), nullptr, 16 );
if (use16mColours) {
//use 24bit true colour ("888" colours (aka 16 million))
bgs << "\033[48;2;"<< ( int ) bg_rgb[0] << ";" << ( int ) bg_rgb[1] << ";" << ( int ) bg_rgb[2] << "m";
} else {
int bgApprox=( int ) rgb2xterm ( bg_rgb );
if (!bgApprox) bgApprox=16; // workaround transparent 0 value
bgs << "\033[48;5;"<< bgApprox << "m";
}
canvasColSeq = bgs.str();
maskWsBegin = canvasColSeq;
}
openTags = {
getOpenTag ( docStyle.getDefaultStyle() ),
getOpenTag ( docStyle.getStringStyle() ),
getOpenTag ( docStyle.getNumberStyle() ),
getOpenTag ( docStyle.getSingleLineCommentStyle() ),
getOpenTag ( docStyle.getCommentStyle() ),
getOpenTag ( docStyle.getEscapeCharStyle() ),
getOpenTag ( docStyle.getPreProcessorStyle() ),
getOpenTag ( docStyle.getPreProcStringStyle() ),
getOpenTag ( docStyle.getLineStyle() ),
getOpenTag ( docStyle.getOperatorStyle() ),
getOpenTag ( docStyle.getInterpolationStyle() ),
getOpenTag ( docStyle.getErrorStyle() ),
getOpenTag ( docStyle.getErrorMessageStyle() )
};
closeTags.assign ( NUMBER_BUILTIN_STATES, "\033[m" );
guideTagOpen = getOpenTag ( docStyle.getIndentGuideStyle() );
guideTagClose = "\033[m";
}
string Xterm256Generator::getOpenTag ( const ElementStyle &col )
{
Colour c= col.getColour();
unsigned char rgb[3];
rgb[0] = ( unsigned char ) strtoll ( c.getRed ( HTML ).c_str(), nullptr, 16 );
rgb[1] = ( unsigned char ) strtoll ( c.getGreen ( HTML ).c_str(), nullptr, 16 );
rgb[2] = ( unsigned char ) strtoll ( c.getBlue ( HTML ).c_str(), nullptr, 16 );
ostringstream s;
s << canvasColSeq;
s << "\033[";
if ( col.isBold() ) s << "1;";
if ( col.isItalic() ) s << "3;";
if ( col.isUnderline() ) s << "4;";
if (use16mColours) {
//use 24bit true colour ("888" colours (aka 16 million))
s << "38;2;"<< ( int ) rgb[0] << ";" << ( int ) rgb[1] << ";" << ( int ) rgb[2] << "m";
} else {
// apply color approximation, 256 colour palette (216 colours + 16 ansi + 24 gray) (colors are 24bit)
s << "38;5;"<< ( int ) rgb2xterm ( rgb ) << "m";
}
return s.str();
}
string Xterm256Generator::getKeywordOpenTag ( unsigned int styleID )
{
return getOpenTag ( docStyle.getKeywordStyle ( currentSyntax->getKeywordClasses() [styleID] ) );
}
string Xterm256Generator::getKeywordCloseTag ( unsigned int styleID )
{
return "\033[m";
}
string Xterm256Generator::getNewLine()
{
ostringstream ss;
printSyntaxError(ss);
if (canvasPadding>0) {
unsigned int lastLineLength=getLastLineLength();
//adapt to long lines; avoid lag with very long lines
if (lastLineLength<512 && lastLineLength > canvasPadding && lastLineLength)
canvasPadding = lastLineLength;
ss << canvasColSeq;
if (canvasPadding > lastLineLength)
ss << string(canvasPadding - lastLineLength, ' ');
ss << "\033[m";
}
if (printNewLines)
ss << newLineTag;
return ss.str();
}
void Xterm256Generator::setESCTrueColor(bool b) {
use16mColours = b;
if (b) setOutputType(ESC_TRUECOLOR);
}
void Xterm256Generator::setESCCanvasPadding(unsigned int p) {
if (p<512) canvasPadding = p;
}
/* the following functions are based on Wolfgang Frischs xterm256 converter utility:
https://web.archive.org/web/20060815160015/http://www.frexx.de/xterm-256-notes/
*/
void Xterm256Generator::xterm2rgb ( unsigned char color, unsigned char* rgb )
{
// 16 basic colors
if ( color<16 ) {
rgb[0] = basic16[color][0];
rgb[1] = basic16[color][1];
rgb[2] = basic16[color][2];
}
// color cube color
if ( color>=16 && color<=232 ) {
color-=16;
rgb[0] = valuerange[ ( color/36 ) %6];
rgb[1] = valuerange[ ( color/6 ) %6];
rgb[2] = valuerange[color%6];
}
// gray tone
if ( color>=233 && color<=253 ) {
rgb[0]=rgb[1]=rgb[2] = 8+ ( color-232 ) *0x0a;
}
}
void Xterm256Generator::maketable()
{
unsigned char c, rgb[3] = {0};
for ( c=0; c<=253; c++ ) {
xterm2rgb ( c,rgb );
colortable[c][0] = rgb[0];
colortable[c][1] = rgb[1];
colortable[c][2] = rgb[2];
}
}
unsigned char Xterm256Generator::rgb2xterm ( unsigned char* rgb )
{
unsigned char c, best_match=0;
double d, smallest_distance;
if ( !initialized ) {
maketable();
initialized = true;
}
smallest_distance = 10000000000.0;
for ( c=0; c<=253; c++ ) {
d = pow ( colortable[c][0]-rgb[0],2.0 ) +
pow ( colortable[c][1]-rgb[1],2.0 ) +
pow ( colortable[c][2]-rgb[2],2.0 );
if ( d<smallest_distance ) {
smallest_distance = d;
best_match=c;
}
}
return best_match;
}
bool Xterm256Generator::initialized=false;
unsigned char Xterm256Generator::colortable[254][3] = { {0} };
const unsigned char Xterm256Generator::valuerange[] = { 0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF };
const unsigned char Xterm256Generator::basic16[16][3] = {
{ 0x00, 0x00, 0x00 }, // 0
{ 0xCD, 0x00, 0x00 }, // 1
{ 0x00, 0xCD, 0x00 }, // 2
{ 0xCD, 0xCD, 0x00 }, // 3
{ 0x00, 0x00, 0xEE }, // 4
{ 0xCD, 0x00, 0xCD }, // 5
{ 0x00, 0xCD, 0xCD }, // 6
{ 0xE5, 0xE5, 0xE5 }, // 7
{ 0x7F, 0x7F, 0x7F }, // 8
{ 0xFF, 0x00, 0x00 }, // 9
{ 0x00, 0xFF, 0x00 }, // 10
{ 0xFF, 0xFF, 0x00 }, // 11
{ 0x5C, 0x5C, 0xFF }, // 12
{ 0xFF, 0x00, 0xFF }, // 13
{ 0x00, 0xFF, 0xFF }, // 14
{ 0xFF, 0xFF, 0xFF } // 15
};
}