asciidoctor / asciimath

Asciimath parser
MIT License
24 stars 16 forks source link

Standardize color descriptors across renderers #34

Closed GarkGarcia closed 4 years ago

GarkGarcia commented 4 years ago

A follow up to https://github.com/asciidoctor/asciimath/pull/33#issuecomment-624697116.

My idea is to create a standard for valid color descriptors, which would be used by all renderers. Invalid color descriptors could then be detected at the parsing level. Also, that would allow for greater flexibility at the rendering level, since each renderer would be able to translate those standard color descriptors accordingly.

That would mean the expression color x y would not be represented as a binary operation, bu rather as a special operator whose first argument is always a valid color descriptor.

pepijnve commented 4 years ago

To get us started, here's what MathML supports:

color
# wrapped for display
 =  xsd:string {
  pattern = '\s*((#[0-9a-fA-F]{3}([0-9a-fA-F]{3})?)|[aA][qQ][uU][aA]|[bB][lL][aA][cC][kK]|
         [bB][lL][uU][eE]|[fF][uU][cC][hH][sS][iI][aA]|[gG][rR][aA][yY]|[gG][rR][eE][eE][nN]|
         [lL][iI][mM][eE]|[mM][aA][rR][oO][oO][nN]|[nN][aA][vV][yY]|[oO][lL][iI][vV][eE]|[pP][uU][rR][pP][lL][eE]|
         [rR][eE][dD]|[sS][iI][lL][vV][eE][rR]|[tT][eE][aA][lL]|[wW][hH][iI][tT][eE]|[yY][eE][lL][lL][oO][wW])\s*'}

I.e. hex RGB values or a handful of case-insensitive color names, aqua, black, blue, fuchsia, gray, green, lime, maroon, navy, olive, purple, red, silver, teal, white, and yellow.

GarkGarcia commented 4 years ago

To get us started, here's what MathML supports:

color
# wrapped for display
 =  xsd:string {
  pattern = '\s*((#[0-9a-fA-F]{3}([0-9a-fA-F]{3})?)|[aA][qQ][uU][aA]|[bB][lL][aA][cC][kK]|
         [bB][lL][uU][eE]|[fF][uU][cC][hH][sS][iI][aA]|[gG][rR][aA][yY]|[gG][rR][eE][eE][nN]|
         [lL][iI][mM][eE]|[mM][aA][rR][oO][oO][nN]|[nN][aA][vV][yY]|[oO][lL][iI][vV][eE]|[pP][uU][rR][pP][lL][eE]|
         [rR][eE][dD]|[sS][iI][lL][vV][eE][rR]|[tT][eE][aA][lL]|[wW][hH][iI][tT][eE]|[yY][eE][lL][lL][oO][wW])\s*'}

I.e. hex RGB values or a handful of case-insensitive color names, aqua, black, blue, fuchsia, gray, green, lime, maroon, navy, olive, purple, red, silver, teal, white, and yellow.

This is a pretty good starting point. I'm pretty sure LaTeX supports that too.

Do have any ideas on how we could represent the color spacial operator in the AST?

GarkGarcia commented 4 years ago

I was thinking about something like the following:

class Colorized < ValueNode
    attr_reader :descriptor

    def initialize value, color_descriptor
        super
        @descriptor = color_descriptor
    end
end
pepijnve commented 4 years ago

What would that node represent? Is value the colored expression or the color itself?

GarkGarcia commented 4 years ago

What would that node represent? Is value the colored expression or the color itself?

Both! If c is an instance of Colorized, c.descriptor represents the color of the node and c.value represents the expression associated with it.

So Colorizedessentially represents here's an expression that should be colored this way. In the terms of the previous example, c represents render c.value with the color c.descriptor.

GarkGarcia commented 4 years ago

Also, Colored may be a better name for the class. The naming convention is not set in stone of course.

GarkGarcia commented 4 years ago

Perhaps this naming-convention is more intuitive:

class Colored < ValueNode
  attr_reader :color

  def initialize expression, color
    super expression
    @color = color
  end

  def to_s
    'color(#{@color})(#{@value})'
  end

  def expression
    @value
  end
