artex 2.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +3 -0
- data/Manifest +25 -0
- data/README.rdoc +46 -0
- data/Rakefile +22 -0
- data/artex.gemspec +37 -0
- data/init.rb +1 -0
- data/lib/artex.rb +31 -0
- data/lib/artex/document.rb +175 -0
- data/lib/artex/escaping.rb +28 -0
- data/lib/artex/framework/merb.rb +13 -0
- data/lib/artex/framework/rails.rb +28 -0
- data/lib/artex/pdf.rb +81 -0
- data/lib/artex/tempdir.rb +52 -0
- data/lib/artex/version.rb +77 -0
- data/rails/init.rb +1 -0
- data/test/document_test.rb +86 -0
- data/test/filter_test.rb +24 -0
- data/test/fixtures/first.tex +50 -0
- data/test/fixtures/first.tex.erb +48 -0
- data/test/fixtures/fragment.tex.erb +1 -0
- data/test/fixtures/text.textile +4 -0
- data/test/tempdir_test.rb +67 -0
- data/test/test_helper.rb +25 -0
- data/vendor/instiki/LICENSE +3 -0
- data/vendor/instiki/redcloth_for_tex.rb +736 -0
- metadata +115 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module ArTeX
|
4
|
+
|
5
|
+
class Tempdir #:nodoc:
|
6
|
+
|
7
|
+
def self.open(parent_path=ArTeX::Document.options[:tempdir])
|
8
|
+
tempdir = new(parent_path)
|
9
|
+
FileUtils.mkdir_p tempdir.path
|
10
|
+
result = Dir.chdir(tempdir.path) do
|
11
|
+
yield tempdir
|
12
|
+
end
|
13
|
+
# We don't remove the temporary directory when exceptions occur,
|
14
|
+
# so that the source of the exception can be dubbed (logfile kept)
|
15
|
+
tempdir.remove!
|
16
|
+
result
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(parent_path, basename='artex')
|
20
|
+
@parent_path = parent_path
|
21
|
+
@basename = basename
|
22
|
+
@removed = false
|
23
|
+
end
|
24
|
+
|
25
|
+
def path
|
26
|
+
@path ||= File.expand_path(File.join(@parent_path, 'artex', "#{@basename}-#{uuid}"))
|
27
|
+
end
|
28
|
+
|
29
|
+
def remove!
|
30
|
+
return false if @removed
|
31
|
+
FileUtils.rm_rf path
|
32
|
+
@removed = true
|
33
|
+
end
|
34
|
+
|
35
|
+
#######
|
36
|
+
private
|
37
|
+
#######
|
38
|
+
|
39
|
+
# Try using uuidgen, but if that doesn't work drop down to
|
40
|
+
# a poor-man's UUID; timestamp, thread & object hashes
|
41
|
+
# Note: I don't want to add any dependencies (so no UUID library)
|
42
|
+
def uuid
|
43
|
+
if (result = `uuidgen`.strip rescue nil).empty?
|
44
|
+
"#{Time.now.to_i}-#{Thread.current.hash}-#{hash}"
|
45
|
+
else
|
46
|
+
result
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# (The MIT License)
|
2
|
+
#
|
3
|
+
# Copyright (c) 2008 Jamis Buck <jamis@37signals.com>,
|
4
|
+
# with modifications by Bruce Williams <bruce@codefluency.com>
|
5
|
+
#
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
7
|
+
# a copy of this software and associated documentation files (the
|
8
|
+
# 'Software'), to deal in the Software without restriction, including
|
9
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
10
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
11
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
12
|
+
# the following conditions:
|
13
|
+
#
|
14
|
+
# The above copyright notice and this permission notice shall be
|
15
|
+
# included in all copies or substantial portions of the Software.
|
16
|
+
#
|
17
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
19
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
20
|
+
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
21
|
+
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
22
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
23
|
+
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
24
|
+
module ArTeX
|
25
|
+
|
26
|
+
|
27
|
+
# A class for describing the current version of a library. The version
|
28
|
+
# consists of three parts: the +major+ number, the +minor+ number, and the
|
29
|
+
# +tiny+ (or +patch+) number.
|
30
|
+
class Version
|
31
|
+
|
32
|
+
# A convenience method for instantiating a new Version instance with the
|
33
|
+
# given +major+, +minor+, and +tiny+ components.
|
34
|
+
def self.[](major, minor, tiny)
|
35
|
+
new(major, minor, tiny)
|
36
|
+
end
|
37
|
+
|
38
|
+
attr_reader :major, :minor, :tiny
|
39
|
+
|
40
|
+
# Create a new Version object with the given components.
|
41
|
+
def initialize(major, minor, tiny)
|
42
|
+
@major, @minor, @tiny = major, minor, tiny
|
43
|
+
end
|
44
|
+
|
45
|
+
# Compare this version to the given +version+ object.
|
46
|
+
def <=>(version)
|
47
|
+
to_i <=> version.to_i
|
48
|
+
end
|
49
|
+
|
50
|
+
# Converts this version object to a string, where each of the three
|
51
|
+
# version components are joined by the '.' character. E.g., 2.0.0.
|
52
|
+
def to_s
|
53
|
+
@to_s ||= [@major, @minor, @tiny].join(".")
|
54
|
+
end
|
55
|
+
|
56
|
+
# Converts this version to a canonical integer that may be compared
|
57
|
+
# against other version objects.
|
58
|
+
def to_i
|
59
|
+
@to_i ||= @major * 1_000_000 + @minor * 1_000 + @tiny
|
60
|
+
end
|
61
|
+
|
62
|
+
def to_a
|
63
|
+
[@major, @minor, @tiny]
|
64
|
+
end
|
65
|
+
|
66
|
+
MAJOR = 2
|
67
|
+
MINOR = 1
|
68
|
+
TINY = 1
|
69
|
+
|
70
|
+
# The current version as a Version instance
|
71
|
+
CURRENT = new(MAJOR, MINOR, TINY)
|
72
|
+
# The current version as a String
|
73
|
+
STRING = CURRENT.to_s
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'artex'
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require File.dirname(__FILE__) << '/test_helper'
|
2
|
+
|
3
|
+
class DocumentTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "Document Generation" do
|
6
|
+
|
7
|
+
setup do
|
8
|
+
change_tmpdir_for_testing
|
9
|
+
end
|
10
|
+
|
11
|
+
should "have a to_pdf method" do
|
12
|
+
assert document(:first).respond_to?(:to_pdf)
|
13
|
+
end
|
14
|
+
|
15
|
+
context "when escaping" do
|
16
|
+
setup do
|
17
|
+
@obj = Object.new
|
18
|
+
def @obj.to_s
|
19
|
+
'\~'
|
20
|
+
end
|
21
|
+
@escaped = '\textbackslash{}\textasciitilde{}'
|
22
|
+
end
|
23
|
+
should "escape character" do
|
24
|
+
assert_equal @escaped, ArTeX::Document.escape(@obj.to_s)
|
25
|
+
end
|
26
|
+
should "convert argument to string before attempting escape" do
|
27
|
+
assert_equal @escaped, ArTeX::Document.escape(@obj)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
should "use a to_pdf block to move a file to a relative path" do
|
32
|
+
begin
|
33
|
+
path = File.expand_path(File.dirname(__FILE__) << '/tmp/this_is_relative_to_pwd.pdf')
|
34
|
+
document(:first).to_pdf do |filename|
|
35
|
+
assert_nothing_raised do
|
36
|
+
FileUtils.move filename, path
|
37
|
+
end
|
38
|
+
assert File.exists?(path)
|
39
|
+
end
|
40
|
+
ensure
|
41
|
+
FileUtils.rm path rescue nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
should "generate PDF and return as a string" do
|
46
|
+
@author = 'Foo'
|
47
|
+
assert_equal '%PDF', document(:first).to_pdf(binding)[0, 4]
|
48
|
+
end
|
49
|
+
|
50
|
+
should "generate TeX source and return as a string with debug option" do
|
51
|
+
@author = 'Foo'
|
52
|
+
assert_not_equal '%PDF', document(:first, :tex => true).to_pdf(binding)[0, 4]
|
53
|
+
end
|
54
|
+
|
55
|
+
should "generate PDF and give access to file directly" do
|
56
|
+
@author = 'Foo'
|
57
|
+
data_read = nil
|
58
|
+
invocation_result = document(:first).to_pdf(binding) do |filename|
|
59
|
+
data_read = File.open(filename, 'rb') { |f| f.read }
|
60
|
+
:not_the_file_contents
|
61
|
+
end
|
62
|
+
assert_equal '%PDF', data_read[0, 4]
|
63
|
+
assert_equal :not_the_file_contents, invocation_result
|
64
|
+
end
|
65
|
+
|
66
|
+
should "generate TeX source and give access to file directly" do
|
67
|
+
@author = 'Foo'
|
68
|
+
data_read = nil
|
69
|
+
invocation_result = document(:first, :tex => true).to_pdf(binding) do |filename|
|
70
|
+
data_read = File.open(filename, 'rb') { |f| f.read }
|
71
|
+
:not_the_file_contents
|
72
|
+
end
|
73
|
+
assert_not_equal '%PDF', data_read[0, 4]
|
74
|
+
assert_equal :not_the_file_contents, invocation_result
|
75
|
+
end
|
76
|
+
|
77
|
+
should "wrap in a layout using `yield'" do
|
78
|
+
doc = document(:fragment, :layout => 'testing_layout[<%= yield %>]')
|
79
|
+
@name = 'ERB'
|
80
|
+
source = doc.source(binding)
|
81
|
+
assert source =~ /^testing_layout.*?ERB, Fragmented/
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
data/test/filter_test.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.dirname(__FILE__) << '/test_helper'
|
2
|
+
|
3
|
+
class FilterTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "Filtering Documents" do
|
6
|
+
|
7
|
+
should "filter through textile" do
|
8
|
+
doc = document('text.textile', :filter => 'textile')
|
9
|
+
source = doc.source(binding)
|
10
|
+
assert source.include?('\item')
|
11
|
+
end
|
12
|
+
|
13
|
+
should "not affect layouts" do
|
14
|
+
doc = document('text.textile',
|
15
|
+
:filter => 'textile',
|
16
|
+
:layout => "* layout\n* is\n<%= yield %>")
|
17
|
+
source = doc.source(binding)
|
18
|
+
assert source.include?("* layout"), "filtered layout"
|
19
|
+
assert source.include?('\item'), "didn't filter content"
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
% This percent indicates a comment.
|
2
|
+
% This is a very simple latex article that introduces the way
|
3
|
+
% equations are typeset. Do this in linux:
|
4
|
+
%
|
5
|
+
% latex first.tex
|
6
|
+
% latex first.tex
|
7
|
+
% xdvi first.dvi
|
8
|
+
% dvips -o first.ps first.dvi
|
9
|
+
% gv first.ps
|
10
|
+
% lpr first.ps
|
11
|
+
% pdflatex first.tex
|
12
|
+
% acroread first.pdf
|
13
|
+
\documentclass[12pt]{article}
|
14
|
+
\title{First \LaTeX}
|
15
|
+
\author{Joe Student}
|
16
|
+
\date{\today}
|
17
|
+
|
18
|
+
\begin{document}
|
19
|
+
|
20
|
+
\maketitle
|
21
|
+
|
22
|
+
\abstract{This is a very simple example of using \LaTeX\ for typesetting.
|
23
|
+
The procedure for typesetting equations is introduced.}
|
24
|
+
\begin{sdsd}
|
25
|
+
\end{bar}
|
26
|
+
|
27
|
+
\section{A few equations}
|
28
|
+
Equations can be typeset inline like so: $\vec{F}=m\vec{a}$ .
|
29
|
+
Equations can also be separated form the text: $$ \vec{F}=m\vec{a} \ . $$
|
30
|
+
Notice the punctuation, the ``.'', had to be included with the equation.
|
31
|
+
Equations can also have a number assigned and a label attached,
|
32
|
+
as in the following:
|
33
|
+
\begin{equation}
|
34
|
+
\frac{D\vec{\omega}}{Dt} =
|
35
|
+
( \vec{\omega} + \vec{\Omega}) \cdot \nabla \vec{U}
|
36
|
+
+ \frac{1}{\rho^2} \nabla \rho \times \nabla p
|
37
|
+
+ \nu \nabla^2 \vec{\omega}
|
38
|
+
\label{vorteq}
|
39
|
+
\end{equation}
|
40
|
+
The vorticity equation (\ref{vorteq}) is referenced by label,
|
41
|
+
not by number. The numbers may change as the document grows and
|
42
|
+
more equations are added.
|
43
|
+
|
44
|
+
New paragraphs are indicated with a blank line in the source code.
|
45
|
+
|
46
|
+
\section{The End}
|
47
|
+
|
48
|
+
Further examples of \LaTeX\ can be found at {\tt it.metr.ou.edu}
|
49
|
+
|
50
|
+
\end{document}
|
@@ -0,0 +1,48 @@
|
|
1
|
+
% This percent indicates a comment.
|
2
|
+
% This is a very simple latex article that introduces the way
|
3
|
+
% equations are typeset. Do this in linux:
|
4
|
+
%
|
5
|
+
% latex first.tex
|
6
|
+
% latex first.tex
|
7
|
+
% xdvi first.dvi
|
8
|
+
% dvips -o first.ps first.dvi
|
9
|
+
% gv first.ps
|
10
|
+
% lpr first.ps
|
11
|
+
% pdflatex first.tex
|
12
|
+
% acroread first.pdf
|
13
|
+
\documentclass[12pt]{article}
|
14
|
+
\title{First \LaTeX}
|
15
|
+
\author{<%= @author %>}
|
16
|
+
\date{\today}
|
17
|
+
|
18
|
+
\begin{document}
|
19
|
+
|
20
|
+
\maketitle
|
21
|
+
|
22
|
+
\abstract{This is a very simple example of using \LaTeX\ for typesetting.
|
23
|
+
The procedure for typesetting equations is introduced.}
|
24
|
+
|
25
|
+
\section{A few equations}
|
26
|
+
Equations can be typeset inline like so: $\vec{F}=m\vec{a}$ .
|
27
|
+
Equations can also be separated form the text: $$ \vec{F}=m\vec{a} \ . $$
|
28
|
+
Notice the punctuation, the ``.'', had to be included with the equation.
|
29
|
+
Equations can also have a number assigned and a label attached,
|
30
|
+
as in the following:
|
31
|
+
\begin{equation}
|
32
|
+
\frac{D\vec{\omega}}{Dt} =
|
33
|
+
( \vec{\omega} + \vec{\Omega}) \cdot \nabla \vec{U}
|
34
|
+
+ \frac{1}{\rho^2} \nabla \rho \times \nabla p
|
35
|
+
+ \nu \nabla^2 \vec{\omega}
|
36
|
+
\label{vorteq}
|
37
|
+
\end{equation}
|
38
|
+
The vorticity equation (\ref{vorteq}) is referenced by label,
|
39
|
+
not by number. The numbers may change as the document grows and
|
40
|
+
more equations are added.
|
41
|
+
|
42
|
+
New paragraphs are indicated with a blank line in the source code.
|
43
|
+
|
44
|
+
\section{The End}
|
45
|
+
|
46
|
+
Further examples of \LaTeX\ can be found at {\tt it.metr.ou.edu}
|
47
|
+
|
48
|
+
\end{document}
|
@@ -0,0 +1 @@
|
|
1
|
+
\par <%= @name %>, Fragmented
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require File.dirname(__FILE__) << '/test_helper'
|
2
|
+
|
3
|
+
class TempdirTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "Creating a temporary directory" do
|
6
|
+
|
7
|
+
setup do
|
8
|
+
change_tmpdir_for_testing
|
9
|
+
end
|
10
|
+
|
11
|
+
should "change directory" do
|
12
|
+
old_location = Dir.pwd
|
13
|
+
block_location = nil
|
14
|
+
ArTeX::Tempdir.open do
|
15
|
+
assert_not_equal old_location, Dir.pwd
|
16
|
+
block_location = Dir.pwd
|
17
|
+
end
|
18
|
+
assert_equal old_location, Dir.pwd
|
19
|
+
assert !File.exists?(block_location)
|
20
|
+
end
|
21
|
+
|
22
|
+
should "use a 'artex' name prefix" do
|
23
|
+
ArTeX::Tempdir.open do
|
24
|
+
assert_equal 'artex', File.basename(Dir.pwd)[0,5]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
should "remove the directory after use if no exception occurs by default" do
|
29
|
+
path = nil
|
30
|
+
ArTeX::Tempdir.open do
|
31
|
+
path = Dir.pwd
|
32
|
+
assert File.exists?(path)
|
33
|
+
end
|
34
|
+
assert !File.exists?(path)
|
35
|
+
end
|
36
|
+
|
37
|
+
should "return the result of the last statement if automatically removing the directory" do
|
38
|
+
result = ArTeX::Tempdir.open do
|
39
|
+
:last
|
40
|
+
end
|
41
|
+
assert_equal :last, :last
|
42
|
+
end
|
43
|
+
|
44
|
+
should "return the result of the last statment if not automatically removing the directory" do
|
45
|
+
tempdir = nil # to capture value
|
46
|
+
result = ArTeX::Tempdir.open do |tempdir|
|
47
|
+
:last
|
48
|
+
end
|
49
|
+
tempdir.remove!
|
50
|
+
assert_equal :last, :last
|
51
|
+
end
|
52
|
+
|
53
|
+
should "not remove the directory after use if an exception occurs" do
|
54
|
+
path = nil
|
55
|
+
assert_raises RuntimeError do
|
56
|
+
ArTeX::Tempdir.open do
|
57
|
+
path = Dir.pwd
|
58
|
+
assert File.directory?(path)
|
59
|
+
raise "Test exception!"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
assert File.directory?(path)
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
begin
|
5
|
+
require 'shoulda'
|
6
|
+
require 'flexmock/test_unit'
|
7
|
+
rescue LoadError
|
8
|
+
abort "the `Shoulda' and `flexmock' gems are required for testing"
|
9
|
+
end
|
10
|
+
|
11
|
+
require File.dirname(__FILE__) << '/../lib/artex'
|
12
|
+
|
13
|
+
class Test::Unit::TestCase
|
14
|
+
|
15
|
+
def change_tmpdir_for_testing
|
16
|
+
flexmock(Dir).should_receive(:tmpdir).and_return(File.dirname(__FILE__) << '/tmp')
|
17
|
+
end
|
18
|
+
|
19
|
+
def document(name, options={})
|
20
|
+
name = name.kind_of?(Symbol) ? "#{name}.tex.erb" : name
|
21
|
+
template = File.read(File.dirname(__FILE__) << "/fixtures/#{name}")
|
22
|
+
ArTeX::Document.new(template, options)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|