jekyll-t4j 0.4.0 → 0.5.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: 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.