end
pepijnve commented 4 years ago

The intent of the ValueNode was to be a wrapper around a single value, be it a number, symbol, text, identifier, etc. So Colored would be an InnerNode rather than a ValueNode.

That aside, I'm not sure this would make sense. I can see the usefulness of having a distinct representation for colours themselves (for instance to add an rgb method), but I think we can leave the color operator as just a binary op node like it is today. Otherwise we would have to make special nodes for, for instance, font application as well.

GarkGarcia commented 4 years ago

That aside, I'm not sure this would make sense. I can see the usefulness of having a distinct representation for colours themselves (for instance to add an rgb method), but I think we can leave the color operator as just a binary op node like it is today. Otherwise we would have to make special nodes for, for instance, font application as well.

Fair enough. How about this representation for the colours?

class Color
  attr_reader :red
  attr_reader :green
  attr_reader :blue

  def initialize r, g, b
    @red = r
    @green = g
    @blue = b
  end

  def to_a
    [@red, @green, @blue]
  end

  def to_i
    @red * 0x10000 + @green * 0x100 + @blue
  end
end

Notice that colours that have a name (such as red) would still be represented in terms of their RGB values. This ensures that every "named-color" can be expressed appropriately by each renderer, even when the color does not have a name in the output language.

GarkGarcia commented 4 years ago

I hope I could make myself understood.

pepijnve commented 4 years ago

That's exactly what I had in mind. Make the color available as something more abstract than a name or hexstring so that the color itself is unambiguously defined and each backend can render it as appropriate.

pepijnve commented 4 years ago

For reference, the RGB values for the HTML color names are specified in https://www.w3.org/TR/html401/types.html#h-6.5

GarkGarcia commented 4 years ago

The predefined colours of xcolor are detailed in here. Also, this are the ones specified in CSS.

This may also be relevant.

GarkGarcia commented 4 years ago

@pepijnve Do you know which scheme is used by asciimathml?

GarkGarcia commented 4 years ago

In my opinion, the standard color sets specified in CSS and xcolor are both a bit bloated. The HTML standard color set is the more appropriate one, but it still has some unnecessary colours (such as Navy and Olive) and it lacks some important colours (such as Orange).

We should probably include all basic RGB colours (Red, Green, Blue, Cyan, Magenta, Yellow, White and Black), as well as some commonly used ones (such as Orange).

pepijnve commented 4 years ago

MathML reuses the HTML4 ones https://www.w3.org/TR/MathML/mathml.html#chapter2_fund.color

GarkGarcia commented 4 years ago

We should probably consider a more human-oriented set of primitive colours too, like this.

I think most users wouldn't be thinking in terms of RGB values when writing equations. I they wish to describe a colour in RGB, they can simply use a hexadecimal color-code

pepijnve commented 4 years ago

As with the other changes, I'm inclined to stick with what asciimath.js does by default and allow customisation. What that means is making another lookup table from color names to rgb values using the HTML4 colors as default. People can then insert whatever color table they want to use if the defaults are not useful.

GarkGarcia commented 4 years ago

As with the other changes, I'm inclined to stick with what asciimath.js does by default and allow customisation. What that means is making another lookup table from color names to rgb values using the HTML4 colors as default. People can then insert whatever color table they want to use if the defaults are not useful.

Fair enough. Makes sense to me.

GarkGarcia commented 4 years ago

I guess the color tables would have to be (optionally) passed to MarkupBuilder::initialize then?

@pepijnve Could you work on the relevant changes? I'm not familiarized enough with the parsing routines to write this sort of thing.

GarkGarcia commented 4 years ago

I'll work on the changes in LatexBuilder, of course.

pepijnve commented 4 years ago

@GarkGarcia I took a first stab at this. The LaTeX builder now generates \color[RGB]{r,g,b} to make sure we're always specifying the exact RGB value.

GarkGarcia commented 4 years ago

The LaTeX builder now generates \color[RGB]{r,g,b} to make sure we're always specifying the exact RGB value.

Great! We should probably output the name of the color whenever possible. That would make the output more readable. I'll work on a PR for it.