jekyll-t4j 0.4.0 → 0.5.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: 0a1870ef42a1f2b51f55427bf9fc304e59224e0d473c9f1e0c43297ad2347709
4
- data.tar.gz: 47330b060c44e1850c8691d2d88e302483c4069bf166b6f9dd4720b92170275b
3
+ metadata.gz: '08b199ca229a735a5862598c3833f1ca8c4643db6458fb24e1575604df8196aa'
4
+ data.tar.gz: aeb6470ab96afd7e12008946f568804c36fb44d42350cec3d5d3f20728b86401
5
5
  SHA512:
6
- metadata.gz: b0842f23b676c35dfdbca726b144c243c0331d56200833af4e47c61210a5b833d0e86cd02cfd022f34b69b0af24272d46630e50521da98125343640115c22c66
7
- data.tar.gz: 16d051b87d412c97fa69ed6222aadd4367506733b39963ccd91896f15a7eb8874b761bdd3db3bce4c2a3c1d8cc0e6445c5039eba1c14c2fe67d2e430dc3e2e5a
6
+ metadata.gz: eff915fbdffe8b6942226ef1dcfc39068fd89d5fd6fc51469844b7793af7804304f41249a5f02e11d2427fc8b9d5fc44c51bd6641ec449f112daaeb0e3e15a18
7
+ data.tar.gz: 5e6f3859887cca6604359dcc8341279f18b50d877815f87d7dc14e0310219c70a9b51d572c2ce1df1dc7b83aaad823a0745400859520e9e7ceeb9593d26fda4e
data/README.md CHANGED
@@ -3,13 +3,16 @@
3
3
  jekyll-t4j is a Jekyll plugin providing (nearly) full support of LaTeX.
4
4
 
5
5
  - **Comprehensive**: support almost all packages, including tikz, chemfig, etc.
6
- - **Fast**: employ KaTeX to boost speed. Use cache, precompiled preamble, bulk rendering.
6
+ - **Highly optimized**: employ KaTeX to boost speed. Use cache, precompiled preamble, bulk rendering.
7
7
  - **Server side rendering**: all stuffs are done in server.
8
8
 
