jektex 0.0.7 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a6f411d475735a808e6fb495464688d7acca5fb2afa3d4f467612fc7d5308b7a
4
- data.tar.gz: e322f027b1c7080553a9cf3e3d4145b49166f6d26e3c5502b3daf8a0ce61a0a5
3
+ metadata.gz: a428ba8f2f9f71ee79b44ed698d5314bf89e240eb577f82a1e0e976878875f2b
4
+ data.tar.gz: 3db9e56ab288eec2e3f9457b4693851b0345f1ec7afabeaabaeba6884aa1c792
5
5
  SHA512:
6
- metadata.gz: 30af1bad450e61c9085975a37b6a2142084854d96201eb67e11cc9c80a21d9d3dbbc5894c82610f2cac7ab52b8272f3f5aae8b74bfa054480edbea9865ebbc48
7
- data.tar.gz: 06c8353988adc7940217d6a75ca313cf72a65492db30ab5421567c822199dd6c4b57c86a28485a0b146de826b69c6c1ee80ccb00335277f0b23065881670949d
6
+ metadata.gz: '058eeb45c2e9c2bea5b8294084732d6c4b54e7344c360338bb62f6c9e5a8b2c55e99507be1de78cde4b81a14781a68ee4f530253e90980398ce4ea7c0da418ec'
7
+ data.tar.gz: b6de46d7eb8f639bb7c06ebd0f7aea6df75647d4f8c930fc779d580ec21c33bfa01c9fd909b0cc577c8862310df58c3e59680271ebba99fc95e9e403659bd345
data/README.md CHANGED
@@ -1,26 +1,27 @@
1
1
  [![Gem Version](https://badge.fury.io/rb/jektex.svg)](https://rubygems.org/gems/jektex)
2
2
 
3
- # Jektex
4
- Jekyll plugin for blazing fast server side cached LaTeX rendering with support of macros.
5
- Enjoy comfort of latex and markdown without cluttering your site with bloated javascript.
3
+ # ![Jektex](https://blackblog.cz/assets/img/projects/jektex.svg)
4
+ A Jekyll plugin for blazing-fast server-side cached LaTeX rendering, with support for macros.
5
+ Enjoy the comfort of LaTeX and Markdown without cluttering your site with bloated JavaScript.
6
6
 
7
7
  ## Features
8
8
  - Renders LaTeX formulas during Jekyll rendering
9
- - Works without any javascript on clients side
10
- - Is faster than any other server side Jekyll latex renderer
11
- - Supports user defined global macros
12
- - Has I/O efficient caching system
13
- - Has dynamic and informative log during rendering
14
- - Is easy to setup
15
- - Does not interfere with Jekyll workflow and project structure
16
- - Marks invalid syntax in document
17
- - Prints location of invalid expression during rendering
18
- - Highly configurable but still having sensible defaults
9
+ - Works without any client-side JavaScript
10
+ - Is faster than any other server-side Jekyll LaTeX renderer
11
+ - Supports user-defined global macros
12
+ - Has I/O-efficient caching system
13
+ - Dynamically informs about the number of expressions during rendering
14
+ - Is very easy to setup
15
+ - Doesn't interfere with Jekyll workflow and project structure
16
+ - Marks invalid expressions in document, printing its location during rendering
17
+ - Is highly configurable with sensible defaults
19
18
  - Makes sure that cache does not contain expression rendered with outdated configuration
19
+ - Supports two major LaTeX notations
20
20
 
21
21
  ## Usage
22
+ Jektex supports both the built-in Kramdown math notation, and the newer LaTeX-only math notation.
22
23
 
23
- ### Notation
24
+ ### Kramdown notation
24
25
  **Inline formula**
25
26
  Put formula between two pairs of dolar signs (`$$`) inside of paragraph.
26
27
  ```latex
@@ -29,7 +30,7 @@ adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliq
29
30
  ```
30
31
 
31
32
  **Display formula**
32
- Put formula between two pairs of dolar sings (`$$`) and surround it by two empty lines.
33
+ Put formula between two pairs of dolar sings (`$$`) and surround it with two empty lines.
33
34
  ```latex
34
35
  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
35
36
  incididunt ut labore et dolore magna aliqua.
@@ -42,43 +43,65 @@ ea commodo consequat.
42
43
 
43
44
  _Why Jektex does not use conventional single `$` for inline formulas and double `$$` for
44
45
  display mode?
45
- This is how [kramdown](https://kramdown.gettalong.org/)(Jekyll's markdown parser) works
46
+ This is how [kramdown](https://kramdown.gettalong.org/) (Jekyll's markdown parser) works
46
47
  so I decided to respect this convention. It makes this plugin more consistent and universal._
47
48
 
49
+
50
+ ### LaTex math mode notation
51
+ **Inline formula**
52
+ Put formula between two escaped brackets `\(` `\)`.
53
+ Its position in the text does not matter.
54
+ ```latex
55
+ Lorem ipsum dolor sit amet, consectetur \(e^{i\theta}=\cos(\theta)+i\sin(\theta)\)
56
+ adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
57
+ ```
58
+
59
+ **Display formula**
60
+ Put formula between two escaped square brackets `\[` `\]`.
61
+ Its position in the text does not matter.
62
+ ```latex
63
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
64
+ incididunt ut labore et dolore magna aliqua.
65
+
66
+ \[ \left[ \frac{-\hbar^2}{2\mu}\nabla^2 + V(\mathbf{r},t)\right] \Psi(\mathbf{r},t) \]
67
+
68
+ Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex
69
+ ea commodo consequat.
70
+ ```
71
+
72
+ ### Logo macro
73
+ There is a build in macro for jektex logo. You can use it as `\jektex`.
74
+
48
75
  ### Config
49
- Jektex si highly configurable from your `_config.yml` file
76
+ Jektex si highly configurable via your `_config.yml` file.
50
77
 
51
78
  **Disabling cache**
52
- You can disable caching with `disable_disk_cache = true` in `_config.yml`. Cache is
53
- enabled by default. You can find more information on [Jekyll official website](https://jekyllrb.com/docs/configuration/options/).
79
+ You can disable caching with `disable_disk_cache = true` in `_config.yml`.
80
+ Caching is enabled by default.
81
+ You can find more information on [Jekyll's official website](https://jekyllrb.com/docs/configuration/options/).
54
82
 
55
83
  **Setting cache location**
56
- By default jektex cache will be saved in `.jekyll-cache` directory. This results in it's
57
- deletion when you call `jekyll clean`. To prevent cache deletion or you just want to
58
- change location of cache for another reason you can achieve that by specifying
59
- `cache_dir` in `_config.yml`.
84
+ By default, Jektex cache will be saved in `.jekyll-cache` directory.
85
+ This results in its deletion when you call `jekyll clean`.
86
+ To prevent cache deletion or to change the cache location, you can specify `cache_dir` in `_config.yml`:
60
87
  ```yaml
61
- # Jektex cache dir location
62
88
  jektex:
63
89
  cache_dir: ".jektex-cache"
64
90
  ```
65
91
 
66
- **Ignore**
67
- By default jektex tries to render LaTeX in all files not excluded by Jekyll. But
68
- sometimes you get in situation when you do not want to render some files. For example
69
- _RSS feed_ with excerpts containing LaTeX. As a solution jektex offers `ignore` option.
70
- You can use conventional wild cards using `*`. For example:
92
+ **Ignoring files**
93
+ By default, Jektex tries to render LaTeX in all files rendered by Jekyll.
94
+ This can sometimes be undesirable, for example when rendering an _RSS feed_ with excerpts containing LaTeX.
95
+ Jektex solves this by using the `ignore` option:
71
96
  ```yaml
72
- # Jektex ignore files
73
97
  jektex:
74
98
  ignore: ["*.xml", "README.md", "_drafts/*" ]
75
99
  ```
76
100
 
77
- This example configuration ignores all `.xml` files, `README.md` and all files
78
- in `_drafts` directory.
101
+ You can use conventional wild cards using `*`.
102
+ This example configuration ignores all `.xml` files, `README.md` and all files in the `_drafts` directory.
79
103
 
80
- Another option for ignoring specific posts is setting `jektex` tag in front matter of
81
- post to `false`. For example:
104
+ Another way to ignore specific posts is setting the `jektex` attribute in front matter to `false`:
82
105
  ```yaml
83
106
  ---
84
107
  title: "How Jektex works"
@@ -88,20 +111,26 @@ layout: post
88
111
  ---
89
112
  ```
90
113
 
91
- Setting `jektex` tag to `true` or not setting at all will result in jektex rendering LaTeX
92
- expressions in that post.
114
+ Setting `jektex` tag to `true` or not setting at all will result in Jektex rendering LaTeX expressions in that post.
93
115
 
94
- **Macros**
95
- You can define global macros like this:
116
+ **Using macros**
117
+ You can define global macros:
96
118
  ```yaml
97
- # Jektex macros
98
119
  jektex:
99
120
  macros:
100
121
  - ["\\Q", "\\mathbb{Q}"]
101
122
  - ["\\C", "\\mathbb{C}"]
102
123
  ```
103
- And yes you have to escape backlash(`\`) with another backlash. This is caused by
104
- [yaml definition](https://yaml.org/).
124
+ And yes, you have to escape the backlash (`\`) with another backlash.
125
+ This is due to the [yaml specification](https://yaml.org/).
126
+
127
+ **Silencing Jektex output**
128
+ Jektex periodically informs the user about rendered/cached equations.
129
+ If this is not desired, you can set the `silent` option (`false` by default).
130
+ ```yaml
131
+ jektex:
132
+ silent: true
133
+ ```
105
134
 
106
135
  **Complete examples**
107
136
  Recommended config:
@@ -109,15 +138,18 @@ Recommended config:
109
138
  jektex:
110
139
  cache_dir: ".jektex-cache"
111
140
  ignore: ["*.xml"]
141
+ silent: false
112
142
  macros:
113
143
  - ["\\Q", "\\mathbb{Q}"]
114
144
  - ["\\C", "\\mathbb{C}"]
115
145
  ```
146
+
116
147
  Having no configuration is equivalent to this:
117
148
  ```yaml
118
149
  jektex:
119
150
  cache_dir: ".jekyll-cache"
120
151
  ignore: []
152
+ silent: false
121
153
  macros: []
122
154
  ```
123
155
 
@@ -125,7 +157,7 @@ jektex:
125
157
  This plugin is available as a [RubyGem](https://rubygems.org/gems/jektex).
126
158
 
127
159
  **Using bundler**
128
- Add `jektex` to your `Gemfile` like this:
160
+ Add Jektex to your `Gemfile`:
129
161
  ```ruby
130
162
  group :jekyll_plugins do
131
163
  gem "jektex"
@@ -135,22 +167,23 @@ end
135
167
  and run `bundle install`
136
168
 
137
169
  **Without bundler**
138
- Just run `gem install jektex`
170
+ Run `gem install jektex`
139
171
 
140
172
  **After installation**
141
- Add jektex to your plugin list in your `_config.yml` file:
173
+ Add Jektex to your plugin list in your `_config.yml` file
142
174
  ```yaml
143
175
  plugins:
144
176
  - jektex
145
177
  ```
146
178
 
147
- and do not forget to add `katex.min.css` to you html head:
179
+ and don't forget to add `katex.min.css` to you HTML head:
148
180
  ```html
149
181
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.15.2/dist/katex.min.css" integrity="sha384-MlJdn/WNKDGXveldHDdyRP1R4CTHr3FeuDNfhsLPYrq2t0UBkUdK2jyTnXPEK1NQ" crossorigin="anonymous">
150
182
  ```
151
- It is much better practice to download [**css** file](https://cdn.jsdelivr.net/npm/katex@0.15.2/dist/katex.min.css) and load it as an asset from your server directly.
183
+ It is much better practice to download the [**css** file](https://cdn.jsdelivr.net/npm/katex@0.15.2/dist/katex.min.css) and load it as an asset from your server directly.
152
184
  You can find more information on [KaTeX's website](https://katex.org/docs/browser.html).
153
185
 
154
186
  ## Contributions and bug reports
155
- Feel free to repost any bugs or even make feature request in [issues on official repository](https://github.com/yagarea/jektex/issues).
187
+ Feel free to report any bugs or even make feature request in [issues on official repository](https://github.com/yagarea/jektex/issues).
156
188
  I am opened for pull requests as well.
189
+
data/lib/jektex/jektex.rb CHANGED
@@ -7,9 +7,9 @@ PATH_TO_JS = File.join(__dir__, "/katex.min.js")
7
7
  DEFAULT_CACHE_DIR = ".jekyll-cache"
8
8
  CACHE_FILE = "jektex-cache.marshal"
9
9
  KATEX = ExecJS.compile(open(PATH_TO_JS).read)
10
- PARSE_ERROR_PLACEHOLDER = "<b style='color: red;'>PARSE ERROR</b>"
11
10
  FRONT_MATTER_TAG = "jektex"
12
11
  INDENT = " " * 13
12
+ HTML_ENTITY_PARSER = HTMLEntities.new
13
13
 
14
14
  $global_macros = Hash.new
15
15
  $updated_global_macros = Array.new
@@ -19,6 +19,7 @@ $count_newly_generated_expressions = 0
19
19
  $path_to_cache = File.join(DEFAULT_CACHE_DIR, CACHE_FILE)
20
20
  $cache = nil
21
21
  $disable_disk_cache = false
22
+ $silent = false
22
23
 
23
24
  $ignored = Array.new
24
25
 
@@ -48,11 +49,26 @@ def print_stats
48
49
  $stdout.flush
49
50
  end
50
51
 
51
- def render(page)
52
+ #######################################################################################
53
+ # Render
54
+
55
+ def render_latex_notation(page)
56
+ # check if document is not set to be ignored
57
+ return page.content if !page.data || is_ignored?(page)
58
+ # convert HTML entities back to characters
59
+ post = page.content.to_s
60
+ # render inline expressions
61
+ post = post.gsub(/(\\\()((.|\n)*?)(?<!\\)\\\)/) { |m| escape_method($1, $2, page.relative_path) }
62
+ # render display mode expressions
63
+ post = post.gsub(/(\\\[)((.|\n)*?)(?<!\\)\\\]/) { |m| escape_method($1, $2, page.relative_path) }
64
+ return post
65
+ end
66
+
67
+ def render_kramdown_notation(page)
52
68
  # check if document is not set to be ignored
53
69
  return page.output if !page.data || is_ignored?(page)
54
70
  # convert HTML entities back to characters
55
- post = HTMLEntities.new.decode(page.output.to_s)
71
+ post = page.output.to_s
56
72
  # render inline expressions
57
73
  post = post.gsub(/(\\\()((.|\n)*?)(?<!\\)\\\)/) { |m| escape_method($1, $2, page.relative_path) }
58
74
  # render display mode expressions
@@ -60,10 +76,10 @@ def render(page)
60
76
  return post
61
77
  end
62
78
 
63
- def escape_method( type, expression, doc_path )
79
+ def escape_method(type, expression, doc_path)
64
80
  # detect if expression is in display mode
65
81
  is_in_display_mode = type.downcase =~ /\[/
66
-
82
+ expression = HTML_ENTITY_PARSER.decode(expression)
67
83
  # generate a hash from the math expression
68
84
  expression_hash = Digest::SHA2.hexdigest(expression) + is_in_display_mode.to_s
69
85
 
@@ -71,7 +87,7 @@ def escape_method( type, expression, doc_path )
71
87
  if($cache.has_key?(expression_hash) && !contains_updated_global_macro?(expression))
72
88
  # check if expressin conains updated macro
73
89
  $count_newly_generated_expressions += 1
74
- print_stats
90
+ print_stats unless $silent
75
91
  return $cache[expression_hash]
76
92
 
77
93
  # else generate one and store it
@@ -79,8 +95,10 @@ def escape_method( type, expression, doc_path )
79
95
  # create the cache directory, if it doesn't exist
80
96
  begin
81
97
  # render using ExecJS
82
- result = KATEX.call("katex.renderToString", expression,
83
- { displayMode: is_in_display_mode, macros: $global_macros})
98
+ result = KATEX.call("katex.renderToString", expression,
99
+ { displayMode: is_in_display_mode,
100
+ macros: $global_macros
101
+ })
84
102
  rescue SystemExit, Interrupt
85
103
  # save cache to disk
86
104
  File.open($path_to_cache, "w"){|to_file| Marshal.dump($cache, to_file)}
@@ -88,32 +106,48 @@ def escape_method( type, expression, doc_path )
88
106
  raise
89
107
  rescue ExecJS::ProgramError => pe
90
108
  # catch parse error
91
- puts "\e[31m " + pe.message.gsub("ParseError: ", "") + "\n\t" + doc_path + "\e[0m"
92
- return PARSE_ERROR_PLACEHOLDER
109
+ puts "\e[31m #{pe.message.gsub("ParseError: ", "")}\n\t#{doc_path}\e[0m" unless $silent
110
+ # render expression with error highlighting enabled
111
+ return KATEX.call("katex.renderToString", expression,
112
+ { displayMode: is_in_display_mode,
113
+ macros: $global_macros,
114
+ throwOnError: false
115
+ })
93
116
  end
94
117
  # save to cache
95
- $cache[expression_hash] = @result
118
+ $cache[expression_hash] = result
96
119
  # update count of newly generated expressions
97
120
  $count_newly_generated_expressions += 1
98
- print_stats
121
+ print_stats unless $silent
99
122
  return result
100
123
  end
101
124
  end
102
125
 
103
126
  Jekyll::Hooks.register :pages, :post_render do |page|
104
- page.output = render(page)
127
+ page.output = render_kramdown_notation(page)
105
128
  end
106
129
 
107
130
  Jekyll::Hooks.register :documents, :post_render do |doc|
108
- doc.output = render(doc)
131
+ doc.output = render_kramdown_notation(doc)
109
132
  end
110
133
 
134
+ Jekyll::Hooks.register :pages, :pre_render do |page|
135
+ page.content = render_latex_notation(page)
136
+ end
137
+
138
+ Jekyll::Hooks.register :documents, :pre_render do |doc|
139
+ doc.content = render_latex_notation(doc)
140
+ end
141
+
142
+ #######################################################################################
143
+ # SETTINGS AND INIT
144
+
111
145
  Jekyll::Hooks.register :site, :after_init do |site|
112
146
  # load jektex config from config file and if no config is defined make empty one
113
147
  config = site.config["jektex"] || Hash.new
114
148
 
115
149
  # check if there is defined custom cache location in config
116
- $path_to_cache = File.join(config["cache_dir"].to_s, CACHE_FILE) if config.has_key?("cache_dir")
150
+ $path_to_cache = File.join(config["cache_dir"].to_s, CACHE_FILE) if config.has_key?("cache_dir")
117
151
 
118
152
  # load content of cache file if it exists
119
153
  if File.exist?($path_to_cache)
@@ -122,7 +156,7 @@ Jekyll::Hooks.register :site, :after_init do |site|
122
156
  $cache = Hash.new
123
157
  end
124
158
 
125
- # check if cache is disable in config
159
+ # check if cache is disabled in config
126
160
  $disable_disk_cache = site.config["disable_disk_cache"] if site.config.has_key?("disable_disk_cache")
127
161
 
128
162
  # load macros
@@ -132,19 +166,29 @@ Jekyll::Hooks.register :site, :after_init do |site|
132
166
  end
133
167
  end
134
168
 
169
+ # check is silent mode is activated
170
+ $silent = config["silent"] if config.has_key?("silent")
135
171
  # make list of updated macros
136
172
  $updated_global_macros = get_list_of_updated_global_macros($global_macros, $cache["cached_global_macros"])
173
+
137
174
  # print macro information
138
- if $global_macros.empty?
139
- puts "#{INDENT}LaTeX: no macros loaded"
140
- else
141
- puts "#{INDENT}LaTeX: #{$global_macros.size} macro" +
142
- ($global_macros.size == 1 ? "" : "s") + " loaded" +
143
- ($updated_global_macros.empty? ? "" : " (#{$updated_global_macros.size} updated)")
175
+ unless $silent
176
+ if $global_macros.empty?
177
+ puts "#{INDENT}LaTeX: no macros loaded" unless $silent
178
+ else
179
+ puts "#{INDENT}LaTeX: #{$global_macros.size} macro" \
180
+ "#{$global_macros.size == 1 ? "" : "s"} loaded" +
181
+ ($updated_global_macros.empty? ? "" : " (#{$updated_global_macros.size} updated)") unless $silent
182
+ end
144
183
  end
145
184
 
185
+ # add jektex logo macro
186
+ $global_macros['\jektex'] =
187
+ '\text{\raisebox{-0.55ex}{J}\kern{-0.3ex}E\kern{-0.25ex}\raisebox{-0.5ex}{K}\kern{-0.7ex}}\TeX'
188
+
146
189
  # load list of ignored files
147
190
  $ignored = config["ignore"] if config.has_key?("ignore")
191
+ $ignored.append("#{$path_to_cache}/*")
148
192
  end
149
193
 
150
194
  Jekyll::Hooks.register :site, :after_reset do
@@ -153,8 +197,10 @@ Jekyll::Hooks.register :site, :after_reset do
153
197
  end
154
198
 
155
199
  Jekyll::Hooks.register :site, :post_write do
200
+ # print stats once more to prevent them from being overwriten by error log
201
+ print_stats unless $silent
156
202
  # print new line to prevent overwriting previous output
157
- print "\n"
203
+ print "\n" unless $silent
158
204
  # check if caching is enabled
159
205
  if !$disable_disk_cache
160
206
  # save global macros to cache
@@ -166,3 +212,4 @@ Jekyll::Hooks.register :site, :post_write do
166
212
  end
167
213
  end
168
214
 
215
+