gnuplot: How to realize multicolor labels?

Viewed 342

How can multicolor labels be realized in gnuplot? Apparently, the enhanced text modus offers parameters like font, fontsize, bold, italics, etc. but not colors. The following workaround is adapted from here.

It splits the label into as many labels as colors you have and uses the enhanced text function &{space} which inserts empty space of the length of space.

Are there maybe better ways?

Code:

### multicolor labels
reset session

Text1 = "One";   Color1 = "red"
Text2 = "Two";   Color2 = "green"
Text3 = "Three"; Color3 = "blue
Text4 = "Four";  Color4 = "magenta"
Text5 = "Five";  Color5 = "cyan"
Text6 = "Six";   Color6 = "yellow"

PosX=0
PosY=0
set label 1 Text1 at PosX,PosY tc rgb Color1
set label 2 sprintf("&{%s}%s",Text1,Text2) at PosX,PosY tc rgb Color2
set label 3 sprintf("&{%s%s}%s",Text1,Text2,Text3) at PosX,PosY tc rgb Color3
set label 4 sprintf("&{%s%s%s}%s",Text1,Text2,Text3,Text4) at PosX,PosY tc rgb Color4
set label 5 sprintf("\n%s",Text5) at PosX,PosY tc rgb Color5
set label 6 sprintf("\n&{%s}%s",Text5,Text6) at PosX,PosY tc rgb Color6

plot x
###end of code

Result: (actually, with set terminal wxt font ",20")

enter image description here

2 Answers

The only way to work with any terminal seems to be the one you've shown.

I don't know if it's a better way, but I'll show you how to do it the same way you did, but with the "labels" plotting style.

Code:

set terminal wxt font ",20"

$text <<EOD
"One"   0xff0000
"Two"   0x00ff00
"Three" 0x0000ff
"Four"  0xff00ff

"Five"  0x00ffff
"Six"   0xffff00
EOD

###########################################
# Rearrangement of data for "with labels"
###########################################
stat $text using 0 nooutput
set print $labels
line = "&{"
do for [i=1:STATS_records+STATS_blank] {
  if ( words($text[i]) == 0 ) {
    string = "\\n"
    color  = "black"
    cooked = line . "}" . string
    line   = cooked . "&{" 
  } else {
    string = word($text[i],1)
    color  = word($text[i],2)
    cooked = line . "}" . string
    line   = line . string
  }
  print sprintf('"%s" %s', cooked, color)
}
set print
###########################################

set xrange [-10:10]
set yrange [-10:10]

plot x,\
     $labels using (0):(0):1:2 with labels tc rgb variable left enhanced title ""

pause -1

Each line of the data block $text represents a string and a color (an integer for the color). A blank line represents a line break. From this data, we reconstruct the string data using "&{string}" and pass it to "with labels".


If you allow the use of the external command 'sed', you can use the following trickey method, but only in the svg terminal.

Code:

# This filter unescapes '<tspan', '</tspan' in svg output
filter = "| sed -e 's/&lt;tspan/<tspan/g' -e 's/&lt;\\/tspan/<\\/tspan/g' > "

set terminal svg font ",20"
set output filter . "test.svg"

labeltext = "<tspan fill='red' >One</tspan>" . \
            "<tspan fill='green'>Two</tspan>" . \
            "<tspan fill='blue'>Three</tspan>" . \
            "<tspan fill='magenta'>Four</tspan>" . \
            "\n" . \
            "<tspan fill='cyan'>Five</tspan>" . \
            "<tspan fill='yellow'>Six</tspan>" 

set label labeltext at 0,0 

plot x

pause -1

In this example, the label text is embedded with a SVG tags. Gnuplot escapes '<' to '&lt;' when outputting the SVG, so we use the "sed" filter to unescape it.

Just for fun, here is another "automated" approach. You can set the colors by {/:<color> <your text>}, similar as in the enhanced text mode.

I know that gnuplot is not ideal for parsing strings and the following procedure is not robust for all cases, e.g. for colored text over two lines you have to set the color twice. Maybe the whole procedure can be packed into functions and macros. Well, it does what is expected but it is not very handy. Simplifications and improvements are welcome.

Code:

### multicolor label
reset session
 
myLabel = "This is {/:red red}, this is {/:green green}, \nand this is {/:blue blue}.\nHere is {/:magenta magenta}\n{/:magenta over two} lines."

# splitting the label
s = myLabel
myLabelSplit = ''
ColorDefault = 'black'
myColor(i) = word(myLabelSplit,i*2+1)
myText(i) = word(myLabelSplit,i*2+2)

do for [i=1:strlen(myLabel)] {
    c = strstrt(s,'{/:') # color
    n = strstrt(s,"\n")  # newline
    if (c==0 && n==0) {
        myLabelSplit = myLabelSplit.sprintf('"%s" "%s" "%s" "%s" ', ColorDefault, "", ColorDefault, s)   
        break
    }    # stop if no color or newline is found
    if ((c<n && c>0) || n==0) {
        text = s[:c-1]
        color = word(s[c+3:],1)
        ctext = s[strlen(text)+3+strlen(color)+2:strstrt(s,'}')-1]
        s = s[strlen(text)+3+strlen(color)+strlen(ctext)+3:]
    }
    else {
        text = s[:n-1]
        color = ColorDefault
        ctext = "\n"
        s = s[strlen(text)+2:]
    }
    myLabelSplit = myLabelSplit.sprintf('"%s" "%s" "%s" "%s" ', ColorDefault, text, color, ctext)   
    print myLabelSplit
}
 
# create the labels
PosX = -8
PosY = 8
space = ''
format = '&{%s}%s'
do for [i=0:words(myLabelSplit)/2-1] {
    if (myText(i) eq "\n") {
        format = "\n".format
        space = ''
    } 
    set label sprintf(format,space,myText(i)) at PosX, PosY tc rgb myColor(i) font ",16"
    if (myText(i) ne "\n") { 
        space = space.myText(i)
    }
}

plot x
### end of code

Result:

enter image description here

Related