0

Is there a way in xsl to change text color to black or white depending on the background color brightness?

So in the example below, based on background color brightness both greetings should have white text. Is there a built in function in xsl to accomplish this?

xsl

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="html"/>

  <xsl:template match="greetings">
    <xsl:apply-templates select="greeting"/>
  </xsl:template>

  <xsl:template match="greeting">
    <html>
      <body>
        <h1>
          <span>
            <xsl:attribute name="style">
              background-color: <xsl:value-of select="@backcolor"/>
              color: <!-- Is there a way to make color black or white based on background color brightness? -->
            </xsl:attribute>
            <xsl:value-of select="."/>
          </span>
        </h1>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

xml

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="colortest.xslt"?> <!--todo: change this if copying to new file-->
<!--todo: change preceding line if copying to new file-->
<greetings>
  <greeting id="1" backcolor="f59595">
    Hello World!
  </greeting>
  <greeting id="2" backcolor="ff0000">
    Hola!
  </greeting>
</greetings>
Rod
  • 14,529
  • 31
  • 118
  • 230
  • Do you need to test the 'brightness' of the background color, and determine whether to select white or black text? What is the format of the `listItemColor` variable? – yas Apr 20 '17 at 18:11
  • That is correct, black or white depending on the brightness. listItemColor is probably going to be hex value. But if there's a way to make work with color name and hex, that'd be good too. But most likely hex. – Rod Apr 20 '17 at 18:28
  • Your question is not clear. Please provide an example (or two) of the input, and a clear set of rules for determining the desired output. – michael.hor257k Apr 20 '17 at 18:58
  • Are you trying to generate a stylesheet programmatically? Because that's what it looks like. But if that's what you were doing, I think you would have said so. As it is all we've got is (a) a problem description that talks about things like color and brightness rather than elements and attributes, and (b) a bit of code that (without any context) makes no sense at all. Show us the input and desired output of your transformation, and explain how they relate to each other. – Michael Kay Apr 20 '17 at 21:49
  • I apologize for confusion, I will come up with a working sample and update my original post. Again, please forgive me and stay with me on this. – Rod Apr 20 '17 at 21:59
  • Hi Rod, Thanks for the clarification. When you say "todo: change preceding line if copying to new file". I'm not perfectly clear about what you mean. But, if I could see the before XML and after XML, I think that would help me. However, I changed my answer to give my best guess at what you want. Let us know. – John Ernst Apr 21 '17 at 02:06
  • I think the problem you have is detecting what constitutes a "bright" colour from a 6-digit hex code. You probably need to take a step back from XSLT, and just try and come up with a generic algorithm for this, and then think about converting that into XSLT. There are a couple of answers on SO that might help. See http://stackoverflow.com/questions/1855884/determine-font-color-based-on-background-color or http://stackoverflow.com/questions/12043187/how-to-check-if-hex-color-is-too-black. – Tim C Apr 21 '17 at 07:12

2 Answers2

0
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xml" href="XSLTFile2.xslt"?>
<greetings>
  <greeting id="1" backcolor="f59595">
    Hello World!
  </greeting>
  <greeting id="2" backcolor="ff0000">
    Hola!
  </greeting>
</greetings>

