Module: Polytexnic::Utils
Instance Method Summary collapse
-
#add_font_info(string) ⇒ Object
Adds some verbatim font info (including size).
-
#cache_urls(doc, latex = false) ⇒ Object
Caches URLs for href and url commands.
-
#debug? ⇒ Boolean
Returns true if we are debugging, false otherwise.
-
#digest(string, options = {}) ⇒ Object
Returns a salted hash digest of the string.
-
#escape_backslashes(string) ⇒ Object
Escapes backslashes.
-
#expand_input!(text, code_function, ext = 'md') ⇒ Object
Expands ‘input’ command by processing & inserting the target source.
-
#framed(code) ⇒ Object
Puts a frame around code.
-
#highlight(key, content, language, formatter, options) ⇒ Object
Highlights a code sample.
-
#highlight_lines(output, options) ⇒ Object
Highlight lines (i.e., with a yellow background).
-
#highlight_source_code(document) ⇒ Object
Highlights source code.
-
#highlighted_lines(options) ⇒ Object
Returns an array with the highlighted lines.
-
#horrible_backslash_kludge(string) ⇒ Object
Does something horrible with backslashes.
-
#linux? ⇒ Boolean
Returns true if platform is Linux.
-
#os_x? ⇒ Boolean
Returns true if platform is OS X.
-
#os_x_newer? ⇒ Boolean
Returns true for OS X Mountain Lion (10.8) and later.
-
#os_x_older? ⇒ Boolean
Returns true for OS X Lion (10.7) and earlier.
-
#pipeline_digest(element) ⇒ Object
Returns a digest for passing things through the pipeline.
-
#profiling? ⇒ Boolean
Returns true if we are profiling the code, false otherwise.
- #set_test_mode! ⇒ Object
- #test? ⇒ Boolean
-
#tralics ⇒ Object
Returns the executable for the Tralics LaTeX-to-XML converter.
-
#tralics_commands ⇒ Object
Returns some commands for Tralics.
-
#underscore_digest ⇒ Object
Returns a digest for use in labels.
-
#xmlelement(name, skip = false) ⇒ Object
Returns a Tralics pseudo-LaTeX XML element.
Instance Method Details
#add_font_info(string) ⇒ Object
Adds some verbatim font info (including size). We prepend rather than replace the styles because the Pygments output includes a required override of the default commandchars. Since the substitution is only important in the context of a PDF book, it only gets made if there’s a style in the ‘softcover.sty’ file. We also support custom overrides in ‘custom_pdf.sty’.
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
# File 'lib/polytexnic/utils.rb', line 220 def add_font_info(string) softcover_sty = File.join('latex_styles', 'softcover.sty') custom_pdf_sty = File.join('latex_styles', 'custom_pdf.sty') regex = '{code}{Verbatim}{(.*)}' styles = nil [softcover_sty, custom_pdf_sty].reverse.each do |filename| if File.exist?(filename) styles ||= File.read(filename).scan(/#{regex}/).flatten.first end end unless styles.nil? string.to_s.gsub!("\\begin{Verbatim}[", "\\begin{Verbatim}[#{styles},") end string end |
#cache_urls(doc, latex = false) ⇒ Object
Caches URLs for href and url commands.
95 96 97 98 99 100 101 102 103 104 |
# File 'lib/polytexnic/utils.rb', line 95 def cache_urls(doc, latex=false) doc.tap do |text| text.gsub!(/\\(href|url){(.*?)}/) do command, url = $1, $2 key = digest(url) literal_cache[key] = url command == 'url' ? "\\href{#{key}}{#{url}}" : "\\href{#{key}}" end end end |
#debug? ⇒ Boolean
Returns true if we are debugging, false otherwise. Manually change to ‘true` on an as-needed basis.
256 257 258 |
# File 'lib/polytexnic/utils.rb', line 256 def debug? false end |
#digest(string, options = {}) ⇒ Object
Returns a salted hash digest of the string.
68 69 70 71 |
# File 'lib/polytexnic/utils.rb', line 68 def digest(string, = {}) salt = [:salt] || SecureRandom.base64 Digest::SHA1.hexdigest("#{salt}--#{string}") end |
#escape_backslashes(string) ⇒ Object
Escapes backslashes. Interpolated backslashes need extra escaping. We only escape ‘\’ by itself, i.e., a backslash followed by spaces or the end of line.
90 91 92 |
# File 'lib/polytexnic/utils.rb', line 90 def escape_backslashes(string) string.gsub(/\\(\s+|$)/) { '\\\\' + $1.to_s } end |
#expand_input!(text, code_function, ext = 'md') ⇒ Object
Expands ‘input’ command by processing & inserting the target source.
36 37 38 39 40 41 42 43 44 45 |
# File 'lib/polytexnic/utils.rb', line 36 def (text, code_function, ext = 'md') text.gsub!(/^[ \t]*\\input\{(.*?)\}[ \t]*$/) do # Prepend a newline for safety. included_text = "\n" + File.read("#{$1}.#{ext}") code_function.call(included_text).tap do |clean_text| # Recursively substitute '\input' in included text. (clean_text, code_function, ext) end end end |
#framed(code) ⇒ Object
Puts a frame around code.
197 198 199 |
# File 'lib/polytexnic/utils.rb', line 197 def framed(code) "\\begin{framed_shaded}\n#{code}\n\\end{framed_shaded}" end |
#highlight(key, content, language, formatter, options) ⇒ Object
Highlights a code sample.
202 203 204 205 206 207 208 209 210 211 212 |
# File 'lib/polytexnic/utils.rb', line 202 def highlight(key, content, language, formatter, ) require 'pygments' = JSON.parse('{' + .to_s + '}') if ['linenos'] && formatter == 'html' # Inline numbers look much better in HTML but are invalid in LaTeX. ['linenos'] = 'inline' end highlight_cache[key] ||= Pygments.highlight(content, lexer: language, formatter: formatter, options: ) end |
#highlight_lines(output, options) ⇒ Object
Highlight lines (i.e., with a yellow background). This is needed due to a Pygments bug that fails to highlight lines in the LaTeX output.
179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/polytexnic/utils.rb', line 179 def highlight_lines(output, ) highlighted_lines().each do |i| if i > output.length - 1 $stderr.puts "Warning: Highlighted line #{i} out of range" unless test? $stderr.puts output.inspect unless test? else output[i] = '\setlength{\fboxsep}{0pt}\colorbox{hilightyellow}{' + output[i] + '}' end end end |
#highlight_source_code(document) ⇒ Object
Highlights source code.
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/polytexnic/utils.rb', line 151 def highlight_source_code(document) if document.is_a?(String) # LaTeX substitutions = {} document.tap do code_cache.each do |key, (content, language, in_codelisting, )| code = highlight(key, content, language, 'latex', ) output = code.split("\n") horrible_backslash_kludge(add_font_info(output.first)) highlight_lines(output, ) code = output.join("\n") substitutions[key] = in_codelisting ? code : framed(code) end document.gsub!(Regexp.union(substitutions.keys), substitutions) end else # HTML document.css('div.code').each do |code_block| key = code_block.content next unless (value = code_cache[key]) content, language, _, = value code_block.inner_html = highlight(key, content, language, 'html', ) end end end |
#highlighted_lines(options) ⇒ Object
Returns an array with the highlighted lines.
192 193 194 |
# File 'lib/polytexnic/utils.rb', line 192 def highlighted_lines() JSON.parse('{' + .to_s + '}')['hl_lines'] || [] end |
#horrible_backslash_kludge(string) ⇒ Object
Does something horrible with backslashes. OK, so the deal is that code highlighted for LaTeX contains the line beginVerbatim Oh crap, there are backslashes in there. This means we have no chance of getting things to work after interpolating, gsubbing, and so on, because in Ruby ‘\foo’ is the same as ‘\\foo’, ‘}’ is ‘}’, etc. I thought I escaped (heh) this problem with the ‘escape_backslashes` method, but here the problem is extremely specific. In particular, \{} is really \ and { and }, but Ruby doesn’t know WTF to do with it, and thinks that it’s “\{}”, which is the same as ‘{}’. The solution is to replace ‘\\’ with some number of backslashes. How many? I literally had to just keep adding backslashes until the output was correct when running ‘softcover build:pdf`.
250 251 252 |
# File 'lib/polytexnic/utils.rb', line 250 def horrible_backslash_kludge(string) string.to_s.gsub!(/commandchars=\\\\/, 'commandchars=\\\\\\\\') end |
#linux? ⇒ Boolean
Returns true if platform is Linux.
63 64 65 |
# File 'lib/polytexnic/utils.rb', line 63 def linux? RUBY_PLATFORM.match(/linux/) end |
#os_x? ⇒ Boolean
Returns true if platform is OS X.
58 59 60 |
# File 'lib/polytexnic/utils.rb', line 58 def os_x? RUBY_PLATFORM.match(/darwin/) end |
#os_x_newer? ⇒ Boolean
Returns true for OS X Mountain Lion (10.8) and later.
48 49 50 |
# File 'lib/polytexnic/utils.rb', line 48 def os_x_newer? os_x? && !os_x_older? end |
#os_x_older? ⇒ Boolean
Returns true for OS X Lion (10.7) and earlier.
53 54 55 |
# File 'lib/polytexnic/utils.rb', line 53 def os_x_older? os_x? && RUBY_PLATFORM.include?('11') end |
#pipeline_digest(element) ⇒ Object
Returns a digest for passing things through the pipeline.
74 75 76 77 |
# File 'lib/polytexnic/utils.rb', line 74 def pipeline_digest(element) value = digest("#{Time.now.to_s}::#{element}") @literal_cache[element.to_s] ||= value end |
#profiling? ⇒ Boolean
Returns true if we are profiling the code, false otherwise. Manually change to ‘true` on an as-needed basis.
262 263 264 265 |
# File 'lib/polytexnic/utils.rb', line 262 def profiling? return false if test? false end |
#set_test_mode! ⇒ Object
267 268 269 |
# File 'lib/polytexnic/utils.rb', line 267 def set_test_mode! @@test_mode = true end |
#test? ⇒ Boolean
271 272 273 |
# File 'lib/polytexnic/utils.rb', line 271 def test? defined?(@@test_mode) && @@test_mode end |
#tralics ⇒ Object
Returns the executable for the Tralics LaTeX-to-XML converter.
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# File 'lib/polytexnic/utils.rb', line 10 def tralics executable = `which tralics`.chomp return executable unless executable.empty? filename = if os_x_newer? 'tralics-os-x-newer' elsif os_x_older? 'tralics-os-x-older' elsif linux? "tralics-#{RUBY_PLATFORM}" end project_root = File.join(File.dirname(__FILE__), '..', '..') executable = File.join(project_root, 'precompiled_binaries', filename) output = `#{executable}` unless output.include?('This is tralics') url = 'https://github.com/softcover/tralics' $stderr.puts "\nError: Document not built" $stderr.puts "No compatible Tralics LaTeX-to-XML translator found" $stderr.puts "Follow the instructions at\n #{url}\n" $stderr.puts "to compile tralics and put it on your path" exit(1) end @tralics ||= executable end |
#tralics_commands ⇒ Object
Returns some commands for Tralics. For various reasons, we don’t actually want to include these in the style file that gets passed to LaTeX. For example, the commands with ‘xmlelt’ aren’t even valid LaTeX; they’re actually pseudo-LaTeX that has special meaning to the Tralics processor.
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/polytexnic/utils.rb', line 121 def tralics_commands base_commands = <<-'EOS' % Commands specific to Tralics \def\hyperref[#1]#2{\xmlelt{a}{\XMLaddatt{target}{#1}#2}} \newcommand{\heading}[1]{\xmlelt{heading}{#1}} \newcommand{\codecaption}[1]{\xmlelt{heading}{#1}} \newcommand{\sout}[1]{\xmlelt{sout}{#1}} \newcommand{\kode}[1]{\xmlelt{kode}{#1}} \newcommand{\coloredtext}[2]{\xmlelt{coloredtext}{\AddAttToCurrent{color}{#1}#2}} \newcommand{\coloredtexthtml}[2]{\xmlelt{coloredtexthtml}{\AddAttToCurrent{color}{#1}#2}} \newcommand{\filepath}[1]{\xmlelt{filepath}{#1}} \newcommand{\image}[1]{\xmlelt{image}{#1}} \newcommand{\imagebox}[1]{\xmlelt{imagebox}{#1}} % Ignore pbox argument, just replacing with content. \newcommand{\pbox}[2]{#2} % Ignore some other commands. \newcommand{\includepdf}[1]{} \newcommand{\newunicodechar}[2]{} \newcommand{\extrafloats}[1]{} EOS custom_commands = <<-EOS \\usepackage{amsthm} \\theoremstyle{definition} \\newtheorem{codelisting}{#{language_labels["listing"]}}[chapter] \\newtheorem{aside}{#{language_labels["aside"]}}[chapter] EOS [base_commands, custom_commands].join("\n") end |
#underscore_digest ⇒ Object
Returns a digest for use in labels. I like to use labels of the form cha:foo_bar, but for some reason Tralics removes the underscore in this case.
82 83 84 |
# File 'lib/polytexnic/utils.rb', line 82 def underscore_digest pipeline_digest('_') end |
#xmlelement(name, skip = false) ⇒ Object
Returns a Tralics pseudo-LaTeX XML element. The use of the ‘skip’ flag is a hack to be able to use xmlelement even when generating, e.g., LaTeX, where we simply want to yield the block.
110 111 112 113 114 |
# File 'lib/polytexnic/utils.rb', line 110 def xmlelement(name, skip = false) output = (skip ? "" : "\\begin{xmlelement}{#{name}}") output << yield if block_given? output << (skip ? "" : "\\end{xmlelement}") end |