9
9
  T4J integrates Jekyll with your local TeX distribution, so you need to have either [MikTeX](https://miktex.org/) or [TeX Live](https://tug.org/texlive/) installed.
10
10
 
11
11
  Feel free to write any LaTeX! 🎉
12
12
 
13
+ > **Warning**
14
+ > T4J now is in rapid development, it's pretty unstable. So don't forget to check and update frequently.
15
+
13
16
  ## Getting Started
14
17
 
15
18
  Let's start by a simple instance, write a post `2023-02-04-hello-latex.md` and fill it with:
@@ -1,28 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "execjs"
3
+ require "duktape"
4
4
 
5
5
  module Jekyll::T4J
6
6
  module Engine
7
7
  KATEX_VERSION = "0.16.4"
8
8
 
9
- @@_katex_js_ = nil
9
+ @@_katex_ctx_ = nil
10
10
 
11
11
  def self.setup_katex
12
- if not @@_katex_js_ then
12
+ if not @@_katex_ctx_ then
13
13
  src = File.read(File.join(__dir__, "katex.js"))
14
14
  src << File.read(File.join(__dir__, "katex.mhchem.js")) if Jekyll::T4J.cfg_pkgs.include?("mhchem")
15
15
 
16
- @@_katex_js_ = ExecJS.runtime.compile(src)
16
+ @@_katex_ctx_ = Duktape::Context.new
17
+ @@_katex_ctx_.exec_string(src)
17
18
  end
18
19
  end
19
20
 
20
- def self.katex_raw(snippet, options = nil)
21
- snippet = split_snippet(snippet)
22
- options = {} unless options
23
- options[:displayMode] = snippet[1]
21
+ def self.katex_raw(snippet)
22
+ begin
23
+ @@_katex_ctx_.call_prop(["katex", "renderToString"], snippet.code_in,
24
+ {
25
+ displayMode: snippet.display_mode?,
26
+ output: "mathml",
27
+ strict: true
28
+ }
29
+ )[20..-8]
30
+ rescue
31
+ nil
32
+ end
33
+ end
24
34
 
25
- @@_katex_js_.call("katex.renderToString", snippet[0], options)
35
+ def self.katex_raw_bulk(snippets)
36
+ snippets.map do |snippet|
37
+ katex_raw(snippet) # TODO: parallel
38
+ end
26
39
  end
27
40
  end
28
41
  end
@@ -7,35 +7,30 @@ unless system("latex -version", [:out, :err] => File::NULL)
7
7
  end
8
8
 
9
9
  require "open3"
10
- require "jekyll/cache"
11
10
 
12
- require "jekyll-t4j/engine/dvisvgm"
13
11
  require "jekyll-t4j/engine/katex"
12
+ require "jekyll-t4j/engine/dvisvgm"
14
13
 
15
14
  module Jekyll::T4J
16
15
  module Engine
17
- @@cache_katex = Jekyll::Cache.new "Jekyll::T4J::Katex"
18
- @@cache_dvisvgm = Jekyll::Cache.new "Jekyll::T4J::Dvisvgm"
19
-
20
- @@header = <<~HD
21
- <link rel=\"stylesheet\" href=\"https://unpkg.com/katex@#{KATEX_VERSION}/dist/katex.min.css\">
22
- <style>
23
- .katex-ext-d {
24
- border-radius: 0px;
25
- display: block;
26
- margin: 5px auto;
27
- }
28
- .katex-ext-i {
29
- border-radius: 0px;
30
- display: inline;
31
- vertical-align: middle;
32
- }
33
- </style>
34
- HD
35
- @@header.freeze
16
+ @@cache_katex = Jekyll::Cache.new "Jekyll-T4J::Katex"
17
+ @@cache_dvisvgm = Jekyll::Cache.new "Jekyll-T4J::Dvisvgm"
36
18
 
37
19
  def self.header
38
- @@header
20
+ <<~HD
21
+ <script>
22
+ document.addEventListener("DOMContentLoaded", () => {
23
+ let z = document.body.getElementsByTagName("include")
24
+ for (let i = 0; i < z.length; ++i) {
25
+ let elmnt = z[i]
26
+ let xhttp = new XMLHttpRequest()
27
+ xhttp.onreadystatechange = function() { elmnt.innerHTML = this.responseText }
28
+ xhttp.open("GET", elmnt.getAttribute("src"), true)
29
+ xhttp.send()
30
+ }
31
+ })
32
+ </script>
33
+ HD
39
34
  end
40
35
 
41
36
  def self.setup
@@ -44,95 +39,82 @@ module Jekyll::T4J
44
39
  end
45
40
 
46
41
  def self.render(snippets, merger)
47
- gen = ->(svg_data, displayMode) {
48
- "<img src=\"#{merger.(svg_data, ".svg")}\" class=\"#{
49
- displayMode ? "katex-ext-d" : "katex-ext-i"
50
- }\" style=\"height:#{
51
- (svg_data[/height='(\S+?)pt'/, 1].to_f * 0.1).to_s[/\d+\.\d{1,4}/]
52
- }em\">"
42
+ gen_svg = ->(svg, displayMode) {
43
+ "<embed src=\"#{merger.(svg, ".svg")}\" style=\"height:#{
44
+ (svg[/height='(\S+?)pt'/, 1].to_f * 0.1).round(4)
45
+ }em;#{
46
+ displayMode ? "display:block;margin:auto" : "display:inline;vertical-align:middle"
47
+ }\">"
53
48
  }