Okay, if you want to use css to control your background color, use xml-stylesheet in your XML to point it to an XSLT file that does the following. This code will select the correct style sheet based on the listItemColor variable and replace the xml-stylesheet in your XML with the correct css stylesheet. (though I still don't know how listItemColor gets its value. Maybe you can tell us.)

Create two stylesheets, one for light and one for dark.

The following is the XSLTFile2.xslt file:

<xsl:variable name="listItemColor" select="'b'"/>

<xsl:template match="processing-instruction('xml-stylesheet')">
  <xsl:choose>
    <xsl:when test="listItemColor ='a'">
      <xsl:processing-instruction name="xml-stylesheet">
        <xsl:text>type="text/css" href="colortest.css"</xsl:text>
      </xsl:processing-instruction>
    </xsl:when>
    <xsl:otherwise>
      <xsl:processing-instruction name="xml-stylesheet">
        <xsl:text>type="text/css" href="colortest2.css"</xsl:text>
      </xsl:processing-instruction>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<!-- Do any other processing. -->

<!-- Identity template.-->
<xsl:template match="node()|@*">
  <xsl:copy>
    <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
</xsl:template>
John Ernst
  • 1,206
  • 1
  • 7
  • 11
  • Revised answer based on spec clarification. – John Ernst Apr 21 '17 at 02:07
  • So, the idea is for Rod to transform the XML to use a different style sheet. Then within that style sheet have different settings. Then when he opens the transformed XML in the browser, it has either light text or dark text. I changed my example to show this. But, Rod has to figure out the details. – John Ernst Apr 21 '17 at 16:10
0

You would have to work out the 'luminance' value of the RGB background colour and then take a threshold on that to determine if the foreground colour needs to be black or white (for best contrast). Unfortunately, XSLT (and XPath) don't provide any functions that deal with colours - and seeing as you're stuck with XSLT 1.0 (I'm assuming - because you're using it in browser?) things get a little verbose... but still possible...

Try something like this...

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"/>
<xsl:variable name="vHexDigits" select="'0123456789abcdef'"/>

<xsl:template match="greetings">
    <html>
        <body>
            <xsl:apply-templates select="greeting"/>
        </body>
    </html>
</xsl:template>

<xsl:template match="greeting">
    <xsl:variable name="vLuminance">
        <xsl:call-template name="RGB2Luminance">
            <xsl:with-param name="pRed" select="(string-length(substring-before($vHexDigits, substring(@backcolor,1,1))) * 16) + string-length(substring-before($vHexDigits, substring(@backcolor,2,1)))"/>
            <xsl:with-param name="pGreen" select="(string-length(substring-before($vHexDigits, substring(@backcolor,3,1))) * 16) + string-length(substring-before($vHexDigits, substring(@backcolor,4,1)))"/>
            <xsl:with-param name="pBlue" select="(string-length(substring-before($vHexDigits, substring(@backcolor,5,1))) * 16) + string-length(substring-before($vHexDigits, substring(@backcolor,6,1)))"/>
        </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="vForegroundColor">
        <xsl:choose>
            <!-- assume our black/white forground threshold is 0.5 -->
            <xsl:when test="$vLuminance &gt; 0.5">
                <xsl:text>black</xsl:text>
            </xsl:when>
            <xsl:otherwise>
                <xsl:text>white</xsl:text>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:variable>
    <h1>
        <span style="background-color: {@backcolor}; color: {$vForegroundColor};">
            <xsl:value-of select="."/>
        </span>
    </h1>
</xsl:template>

<xsl:template name="RGB2Luminance">
    <xsl:param name="pRed"/>
    <xsl:param name="pGreen"/>
    <xsl:param name="pBlue"/>
    <xsl:variable name="vR" select="$pRed div 255"/>
    <xsl:variable name="vG" select="$pGreen div 255"/>
    <xsl:variable name="vB" select="$pBlue div 255"/>
    <xsl:variable name="vMax">
        <xsl:choose>
            <xsl:when test="$vR &gt;= $vG and $vR &gt;= $vB">
                <xsl:value-of select="$vR"/>
            </xsl:when>
            <xsl:when test="$vG &gt;= $vR and $vG &gt;= $vB">
                <xsl:value-of select="$vG"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$vB"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:variable>
    <xsl:variable name="vMin">
        <xsl:choose>
            <xsl:when test="$vR &lt;= $vG and $vR &lt;= $vB">
                <xsl:value-of select="$vR"/>
            </xsl:when>
            <xsl:when test="$vG &lt;= $vR and $vG &lt;= $vB">
                <xsl:value-of select="$vG"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$vB"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:variable>
    <xsl:value-of select="($vMax + $vMin) div 2"/>
</xsl:template>
</xsl:stylesheet>
Marrow父
  • 357
  • 2
  • 6
  • What you are actually calculating here is not *luminance*, but *lightness* in the HSL color space. And neither *luminance* nor *lightness* are "*brightness*" - but that's a can of worms not worth opening here. – michael.hor257k Apr 21 '17 at 12:39
  • @michael.hor257k - indeed, I quickly hacked the code from an XSLT 2.0 function and forgot the 'L' in HSL stood for 'lightness'. Thx – Marrow父 Apr 24 '17 at 11:51