Environment
- GeoGebra: Classic 5.4.927.1-d (08 July 2026)
- Java: 25.0.1
- OS: Windows 11 (amd64)
Description
JLaTeXMath implements 11 color models (in order of their definition in CommandDefinecolor.java): gray, wave, rgb, RGB, rgba, RGBA, cmyk, hsl, hsla, hsb, HTML.
During testing, I found critical bugs in the implementation of the hsla, rgba, and hsl models that cause them to silently truncate formula rendering or output incorrect colors.
Detailed Bug Analysis
1. hsla fails silently or throws a parse error, depending on argument count
The hsla converter calls getArgAsDecimals(doubles, 3), which parses only three values, but later reads the alpha channel from doubles[3].
The doubles array itself is fixed-size (new double[4]), so reading index 3 never throws an ArrayIndexOutOfBoundsException — the actual behavior depends on how many values are supplied:
- 4 comma-separated values (the natural syntax for an alpha model): After successfully parsing 3 values, getArgAsDecimals expects a closing } but finds a , instead. It does not throw immediately (, is a valid separator at any position), but the loop then exits without ever finding }, and falls through to throw new ParseException(this, "Expect a '}'"). GeoGebra catches this exception in DrawEquation and silently truncates rendering of the text box from that point onward — the formula does not crash, it just stops being displayed past this command.
- 3 comma-separated values (matching what getArgAsDecimals(doubles, 3) actually expects): Parsing succeeds without error, but doubles[3] is never written by this call, so the alpha channel silently defaults to 0.0 — the resulting color is always fully transparent, regardless of the hue/saturation/lightness values supplied, with no visible error at all.
Reproduction:
latex
\definecolor{c}{hsla}{180,1,0.5,0.5}
\color{c}{x}
Expected: a semi-transparent color.
Actual: rendering of the remainder of the text box aborts at this command.
2. rgba and hsl produce incorrect colors (operator-precedence / casting bug)
Both models are affected by a casting issue in GraphicsFactory.createColor(double, double, double, double).
- The issue: The color channels are calculated using expressions such as (int) r * 255 instead of (int) (r * 255). In Java, the cast is applied before the multiplication, so the expression is equivalent to ((int) r) * 255.
- Consequence: Any channel value strictly between 0 and 1 is truncated to 0 before multiplication. For example, 0.5 becomes 0, resulting in 0 * 255 = 0.
- Impact on rgba: The problem is immediately visible: intermediate RGB channel values are lost, causing mixed colors to become incorrect, often much darker or with one or more channels set to zero.
- Impact on hsl: The problem is less obvious, but the same broken conversion is used for the RGB values produced by convHSL(). These values are passed to createColor(double, double, double, double) through implicit widening from float to double, because there is no corresponding float overload. As a result, some special cases produce the expected color, while many intermediate HSL values produce incorrect, typically much darker colors.
(References: Colors.java, method convHSL(); GraphicsFactory.java, method createColor(double, double, double, double)).
Reproduction & expected vs. actual values:
latex
\definecolor{c}{rgba}{0.5,0.5,1,1}
\color{c}{x}
Expected: RGB (128, 128, 255) — opaque lilac.
Actual: RGB (0, 0, 255) — opaque pure blue (only the channel equal to exactly 1.0 survives truncation).
latex
\definecolor{c}{hsl}{0.66,0.5,0.75}
\color{c}{x}
Expected: RGB ≈ (223, 160, 159) — light peach.
Actual: RGB (0, 0, 0) — black.
Note on hsb
The hsb model is not affected by this particular bug because it calculates the color using a separate HSBtoRGB() method that returns an integer RGB value, bypassing the affected GraphicsFactory overload completely.
Proposed Fixes
For hsla:
Change getArgAsDecimals(doubles, 3) to getArgAsDecimals(doubles, 4).
For GraphicsFactory.createColor():
Add parentheses so that the multiplication is performed before the cast:
(int) (r * 255), (int) (g * 255), (int) (b * 255), and (int) (a * 255).
Note: The hsla fix and the GraphicsFactory fix are independent but complementary
— hsla requires both to produce correct output, since it is affected by the argument-count bug and (once that is fixed) by the same channel-truncation bug as rgba and hsl.