54
-
55
- # normalize 'snippets'
56
- snippets.map! {|snippet|
57
- if not snippet.start_with?("\\") then
58
- s = split_snippet(snippet)
59
- snippet = s[1] ? "\\[#{s[0]}\\]" : "\\(#{s[0]}\\)"
60
- end
61
-
62
- snippet
49
+ gen_mathml = ->(mathml) {
50
+ "<include src=\"#{merger.(mathml, ".xml")}\"></include>"
63
51
  }
64
52
 
65
- # fill 'snippets' with katex and cached dvisvgm
66
- unset = []
67
- uncached = {}
68
- snippets.each_index {|i|
69
- s = snippets[i]
70
- r = @@cache_katex.getset(s) {
71
- begin
72
- katex_raw(s, {strict: true})
73
- rescue
74
- "NIL"
75
- end
76
- }
53
+ # 'snippets' << caches
54
+ uncached = {} #act as hash set
55
+ snippets.each_with_index do |s, i|
56
+ r = nil
77
57
 
78
- if r == "NIL" then
58
+ begin
59
+ r = gen_mathml.(@@cache_katex[s.source])
60
+ rescue
79
61
  begin
80
- r = gen.(@@cache_dvisvgm[Jekyll::T4J.cfg_pkgs + s], is_display_mode?(s))
62
+ r = gen_svg.(@@cache_dvisvgm[Jekyll::T4J.cfg_pkgs + s.source], s.display_mode?)
81
63
  rescue
82
64
  uncached[s] = nil
83
- unset << i
84
- r = s
85
65
  end
86
66
  end
87
67
 
88
- snippets[i] = r
89
- }
68
+ snippets[i] = r if r
69
+ end
70
+
71
+ # finish if no 'uncached' found
72
+ return if uncached.empty?
73
+ uncached = uncached.keys
74
+
75
+ # otherwise render 'uncached' with katex and cache them
76
+ katex_raw_bulk(uncached).each_with_index do |result, i|
77
+ @@cache_katex[uncached[i].source] = result if result
78
+ end
79
+
80
+ # 'snippets' << rendering results
81
+ uncached = {} #act as hash set again
82
+ snippets.each_with_index do |s, i|
83
+ next if !s.is_a?(Snippet)
90
84
 
91
- # render 'uncached'
92
- if not uncached.empty? then
93
- # bulk render 'uncached'
94
85
  begin
95
- uncached_snippets = uncached.keys
96
- rendered = dvisvgm_raw_bulk(uncached_snippets)
97
- rendered.each_index {|i| uncached[uncached_snippets[i]] = rendered[i]}
86
+ snippets[i] = gen_mathml.(@@cache_katex[s.source])
87
+ rescue
88
+ uncached[s] = nil
98
89
  end
90
+ end
99
91
 
100
- # flush 'uncached' to 'snippets' and cache them
101
- unset.each {|i|
102
- s = snippets[i]
103
- r = uncached[s]
92
+ # finish if no 'uncached' found
93
+ return if uncached.empty?
94
+ uncached = uncached.keys
104
95
 
105
- snippets[i] = gen.(r, is_display_mode?(s))
106
- @@cache_dvisvgm[Jekyll::T4J.cfg_pkgs + s] = r
107
- }
96
+ # otherwise render 'uncached' with dvisvgm and cache them
97
+ dvisvgm_raw_bulk(uncached).each_with_index do |result, i|
98
+ @@cache_dvisvgm[Jekyll::T4J.cfg_pkgs + uncached[i].source] = result
108
99
  end
109
- end
110
100
 
111
- def self.shell(cmd, pwd, times = 1)
112
- while true do
113
- break if times <= 0
114
- times -= 1
101
+ # 'snippets' << rendering results
102
+ snippets.each_with_index do |s, i|
103
+ next if !s.is_a?(Snippet)
115
104
 
116
- log, s = Open3.capture2e(cmd, :chdir => pwd) # TODO: even the quickest 'dvisvgm' cost at least 0.3s in my laptop
117
- raise log if not s.success?
105
+ begin
106
+ snippets[i] = gen_svg.(@@cache_dvisvgm[Jekyll::T4J.cfg_pkgs + s.source], s.display_mode?)
107
+ rescue
108
+ # impossible!
109
+ end
118
110
  end
119
111
  end
120
112
 
121
- def self.is_display_mode?(snippet)
122
- snippet.start_with?("\\[") or snippet.start_with?("$$")
123
- end
124
-
125
- def self.split_snippet(snippet)
126
- displayMode = false
127
- range = 2..-3
128
-
129
- if snippet.start_with?("\\[") or snippet.start_with?("$$") then
130
- displayMode = true
131
- elsif snippet.start_with?("$") then
132
- range = 1..-2
113
+ def self.shell(cmd, pwd, n = 1)
114
+ n.times do
115
+ log, s = Open3.capture2e(cmd, :chdir => pwd) # TODO: even the quickest 'dvisvgm' cost at least 0.3s in my laptop
116
+ raise log if not s.success?
133
117
  end
134
-
135
- [snippet[range], displayMode]
136
118
  end
137
119
  end
138
120
  end
@@ -20,10 +20,10 @@ module Jekyll::T4J
20
20
 
21
21
  # write external files and clean up
22
22
  Jekyll::Hooks.register :site, :post_write, priority: Jekyll::Hooks::PRIORITY_MAP[:low] do |site|
23
- @@table.each {|filedata, request|
23
+ @@table.each do |filedata, request|
24
24
  basename = request[0]
25
25
  request[1].each {|extname| File.write(File.join(site.dest, basename + extname), filedata)}
26
- }
26
+ end
27
27
 
28
28
  @@table.clear
29
29
  end
@@ -82,16 +82,16 @@ module Jekyll::T4J
82
82
  docs = []
83
83
 
84
84
  # collect tex snippets
85
- site.each_site_file {|f|
85
+ site.each_site_file do |f|
86
86
  next if f.is_a?(Jekyll::StaticFile)
87
87
  next if f.is_a?(Jekyll::Page) and not f.html?
88
88
 
89
89
  parts = extract(f.output)
90
90
  if parts.size > 1 then #'f' has tex snippet(s)
91
- parts.each {|part| snippets << CGI::unescapeHTML(part[0]) if part[1]} # TODO: a subtle bug about unescaping HTML
91
+ parts.each {|part| snippets << Jekyll::T4J::Snippet.new(CGI::unescapeHTML part[0]) if part[1]}
92
92
  docs << [f, parts]
93
93
  end
94
- }
94
+ end
95
95
 
96
96
  # render 'snippets'
97
97
  Jekyll::T4J::Engine.render(snippets, ->(data, extname) {Jekyll::T4J::Merger.ask_for_merge(data, extname)})
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll::T4J
4
+ class Snippet
5
+ @is_display_mode
6
+ @source
7
+ @code_in
8
+
9
+ attr_reader :source, :code_in
10
+
11
+ def initialize(raw)
12
+ @is_display_mode = raw.start_with?("\\[") or raw.start_with?("$$")
13
+
14
+ # extract the code inside and minimize it
15
+ @code_in = raw[(display_mode? or raw.start_with?("\\")) ? (2..-3) : (1..-2)]
16
+ @code_in.strip! # TODO: minimize code
17
+ @code_in.freeze
18
+
19
+ # create minimized source
20
+ @source = display_mode? ? "\\[#{code_in}\\]" : "\\(#{code_in}\\)"
21
+ @source.freeze
22
+
23
+ # we are immutable
24
+ freeze
25
+ end
26
+
27
+ def display_mode?
28
+ @is_display_mode
29
+ end
30
+
31
+ def hash
32
+ source.hash
33
+ end
34
+
35
+ def ==(other)
36
+ self.class === other and source == other.source
37
+ end
38
+
39
+ alias to_s source
40
+ alias eql? ==
41
+ end
42
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Jekyll
4
4
  module T4J
5
- VERSION = "0.4.0"
5
+ VERSION = "0.5.0"
6
6
  end
7
7
  end
data/lib/jekyll-t4j.rb CHANGED
@@ -14,6 +14,7 @@ module Jekyll::T4J
14
14
  # initialize plugin
15
15
  Jekyll::Hooks.register :site, :after_init do |site|
16
16
  Jekyll.logger.info "Initializing T4J..."
17
+ t0 = Time.now
17
18
 
18
19
  cfg = site.config["t4j"]
19
20
  if cfg and (cfg = cfg["packages"]) then
@@ -30,9 +31,11 @@ module Jekyll::T4J
30
31
  end
31
32
 
32
33
  Jekyll::T4J::Engine.setup
34
+ Jekyll.logger.info "", "done in #{(Time.now - t0).round(3)} seconds."
33
35
  end
34
36
  end
35
37
 
38
+ require "jekyll-t4j/snippet"
36
39
  require "jekyll-t4j/merger"
37
40
  require "jekyll-t4j/engine"
38
41
  require "jekyll-t4j/renderer"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-t4j
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - crow02531
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-02-14 00:00:00.000000000 Z
11
+ date: 2023-02-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jekyll
@@ -25,20 +25,21 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '4.1'
27
27
  - !ruby/object:Gem::Dependency
28
- name: execjs
28
+ name: duktape
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '2.8'
33
+ version: '2.7'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '2.8'
41
- description: " A Jekyll plugin providing (nearly) full support of LaTeX.\n"
40
+ version: '2.7'
41
+ description: " An optimized Jekyll plugin providing (nearly) full support of
42
+ LaTeX.\n"
42
43
  email: crow02531@outlook.com
43
44
  executables: []
44
45
  extensions: []
@@ -55,6 +56,7 @@ files:
55
56
  - lib/jekyll-t4j/engine/katex.rb
56
57
  - lib/jekyll-t4j/merger.rb
57
58
  - lib/jekyll-t4j/renderer.rb
59
+ - lib/jekyll-t4j/snippet.rb
58
60
  - lib/jekyll-t4j/version.rb
59
61
  homepage: https://github.com/crow02531/jekyll-t4j
60
62
  licenses:
@@ -76,7 +78,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
76
78
  version: '0'
77
79
  requirements:
78
80
  - A TeX distribution
79
- rubygems_version: 3.3.7
81
+ rubygems_version: 3.3.26
80
82
  signing_key:
81
83
  specification_version: 4
82
84
  summary: LaTeX support for Jekyll.