1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
# -*- coding: utf-8 -*- #
# frozen_string_literal: true
module Rouge
class TexThemeRenderer
def initialize(theme, opts={})
@theme = theme
@prefix = opts.fetch(:prefix) { 'RG' }
end
# Our general strategy is this:
#
# * First, define the \RG{tokname}{content} command, which will
# expand into \RG@tok@tokname{content}. We use \csname...\endcsname
# to interpolate into a command.
#
# * Define the default RG* environment, which will enclose the whole
# thing. By default this will simply set \ttfamily (select monospace font)
# but it can be overridden with \renewcommand by the user to be
# any other formatting.
#
# * Define all the colors using xcolors \definecolor command. First we define
# every palette color with a name such as RG@palette@themneame@colorname.
# Then we find all foreground and background colors that have literal html
# colors embedded in them and define them with names such as
# RG@palette@themename@000000. While html allows three-letter colors such
# as #FFF, xcolor requires all six characters to be present, so we make sure
# to normalize that as well as the case convention in #inline_name.
#
# * Define the token commands RG@tok@xx. These will take the content as the
# argument and format it according to the theme, referring to the color
# in the palette.
def render(&b)
yield <<'END'.gsub('RG', @prefix)
\makeatletter
\def\RG#1#2{\csname RG@tok@#1\endcsname{#2}}%
\newenvironment{RG*}{\ttfamily}{\relax}%
END
base = @theme.class.base_style
yield "\\definecolor{#{@prefix}@fgcolor}{HTML}{#{inline_name(base.fg || '#000000')}}"
yield "\\definecolor{#{@prefix}@bgcolor}{HTML}{#{inline_name(base.bg || '#FFFFFF')}}"
render_palette(@theme.palette, &b)
@theme.styles.each do |tok, style|
render_inline_pallete(style, &b)
end
Token.each_token do |tok|
style = @theme.class.get_own_style(tok)
style ? render_style(tok, style, &b) : render_blank(tok, &b)
end
yield '\makeatother'
end
def render_palette(palette, &b)
palette.each do |name, color|
hex = inline_name(color)
yield "\\definecolor{#{palette_name(name)}}{HTML}{#{hex}}%"
end
end
def render_inline_pallete(style, &b)
gen_inline(style[:fg], &b)
gen_inline(style[:bg], &b)
end
def inline_name(color)
color =~ /^#(\h+)/ or return nil
# xcolor does not support 3-character HTML colors,
# so we convert them here
case $1.size
when 6
$1
when 3
# duplicate every character: abc -> aabbcc
$1.gsub(/\h/, '\0\0')
else
raise "invalid HTML color: #{$1}"
end.upcase
end
def gen_inline(name, &b)
# detect inline colors
hex = inline_name(name)
return unless hex
@gen_inline ||= {}
@gen_inline[hex] ||= begin
yield "\\definecolor{#{palette_name(hex)}}{HTML}{#{hex}}%"
end
end
def camelize(name)
name.gsub(/_(.)/) { $1.upcase }
end
def palette_name(name)
name = inline_name(name) || name.to_s
"#{@prefix}@palette@#{camelize(@theme.name)}@#{camelize(name.to_s)}"
end
def token_name(tok)
"\\csname #@prefix@tok@#{tok.shortname}\\endcsname"
end
def render_blank(tok, &b)
"\\expandafter\\def#{token_name(tok)}#1{#1}"
end
def render_style(tok, style, &b)
out = String.new('')
out << "\\expandafter\\def#{token_name(tok)}#1{"
out << "\\fboxsep=0pt\\colorbox{#{palette_name(style[:bg])}}{" if style[:bg]
out << '\\textbf{' if style[:bold]
out << '\\textit{' if style[:italic]
out << "\\textcolor{#{palette_name(style[:fg])}}{" if style[:fg]
out << "#1"
# close the right number of curlies
out << "}" if style[:bold]
out << "}" if style[:italic]
out << "}" if style[:fg]
out << "}" if style[:bg]
out << "}%"
yield out
end
end
end
|