pandocfilters 0.0.1.alpha
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/pandocfilters.rb +115 -0
- data/lib/pandocfilters/core_ext/string.rb +11 -0
- data/lib/pandocfilters/filters/blockquote_cite +24 -0
- data/lib/pandocfilters/filters/dedication +39 -0
- data/lib/pandocfilters/filters/page_break +16 -0
- data/lib/pandocfilters/node.rb +80 -0
- data/lib/pandocfilters/version.rb +3 -0
- metadata +65 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d03820dd1432a65258ce231a8c33965755bd6d7e
|
4
|
+
data.tar.gz: 3539e051a195ada06037daa752569b618033e725
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: babf25b565f5fcb466f38afcb7baeecfc81c18a24f48c6fc473350803ddb2f028ea1d78ffdc81b981a4e16fdd1462aaf2423aa554ffeeb7654da91bacbe8d11c
|
7
|
+
data.tar.gz: 0da641977ceace8e8802afddb0a57bc8a99472188045b13b05d70da9b89a7670b4aee29c0616dd90b1a0d48f4dcef6486c8bd254127a477abdfb9fd53e8c03de
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'json'
|
2
|
+
require_relative 'pandocfilters/node'
|
3
|
+
|
4
|
+
module PandocFilters
|
5
|
+
|
6
|
+
# Fetch prebuild filter.
|
7
|
+
#
|
8
|
+
# @param name [String] Filter name.
|
9
|
+
# @return [String] Path to the filter.
|
10
|
+
def self.filter(name)
|
11
|
+
glob = File.expand_path('../pandocfilters/filters/*' ,__FILE__)
|
12
|
+
filters = Dir.glob(glob).map { |f| File.basename(f) }
|
13
|
+
|
14
|
+
raise "No build-in filter names #{name}" unless filters.include?(name)
|
15
|
+
|
16
|
+
File.expand_path("../pandocfilters/filters/#{name}" ,__FILE__)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Converts an action into a filter that reads a JSON-formatted
|
20
|
+
# pandoc document from stdin, transforms it by walking the tree
|
21
|
+
# with the action, and returns a new JSON-formatted pandoc document
|
22
|
+
# to stdout. The argument is a function action(key, value, format, meta),
|
23
|
+
# where key is the type of the pandoc object (e.g. 'Str', 'Para'),
|
24
|
+
# value is the contents of the object (e.g. a string for 'Str',
|
25
|
+
# a list of inline elements for 'Para'), format is the target
|
26
|
+
# output format (which will be taken for the first command line
|
27
|
+
# argument if present), and meta is the document's metadata.
|
28
|
+
# If the function returns None, the object to which it applies
|
29
|
+
# will remain unchanged. If it returns an object, the object will
|
30
|
+
# be replaced. If it returns a list, the list will be spliced in to
|
31
|
+
# the list to which the target object belongs. (So, returning an
|
32
|
+
# empty list deletes the object.)
|
33
|
+
#
|
34
|
+
# action Callable object
|
35
|
+
#
|
36
|
+
# Return Manuplated JSON
|
37
|
+
def self.process(&action)
|
38
|
+
doc = JSON.load($stdin.read)
|
39
|
+
if ARGV.size > 1
|
40
|
+
format = ARGV[1]
|
41
|
+
else
|
42
|
+
format = ""
|
43
|
+
end
|
44
|
+
altered = self.walk(doc, format, doc[0]['unMeta'], &action)
|
45
|
+
JSON.dump(altered, $stdout)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Walks the tree x and returns concatenated string content,
|
49
|
+
# leaving out all formatting.
|
50
|
+
def self.stringify(x)
|
51
|
+
result = []
|
52
|
+
|
53
|
+
go = lambda do |key, val, format, meta|
|
54
|
+
if ['Str', 'MetaString'].include? key
|
55
|
+
result.push(val)
|
56
|
+
elsif key == 'Code'
|
57
|
+
result.push(val[1])
|
58
|
+
elsif key == 'Math'
|
59
|
+
result.push(val[1])
|
60
|
+
elsif key == 'LineBreak'
|
61
|
+
result.push(" ")
|
62
|
+
elsif key == 'Space'
|
63
|
+
result.push(" ")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
self.walk(x, "", {}, &go)
|
68
|
+
|
69
|
+
result.join('')
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns an attribute list, constructed from the
|
73
|
+
# dictionary attrs.
|
74
|
+
def attributes(attrs)
|
75
|
+
attrs ||= {}
|
76
|
+
ident = attrs.fetch('id', '')
|
77
|
+
classes = attrs.fetch("classes", [])
|
78
|
+
keyvals = []
|
79
|
+
attrs.keep_if { |k, v| k != "classes" && k != "id" }.each do |k, v|
|
80
|
+
keyvals << [k, v]
|
81
|
+
end
|
82
|
+
|
83
|
+
[ident, classes, keyvals]
|
84
|
+
end
|
85
|
+
|
86
|
+
# Walk a tree, applying an action to every object.
|
87
|
+
# Returns a modified tree.
|
88
|
+
def self.walk(x, format, meta, &action)
|
89
|
+
if x.is_a? Array
|
90
|
+
array = []
|
91
|
+
x.each do |item|
|
92
|
+
if item.is_a?(Hash) && item.has_key?('t')
|
93
|
+
res = action.call(item['t'], item['c'], format, meta)
|
94
|
+
if res.nil?
|
95
|
+
array.push(self.walk(item, format, meta, &action))
|
96
|
+
elsif res.is_a? Array
|
97
|
+
res.each { |z| array.push(self.walk(z, format, meta, &action)) }
|
98
|
+
else
|
99
|
+
array.push(self.walk(res, format, meta, &action))
|
100
|
+
end
|
101
|
+
else
|
102
|
+
array.push(self.walk(item, format, meta, &action))
|
103
|
+
end
|
104
|
+
end
|
105
|
+
return array
|
106
|
+
elsif x.is_a? Hash
|
107
|
+
hash = {}
|
108
|
+
x.each { |k, _| hash[k] = self.walk(x[k], format, meta, &action) }
|
109
|
+
return hash
|
110
|
+
else
|
111
|
+
return x
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Paragraph starts with `CITE:' act like blockquote cite.
|
4
|
+
# Align the cite to right.
|
5
|
+
|
6
|
+
require 'pandocfilters'
|
7
|
+
|
8
|
+
filter = lambda do |key, value, format, meta|
|
9
|
+
if key == 'Para' && (cite = PandocFilters.stringify(value)).start_with?('CITE:')
|
10
|
+
cite.sub!(/CITE:\s?/, '')
|
11
|
+
xml = %(<w:p>
|
12
|
+
<w:pPr>
|
13
|
+
<w:pStyle w:val="BlockquoteCite"/>
|
14
|
+
</w:pPr>
|
15
|
+
<w:r>
|
16
|
+
<w:t xml:space="preserve">#{cite}</w:t>
|
17
|
+
</w:r>
|
18
|
+
</w:p>)
|
19
|
+
|
20
|
+
return PandocFilters::Node.raw_block('openxml', xml)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
PandocFilters.process &filter
|
@@ -0,0 +1,39 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Identify paragraph starts with `DEDICATION:' as dedication.
|
4
|
+
# Then replace the dedication with raw OOXML xml, and trip off the `DEDICATION:'.
|
5
|
+
# THe dedication is placed on a new page, and center aligned.
|
6
|
+
|
7
|
+
require 'pandocfilters'
|
8
|
+
|
9
|
+
filter = lambda do |key, value, format, meta|
|
10
|
+
if key == 'Para'
|
11
|
+
if value[0]['c'] == 'DEDICATION:'
|
12
|
+
dedication = PandocFilters.stringify(value).sub(/DEDICATION:\s?/, '')
|
13
|
+
xml = %(<w:p>
|
14
|
+
<w:pPr>
|
15
|
+
<w:pStyle w:val="DedicationText"/>
|
16
|
+
</w:pPr>
|
17
|
+
<w:r>
|
18
|
+
<w:t xml:space="preserve">#{dedication}</w:t>
|
19
|
+
</w:r>
|
20
|
+
</w:p>)
|
21
|
+
|
22
|
+
PandocFilters::Node.raw_block('openxml', xml)
|
23
|
+
elsif value[0]['c'] == 'DEDICATION_FIRST:'
|
24
|
+
dedication = PandocFilters.stringify(value).sub(/DEDICATION_FIRST:\s?/, '')
|
25
|
+
xml = %(<w:p>
|
26
|
+
<w:pPr>
|
27
|
+
<w:pStyle w:val="DedicationTextFirst"/>
|
28
|
+
</w:pPr>
|
29
|
+
<w:r>
|
30
|
+
<w:t xml:space="preserve">#{dedication}</w:t>
|
31
|
+
</w:r>
|
32
|
+
</w:p>)
|
33
|
+
|
34
|
+
PandocFilters::Node.raw_block('openxml', xml)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
PandocFilters.process &filter
|
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Identify paragraph contains `<!--PAGEBREAK-->' as page break in docx.
|
4
|
+
# Then replace the page break with raw OOXML xml'.
|
5
|
+
|
6
|
+
require 'pandocfilters'
|
7
|
+
|
8
|
+
filter = lambda do |key, value, format, meta|
|
9
|
+
if key == 'RawBlock' && value[1] == '<!--PAGEBREAK-->'
|
10
|
+
xml = %(<w:p><w:r><w:br w:type="page"/></w:r></w:p>)
|
11
|
+
|
12
|
+
return PandocFilters::Node.raw_block('openxml', xml)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
PandocFilters.process &filter
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require_relative 'core_ext/string'
|
2
|
+
|
3
|
+
module PandocFilters
|
4
|
+
class Node
|
5
|
+
# Pandoc build-in node types
|
6
|
+
# see http://hackage.haskell.org/package/pandoc-types-1.12.4.3/docs/Text-Pandoc-Definition.html
|
7
|
+
#
|
8
|
+
# key: node type
|
9
|
+
# value: expected arguments number
|
10
|
+
NODES = {
|
11
|
+
# block elements
|
12
|
+
plain: 1,
|
13
|
+
para: 1,
|
14
|
+
code_block: 2,
|
15
|
+
raw_block: 2,
|
16
|
+
block_quote: 1,
|
17
|
+
ordered_list: 2,
|
18
|
+
bullet_list: 1,
|
19
|
+
definition_list: 1,
|
20
|
+
header: 3,
|
21
|
+
horizontal_rule: 0,
|
22
|
+
table: 5,
|
23
|
+
div: 2,
|
24
|
+
null: 0,
|
25
|
+
|
26
|
+
# inline elements
|
27
|
+
str: 1,
|
28
|
+
emph: 1,
|
29
|
+
strong: 1,
|
30
|
+
strikeout: 1,
|
31
|
+
superscript: 1,
|
32
|
+
subscript: 1,
|
33
|
+
small_caps: 1,
|
34
|
+
quoted: 2,
|
35
|
+
cite: 2,
|
36
|
+
code: 2,
|
37
|
+
space: 0,
|
38
|
+
line_break: 0,
|
39
|
+
math: 2,
|
40
|
+
raw_inline: 2,
|
41
|
+
link: 2,
|
42
|
+
image: 2,
|
43
|
+
note: 1,
|
44
|
+
span: 2
|
45
|
+
}
|
46
|
+
|
47
|
+
class << self
|
48
|
+
def method_missing(name, *args)
|
49
|
+
raise "undefined #{name} node type" unless NODES.keys.include?(name)
|
50
|
+
unless args.size == NODES[name]
|
51
|
+
raise "#{name} expects #{NODES[name]} arguments, but given #{args.size}"
|
52
|
+
end
|
53
|
+
|
54
|
+
new(name.to_s.camelize, *args).to_hash
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
attr_reader :type
|
59
|
+
attr_reader :args
|
60
|
+
|
61
|
+
def initialize(type, *args)
|
62
|
+
@type = type
|
63
|
+
@args = args
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_hash
|
67
|
+
xs = case args.size
|
68
|
+
when 0
|
69
|
+
[]
|
70
|
+
when 1
|
71
|
+
args[0]
|
72
|
+
else
|
73
|
+
args
|
74
|
+
end
|
75
|
+
|
76
|
+
{'t': type, 'c': xs}
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
metadata
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pandocfilters
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1.alpha
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andor Chen
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-06-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 10.4.2
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 10.4.2
|
27
|
+
description: A Ruby gem for writing pandoc filters.
|
28
|
+
email: andor.chen.27@gmail.com
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- lib/pandocfilters.rb
|
34
|
+
- lib/pandocfilters/core_ext/string.rb
|
35
|
+
- lib/pandocfilters/filters/blockquote_cite
|
36
|
+
- lib/pandocfilters/filters/dedication
|
37
|
+
- lib/pandocfilters/filters/page_break
|
38
|
+
- lib/pandocfilters/node.rb
|
39
|
+
- lib/pandocfilters/version.rb
|
40
|
+
homepage: https://github.com/andorchen/pandocfilters.rb
|
41
|
+
licenses:
|
42
|
+
- MIT
|
43
|
+
metadata: {}
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options: []
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 1.9.3
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">"
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: 1.3.1
|
58
|
+
requirements:
|
59
|
+
- pandoc >= 1.14
|
60
|
+
rubyforge_project:
|
61
|
+
rubygems_version: 2.4.5
|
62
|
+
signing_key:
|
63
|
+
specification_version: 2
|
64
|
+
summary: A Ruby gem for writing pandoc filters.
|
65
|
+
test_files: []
|