jektex 0.0.7 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+