pandocfilters 0.0.1.alpha
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 +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: []
|