├── .travis.yml
├── COPYING
├── _unittest
├── test.py
└── testdata
│ ├── CodeParser.test.md
│ ├── ConstantParser.test.md
│ ├── FigureParser.test.md
│ ├── IncludeParser.test.md
│ ├── TOCParser.test.md
│ ├── TableParser.test.md
│ └── includes
│ ├── PythonParser.test.md
│ ├── code.py
│ ├── figure.png
│ ├── include.txt
│ └── table.csv
├── academicmarkdown
├── HTMLFilter.py
├── MDFilter.py
├── _BaseParser.py
├── _CodeParser.py
├── _ConstantParser.py
├── _ExecParser.py
├── _FigureParser.py
├── _GitHubParser.py
├── _IncludeParser.py
├── _ODTFixer.py
├── _Pandoc.py
├── _PythonParser.py
├── _TOCParser.py
├── _TableParser.py
├── _VideoParser.py
├── _WcParser.py
├── _WkHtmlToPdf.py
├── _YAMLParser.py
├── _ZoteroParser.py
├── __init__.py
├── build.py
├── constants.py
├── git.py
├── py3compat.py
├── styles
│ ├── apa
│ │ ├── citation-style.csl
│ │ ├── reference.docx
│ │ ├── reference.odt
│ │ ├── stylesheet.css
│ │ └── template.html
│ └── modern
│ │ ├── citation-style.csl
│ │ ├── reference.docx
│ │ ├── reference.odt
│ │ ├── stylesheet.css
│ │ └── template.html
└── tools.py
├── debian
├── changelog
├── compat
├── control
├── copyright
└── rules
├── example
├── compile.py
├── example.html
├── example.pdf
└── src
│ ├── example.md
│ ├── foveal_acuity.png
│ └── gullvision.png
├── readme.md
├── readme.py
└── setup.py
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | python:
3 | - "2.7"
4 | - "3.4"
5 | install:
6 | - pip install pyyaml
7 | - python setup.py install
8 | script: _unittest/test.py
9 |
--------------------------------------------------------------------------------
/_unittest/test.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #-*- coding:utf-8 -*-
3 |
4 | """
5 | This file is part of pseudorandom.
6 |
7 | pseudorandom is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | pseudorandom is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with pseudorandom. If not, see .
19 | """
20 |
21 | import os
22 | import unittest
23 | import academicmarkdown
24 | from academicmarkdown.py3compat import *
25 | import importlib
26 |
27 | class AcadamicMarkdownTest(unittest.TestCase):
28 |
29 | """
30 | desc:
31 | Basic unit testing for academicmarkdown.
32 | """
33 |
34 | def setUp(self):
35 |
36 | academicmarkdown.build.path += [os.path.join(self.dataFolder(),
37 | u'includes')]
38 |
39 | def dataFolder(self):
40 |
41 | return os.path.join(os.path.dirname(__file__), u'testdata')
42 |
43 | def getTestData(self, fname):
44 |
45 | with open(os.path.join(self.dataFolder(), fname)) as fd:
46 | s = fd.read()
47 | return safe_decode(s)
48 |
49 | def singleTest(self, path):
50 |
51 | clsName = path[:-8]
52 | print(u'testing %s' % clsName)
53 | cls = getattr(academicmarkdown, clsName)
54 | md = self.getTestData(path)
55 | l = md.split(u'===')
56 | inp = l[0]
57 | predOut = l[1].strip()
58 | realOut = cls().parse(inp).strip()
59 | self.assertEqual(predOut, realOut)
60 |
61 | def test_all(self):
62 |
63 | for path in os.listdir(self.dataFolder()):
64 | if not path.endswith(u'.test.md'):
65 | continue
66 | self.singleTest(path)
67 |
68 | if __name__ == '__main__':
69 | unittest.main()
70 |
--------------------------------------------------------------------------------
/_unittest/testdata/CodeParser.test.md:
--------------------------------------------------------------------------------
1 | %--
2 | code:
3 | id: Test
4 | source: code.py
5 | syntax: python
6 | caption: |
7 | Test!
8 | --%
9 | ===
10 | ~~~ .python
11 | print('test')
12 | ~~~
13 |
--------------------------------------------------------------------------------
/_unittest/testdata/ConstantParser.test.md:
--------------------------------------------------------------------------------
1 | %--
2 | constant:
3 | test: "This is a test"
4 | --%
5 | %test
6 | ===
7 | This is a test
8 |
--------------------------------------------------------------------------------
/_unittest/testdata/FigureParser.test.md:
--------------------------------------------------------------------------------
1 | %--
2 | figure:
3 | id: test
4 | source: figure.png
5 | caption: |
6 | This is a test!
7 | --%
8 | ===
9 |
10 |
11 | Figure 1. This is a <b>test</b>!
12 |
13 |
--------------------------------------------------------------------------------
/_unittest/testdata/IncludeParser.test.md:
--------------------------------------------------------------------------------
1 | %--
2 | include: "include.txt"
3 | --%
4 | ===
5 | This is a test
6 |
--------------------------------------------------------------------------------
/_unittest/testdata/TOCParser.test.md:
--------------------------------------------------------------------------------
1 | %--
2 | toc:
3 | excludes: [Level 2A]
4 | mindepth: 2
5 | maxdepth: 4
6 | --%
7 |
8 | # Level 1
9 |
10 | ## Level 2A
11 |
12 | ## Level 2B
13 |
14 | ### Level 3A
15 |
16 | ## Level.2-é
17 | ===
18 | - [Level 2A](#level-2a)
19 | - [Level 2B](#level-2b)
20 | - [Level 3A](#level-3a)
21 | - [Level.2-é](#level2)
22 |
23 |
24 |
25 | # Level 1
26 |
27 | ## Level 2A
28 |
29 | ## Level 2B
30 |
31 | ### Level 3A
32 |
33 | ## Level.2-é
34 |
--------------------------------------------------------------------------------
/_unittest/testdata/TableParser.test.md:
--------------------------------------------------------------------------------
1 | %--
2 | table:
3 | id: test
4 | source: table.csv
5 | caption: |
6 | This is a test
7 | --%
8 | ===
9 | | A | B |
10 | | --: | --: |
11 | | 1.0000 | 2.0000 |
12 | | 3.0000 | 4.0000 |
13 |
14 |
15 | __Table 1.__ This is a test
16 |
17 | {: .tbl-caption #test}
18 |
--------------------------------------------------------------------------------
/_unittest/testdata/includes/PythonParser.test.md:
--------------------------------------------------------------------------------
1 | %--
2 | python: |
3 | print('This is a test')
4 | --%
5 | ===
6 | This is a test
7 |
--------------------------------------------------------------------------------
/_unittest/testdata/includes/code.py:
--------------------------------------------------------------------------------
1 | print('test')
2 |
--------------------------------------------------------------------------------
/_unittest/testdata/includes/figure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smathot/academicmarkdown/696592d035e341e170b6c3e86a0855a8cc4d0f29/_unittest/testdata/includes/figure.png
--------------------------------------------------------------------------------
/_unittest/testdata/includes/include.txt:
--------------------------------------------------------------------------------
1 | This is a test
2 |
--------------------------------------------------------------------------------
/_unittest/testdata/includes/table.csv:
--------------------------------------------------------------------------------
1 | A,B
2 | 1,2
3 | 3,4
4 |
--------------------------------------------------------------------------------
/academicmarkdown/HTMLFilter.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | This file is part of zoteromarkdown.
5 |
6 | zoteromarkdown is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | zoteromarkdown is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with zoteromarkdown. If not, see .
18 | """
19 |
20 | import re
21 |
22 | def citationGlue(s):
23 |
24 | """
25 | Glues citations together to allow sorting, like so:
26 |
27 | [@B]+[@A]
28 |
29 | This will put '@B' before '@A'.
30 |
31 | Returns:
32 | A unicode string with all citations glued.
33 | """
34 |
35 | regexp = \
36 | r'[\]\)]\+[\[\(]'
37 | for i in re.finditer(regexp, s, re.M):
38 | cite = i.group()
39 | s = s.replace(i.group(), u'')
40 | return s
41 |
42 | def DOI(s):
43 |
44 | """
45 | Creates hyperlinks from DOI references.
46 |
47 | Arguments:
48 | s -- A unicode string.
49 |
50 | Returns:
51 | A unicode string with all DOIs changed into hyperlinks.
52 | """
53 |
54 | regexp = r'(doi:10[.][0-9]{4,}[^\s"/<>]*/[^\s"<>]+)'
55 | for i in re.finditer(regexp, s, re.M):
56 | doi = i.group()
57 | s = s.replace(doi, u'%s' % \
58 | (doi[4:], doi))
59 | return s
60 |
61 | def headerIndent(s, depth=1, minLevel=1, maxLevel=6):
62 |
63 | """
64 | Makes all headers jump down one level. For example
becomes
, etc.
65 |
66 | Arguments:
67 | s -- A unicode string.
68 |
69 | Keyword arguments:
70 | depth -- The depth of the extra indentation. For example, a depth of
71 | 2 means that
becomes
' % i, u'' % (i+depth)) \
83 | .replace(u'' % i, u'' % (i+depth))
84 | return s
85 |
86 | def quote(s):
87 |
88 | s = s.replace('
—', '
―')
89 | s = s.replace(' \n—', ' \n―')
90 | return s
91 |
--------------------------------------------------------------------------------
/academicmarkdown/MDFilter.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | This file is part of zoteromarkdown.
5 |
6 | zoteromarkdown is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | zoteromarkdown is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with zoteromarkdown. If not, see .
18 | """
19 |
20 | import re
21 |
22 | def highlight(md):
23 |
24 | """
25 | desc:
26 | Processes the custom Markdown ++highlight++ syntax.
27 |
28 | arguments:
29 | md:
30 | desc: A Markdown string.
31 | type: unicode
32 |
33 | returns:
34 | desc: A processed Markdown string.
35 | type: unicode
36 | """
37 |
38 | regexp = r'\+\+(.*?)\+\+'
39 | for i in re.finditer(regexp, md, re.M):
40 | old = i.group(0)
41 | new = u'%s' % i.groups()[0]
42 | md = md.replace(old, new)
43 | return md
44 |
45 | def arrows(md):
46 |
47 | """
48 | desc:
49 | Converts -> and <- into arrows.
50 |
51 | arguments:
52 | md:
53 | desc: A Markdown string.
54 | type: unicode
55 |
56 | returns:
57 | desc: A processed Markdown string.
58 | type: unicode
59 | """
60 |
61 | md = md.replace(u' <- ', u' ← ')
62 | md = md.replace(u' -> ', u' → ')
63 | md = md.replace(u' \<- ', u' <- ')
64 | md = md.replace(u' \-> ', u' -> ')
65 | return md
66 |
67 | def autoItalics(md):
68 |
69 | """
70 | Automatically italicizes certain expressions. For example, 'p = .05',
71 | becomes '*p* = .05'.
72 |
73 | Arguments:
74 | md -- A Markdown string.
75 |
76 | Returns:
77 | A processed Markdown string.
78 | """
79 |
80 | # M, SE, SD, p, r, t
81 | regexp = r'\b(?P(M|p|r|SE|SD|t|β|z)) *(?P[=><]) *(?P-?\d*\.?\d*)\b'
82 | for i in re.finditer(regexp, md, re.M):
83 | old = i.group(0)
84 | new = u'*%s* %s %s' % (i.group('key'), i.group('opr'), \
85 | i.group('val'))
86 | md = md.replace(old, new)
87 |
88 | # T tests with degrees of freedom
89 | regexp = r'\bt\((?P\d*\.?\d*)\) *(?P[=><]) *(?P-?\d*\.?\d*)\b'
90 | for i in re.finditer(regexp, md, re.M):
91 | old = i.group(0)
92 | new = u'*t*(%s) %s %s' % (i.group('df'), i.group('opr'), \
93 | i.group('val'))
94 | md = md.replace(old, new)
95 |
96 | # Chisquare tests with degrees of freedom
97 | regexp = r'\bX2\((?P\d*\.?\d*)\) *(?P[=><]) *(?P-?\d*\.?\d*)\b'
98 | for i in re.finditer(regexp, md, re.M):
99 | old = i.group(0)
100 | new = u'*Χ^2^*(%s) %s %s' % (i.group('df'), i.group('opr'), \
101 | i.group('val'))
102 | md = md.replace(old, new)
103 |
104 | # F tests
105 | regexp = r'\bF\((?P\d*\.?\d*),(?P\d*\.?\d*)\) *(?P[=><]) *(?P-?\d*\.?\d*)\b'
106 | for i in re.finditer(regexp, md, re.M):
107 | old = i.group(0)
108 | new = u'*F*(%s,%s) %s %s' % (i.group('df1'), i.group('df2'), \
109 | i.group('opr'), i.group('val'))
110 | md = md.replace(old, new)
111 |
112 | return md
113 |
114 | def magicVars(md):
115 |
116 | """
117 | Replace magic variables, such as %wc%.
118 |
119 | Arguments:
120 | md -- A Markdown string.
121 |
122 | Returns:
123 | A processed Markdown string.
124 | """
125 |
126 | md = md.replace(u'%wc%', u'%s' % len(md.split()))
127 | md = md.replace(u'%cc%', u'%s' % len(md))
128 | return md
129 |
130 | def pageBreak(md):
131 |
132 | """
133 | Converts '~' paragraphs to HTML5 page breaks.
134 |
135 | Arguments:
136 | md -- A Markdown string.
137 |
138 | Returns:
139 | A processed Markdown string.
140 | """
141 |
142 | return md.replace(u'\n~\n', \
143 | u'\n\n')
144 |
145 | def quote(md):
146 |
147 | return md.replace(u'\n— ', u' \n— ')
148 |
--------------------------------------------------------------------------------
/academicmarkdown/_BaseParser.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | This file is part of zoteromarkdown.
5 |
6 | zoteromarkdown is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | zoteromarkdown is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with zoteromarkdown. If not, see .
18 | """
19 |
20 | import re
21 | import yaml
22 | from academicmarkdown.py3compat import *
23 |
24 | class BaseParser(object):
25 |
26 | def __init__(self, verbose=False):
27 |
28 | """
29 | Constructor.
30 |
31 | Keyword arguments:
32 | verbose -- Indicates whether verbose output should be generated.
33 | (default=False)
34 | """
35 |
36 | self.verbose = verbose
37 |
38 | def parse(self, md):
39 |
40 | """
41 | Parses a MarkDown text.
42 |
43 | Arguments:
44 | md -- The Markdown text.
45 |
46 | Returns:
47 | The parsed Markdown text.
48 | """
49 |
50 | raise Exception(u'BaseParser.parse() should be overridden.')
51 |
52 | def msg(self, msg):
53 |
54 | """
55 | Print output in verbose mode.
56 |
57 | Arguments:
58 | msg -- The message to print.
59 | """
60 |
61 | if self.verbose:
62 | print(safe_encode(u'[%s] %s' % (self.__class__.__name__, msg),
63 | enc=u'ascii', errors=u'ignore'))
64 |
65 | def getPath(self, path):
66 |
67 | """
68 | Checks whether a path is present in the `srcFolder` and if so fixes it.
69 | URLs are accepted as valid paths.
70 |
71 | Arguments:
72 | path -- A path.
73 |
74 | Returns:
75 | A path.
76 | """
77 |
78 | import os
79 | from academicmarkdown import build
80 | if path.startswith(u'http://'):
81 | return path
82 | for buildPath in build.path:
83 | _path = os.path.join(buildPath, path)
84 | if os.path.exists(_path):
85 | return _path
86 | if not os.path.exists(path):
87 | raise Exception(u'Cannot find file %s' % path)
88 | return path
89 |
--------------------------------------------------------------------------------
/academicmarkdown/_CodeParser.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | This file is part of zoteromarkdown.
5 |
6 | zoteromarkdown is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | zoteromarkdown is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with zoteromarkdown. If not, see .
18 | """
19 |
20 | import yaml
21 | from academicmarkdown import YAMLParser
22 | import subprocess
23 |
24 | codeTemplate = {
25 | u'pandoc' : u"""
26 | ~~~ {%(syntax)s}
27 | %(code)s
28 | ~~~
29 | """,
30 | u'kramdown' : u"""
31 | ~~~ %(syntax)s
32 | %(code)s
33 | ~~~
34 | """,
35 | u'jekyll' : u"""
36 | {%% highlight %(syntax)s %%}
37 | %(code)s
38 | {%% endhighlight %%}
39 |
40 | __Listing %(nCode)d.__ %(caption)s\n{: .lst-caption #%(id)s}
41 | """}
42 |
43 | class CodeParser(YAMLParser):
44 |
45 | """
46 | The `code` blocks embeds a code listing in the text, quite to similar to the
47 | `figure` block.
48 |
49 | %--
50 | code:
51 | id: CodeA caption: |
52 | Test!
53 |
54 | source: my_script.py
55 | syntax: python
56 | caption: "A simple Python script"
57 | --%
58 |
59 | The `caption` and `syntax` attributes are optional.
60 | """
61 |
62 | def __init__(self, style=u'inline', template=u'kramdown', verbose=False):
63 |
64 | """
65 | Constructor.
66 |
67 | Keyword arguments:
68 | style -- Can be u'inline' or u'below' to indicate whether code
69 | should be placed in or below the text.
70 | (default=u'inline')
71 | template -- Indicates the output format, which can be 'kramdown' or
72 | 'liquid'. (default=u'kramdown')
73 | verbose -- Indicates whether verbose output should be generated.
74 | (default=False)
75 | """
76 |
77 | self.style = style
78 | self.template = template
79 | super(CodeParser, self).__init__(_object=u'code', required=['id', \
80 | 'source', 'syntax'], verbose=verbose)
81 |
82 | def parse(self, md):
83 |
84 | """See BaseParser.parse()."""
85 |
86 | self.nCode = 0
87 | return super(CodeParser, self).parse(md)
88 |
89 | def parseObject(self, md, _yaml, d):
90 |
91 | """See YAMLParser.parseObject()."""
92 |
93 | self.nCode += 1
94 | d['nCode'] = self.nCode
95 | self.msg(u'Found code: %s (%d)' % (d['id'], self.nCode))
96 | d[u'source'] = self.getPath(d[u'source'])
97 | if u'caption' not in d:
98 | d[u'caption'] = u''
99 | if self.template == u'kramdown':
100 | d[u'syntax'] = u'.' + d[u'syntax']
101 | with open(self.getPath(d[u'source'])) as fd:
102 | d[u'code'] = fd.read().strip()
103 | code = codeTemplate[self.template] % d
104 | if self.style == u'inline':
105 | md = md.replace(_yaml, code)
106 | else:
107 | md = md.replace(_yaml, u'')
108 | md += code
109 | md = md.replace(u'%%%s' % d[u'id'], u'[Listing %d](#%s)' % (self.nCode, \
110 | d[u'id']))
111 | return md
112 |
--------------------------------------------------------------------------------
/academicmarkdown/_ConstantParser.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | This file is part of zoteromarkdown.
5 |
6 | zoteromarkdown is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | zoteromarkdown is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with zoteromarkdown. If not, see .
18 | """
19 |
20 | from academicmarkdown import YAMLParser, MDFilter
21 | from academicmarkdown.constants import *
22 | import os
23 |
24 | class ConstantParser(YAMLParser):
25 |
26 | """
27 | The `constant` block allows you to define constants. For example, if you
28 | define MyConstant1 (as below), all occurrences of "%MyConstant1" in the text
29 | will be replcated by "You can refer to this as %MyConstant1".
30 |
31 | %--
32 | constant:
33 | MyConstant1: "You can refer to this as %MyConstant1"
34 | MyConstant2: "You can refer to this as %MyConstant2"
35 | --%
36 | """
37 |
38 | def __init__(self, verbose=False):
39 |
40 | """See YAMLParser.__init__()."""
41 |
42 | super(ConstantParser, self).__init__(_object=u'constant',
43 | verbose=verbose)
44 |
45 | def parseObject(self, md, _yaml, d):
46 |
47 | """See YAMLParser.parseObject()."""
48 |
49 | # Remove the YAML block
50 | md = md.replace(_yaml, u'')
51 | for key, val in d.items():
52 | self.msg(key)
53 | md = md.replace(u'%%%s' % key, val.strip())
54 | return md
55 |
56 |
57 |
--------------------------------------------------------------------------------
/academicmarkdown/_ExecParser.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | This file is part of zoteromarkdown.
5 |
6 | zoteromarkdown is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | zoteromarkdown is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with zoteromarkdown. If not, see .
18 | """
19 |
20 | from academicmarkdown import YAMLParser
21 | from academicmarkdown.py3compat import *
22 | import subprocess
23 | import shlex
24 |
25 | class ExecParser(YAMLParser):
26 |
27 | """
28 | The `exec` block inserts the return value of an external command in the
29 | text. For example, the following block embeds something like
30 | 'Generated on 10/18/2013':
31 |
32 | %-- exec: "date +'Generated on %x'" --%
33 | """
34 |
35 | def __init__(self, verbose=False):
36 |
37 | """See YAMLParser.__init__()."""
38 |
39 | super(ExecParser, self).__init__(_object=u'exec', verbose=verbose)
40 |
41 | def parseObject(self, md, _yaml, d):
42 |
43 | """See YAMLParser.parseObject()."""
44 |
45 | if not isinstance(d, basestring):
46 | return u'Expecting a string, not "%s"' % d
47 | self.msg(u'Command: %s' % d)
48 | if not py3:
49 | d = safe_encode(d)
50 | output = safe_decode(subprocess.check_output(shlex.split(d)))
51 | self.msg(u'Returns: %s' % output)
52 | return md.replace(_yaml, output)
53 |
--------------------------------------------------------------------------------
/academicmarkdown/_FigureParser.py:
--------------------------------------------------------------------------------
1 |
2 | # -*- coding: utf-8 -*-
3 |
4 | """
5 | This file is part of zoteromarkdown.
6 |
7 | zoteromarkdown is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | zoteromarkdown is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with zoteromarkdown. If not, see .
19 | """
20 |
21 | import os
22 | import yaml
23 | from academicmarkdown import YAMLParser
24 | import subprocess
25 | import sys
26 |
27 | figureTemplate = {
28 | u'html5': u"""
29 |
30 |
31 | Figure %(nFig)d. %(caption)s
32 |
33 | """,
34 | u'jekyll': u"""
35 | s)
36 |
37 | __Figure %(nFig)d.__ %(caption)s\n{: .fig-caption #%(id)s}\n
38 | """,
39 | u'odt': u"""
40 | s)
41 |
42 | __Figure %(nFig)d.__ *%(caption)s*
43 | """,
44 | u'markdown': u"""
45 | s)
46 | """}
47 |
48 |
49 | class FigureParser(YAMLParser):
50 |
51 | """
52 | The `figure` block embeds a Figure in the text. Figures are numbered
53 | automatically. The ID can be used to refer to the Figure in the text, using
54 | a `%` character. So the following figure would be referred to as `%FigFA`.
55 |
56 | %--
57 | figure:
58 | id: FigFA
59 | source: foveal_acuity.svg
60 | caption: "Visual acuity drops of rapidly with distance from the fovea."
61 | width: 100
62 | --%
63 |
64 | The `caption` and `width` attributes are optional.
65 | """
66 |
67 | def __init__(self, style=u'inline', template=u'html5', convertSVG=True, \
68 | margins=(30, 20, 30, 20), verbose=False):
69 |
70 | """
71 | Constructor.
72 |
73 | Keyword arguments:
74 | style -- Can be u'inline' or u'below' to indicate whether figures
75 | should be placed in or below the text.
76 | (default=u'inline')
77 | template -- Indicates the output format, which can be 'odt' or
78 | 'html5'. (default=u'html5')
79 | convertSVG -- Indicates whether .svg files should be converted to .png
80 | for better embedding. (default=True)
81 | margins -- Page margins. (default=30,20,30,20)
82 | verbose -- Indicates whether verbose output should be generated.
83 | (default=False)
84 | """
85 |
86 | self.style = style
87 | self.template = template
88 | self.convertSVG = convertSVG
89 | self.margins = margins
90 | super(FigureParser, self).__init__(_object=u'figure', required=['id', \
91 | 'source'], verbose=verbose)
92 |
93 | def parse(self, md):
94 |
95 | """See BaseParser.parse()."""
96 |
97 | self.nFig = 0
98 | return super(FigureParser, self).parse(md)
99 |
100 | def parseObject(self, md, _yaml, d):
101 |
102 | """See YAMLParser.parseObject()."""
103 |
104 | self.nFig += 1
105 | d['nFig'] = self.nFig
106 | self.msg(u'Found figure: %s (%d)' % (d['id'], self.nFig))
107 |
108 | d[u'source'] = self.getPath(d[u'source'])
109 |
110 | if d[u'source'].lower().endswith(u'.svg') and self.convertSVG:
111 | dest = d[u'source'] + u'.png'
112 | self.msg(u'Converting from SVG')
113 | A4WidthMm = 210
114 | A4WidthPx = 744.09
115 | pxPerMm = A4WidthPx / A4WidthMm
116 | realWidthMm = A4WidthMm - self.margins[1] - self.margins[3]
117 | realWidthPx = realWidthMm * pxPerMm
118 | cmd = [u'inkscape', d[u'source'], '-W']
119 | figWidthPx = float(subprocess.check_output(cmd))
120 | d[u'width'] = min(100, 100. * figWidthPx / realWidthPx)
121 | self.msg(u'Width: %.2f (%.2f%%)' % (figWidthPx, d[u'width']))
122 | if not os.path.exists(dest) or '--clear-svg' in sys.argv:
123 | cmd = [u'inkscape', u'-f', d[u'source'], u'-e', dest, u'-d', \
124 | '150', u'-b', u'white', u'-y', u'1.0']
125 | subprocess.call(cmd)
126 | else:
127 | self.msg('"%s" exists, not regenerating' % dest)
128 | d[u'source'] = dest
129 |
130 | if u'caption' not in d:
131 | d[u'caption'] = u''
132 | replaceList = [
133 | (u'"', u'"'),
134 | (u'\'', u'''),
135 | (u'<', u'<'),
136 | (u'>', u'>')
137 | ]
138 | for _from, _to in replaceList:
139 | d[u'caption'] = d[u'caption'].replace(_from, _to)
140 | d[u'caption'] = d[u'caption'].strip()
141 | if u'width' not in d:
142 | d[u'width'] = 100
143 | img = figureTemplate[self.template] % d
144 | if self.style == u'inline':
145 | md = md.replace(_yaml, img)
146 | else:
147 | md = md.replace(_yaml, u'')
148 | md += img
149 | # Replace both %MyFigure::a and %MyFigure, to allow for suffixes
150 | md = md.replace(u'%%%s::' % d[u'id'], u'[Figure %d](#%s)' % (self.nFig, \
151 | d[u'id']))
152 | md = md.replace(u'%%%s' % d[u'id'], u'[Figure %d](#%s)' % (self.nFig, \
153 | d[u'id']))
154 | return md
155 |
--------------------------------------------------------------------------------
/academicmarkdown/_GitHubParser.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | This file is part of zoteromarkdown.
5 |
6 | zoteromarkdown is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | zoteromarkdown is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with zoteromarkdown. If not, see .
18 | """
19 |
20 | import yaml
21 | from datamatrix import functional as fnc
22 | from academicmarkdown import YAMLParser
23 | from academicmarkdown.py3compat import *
24 | from academicmarkdown.constants import *
25 | try:
26 | from urllib.request import urlopen
27 | except ImportError:
28 | from urllib2 import urlopen
29 |
30 |
31 | @fnc.memoize(persistent=True)
32 | def urlget(url):
33 |
34 | print(url)
35 | fd = urlopen(url)
36 | content = fd.read()
37 | fd.close()
38 | return content
39 |
40 |
41 | class GitHubParser(YAMLParser):
42 |
43 | """
44 | The `github` block includes references to GitHub issues or user profiles.
45 | For example:
46 |
47 | %-- github: { user: smathot } --%
48 | %-- github: { repo: "smathot/academicmarkdown", issue: 1 } --%
49 | """
50 |
51 | def __init__(self, verbose=False):
52 |
53 | """See YAMLParser.__init__()."""
54 |
55 | super(GitHubParser, self).__init__(_object=u'github', verbose=verbose)
56 |
57 | def parseObject(self, md, _yaml, d):
58 |
59 | """See YAMLParser.parseObject()."""
60 |
61 | if not isinstance(d, dict):
62 | raise Exception(u'Expecting a dict')
63 | if u'user' in d:
64 | username = d['user']
65 | return md.replace(_yaml, \
66 | u'[@%s](https://github.com/%s)' % (username, username))
67 | if u'issue' in d:
68 | url = u'https://api.github.com/repos/%(repo)s/issues/%(issue)d' % d
69 | s = urlget(url)
70 | d = yaml.load(s)
71 | summary = u'[Issue #%(number)s](%(url)s): %(title)s' % d
72 | for label in d[u'labels']:
73 | summary += u' (*%(name)s*) ' % label
74 | return md.replace(_yaml, summary)
75 | raise Exception(u'Invalid GitHub block')
76 |
--------------------------------------------------------------------------------
/academicmarkdown/_IncludeParser.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | This file is part of zoteromarkdown.
5 |
6 | zoteromarkdown is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | zoteromarkdown is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with zoteromarkdown. If not, see .
18 | """
19 |
20 | from academicmarkdown import YAMLParser, MDFilter
21 | from academicmarkdown.py3compat import *
22 | from academicmarkdown.constants import *
23 | import os
24 |
25 | class IncludeParser(YAMLParser):
26 |
27 | """
28 | The `include` block includes an other Markdown file. For example:
29 |
30 | %-- include: example/intro.md --%
31 | """
32 |
33 | def __init__(self, verbose=False):
34 |
35 | """See YAMLParser.__init__()."""
36 |
37 | super(IncludeParser, self).__init__(_object=u'include', verbose=verbose)
38 |
39 | def parseObject(self, md, _yaml, d):
40 |
41 | """See YAMLParser.parseObject()."""
42 |
43 | if not isinstance(d, basestring):
44 | return u'Expecting a string, not "%s"' % d
45 | d = self.getPath(d)
46 | self.msg('Include: %s' % d)
47 | _md = safe_decode(open(d).read())
48 | # Apply pre-processing Markdown Filters
49 | for flt in preMarkdownFilters:
50 | fltFunc = getattr(MDFilter, flt)
51 | _md = fltFunc(_md)
52 | ip = IncludeParser(verbose=self.verbose)
53 | _md = ip.parse(_md)
54 | return md.replace(_yaml, _md)
55 |
--------------------------------------------------------------------------------
/academicmarkdown/_ODTFixer.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | This file is part of zoteromarkdown.
5 |
6 | zoteromarkdown is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | zoteromarkdown is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with zoteromarkdown. If not, see .
18 | """
19 |
20 | import zipfile
21 | from academicmarkdown import BaseParser
22 | from academicmarkdown.py3compat import *
23 | import re
24 |
25 | class ODTFixer(BaseParser):
26 |
27 | def __init__(self, verbose=False):
28 |
29 | super(ODTFixer, self).__init__(verbose=verbose)
30 |
31 | def fix(self, path):
32 |
33 | self.msg(u'Fixing %s' % path)
34 | self.msg(u'Reading ...')
35 | archive = zipfile.ZipFile(path, 'a')
36 | content = safe_decode(archive.read(u'content.xml'))
37 | # Style information is embedded as HTML comments, like so:
38 | # . The style needs to be extracted and placed
39 | # into the tags that open a paragraph.
40 | # We also need to take into account that '<', '>'. and '"' characters
41 | # have been HTML-ized by pandoc.
42 | lines = []
43 | for line in content.split('\n'):
44 | for toStyle in re.findall( \
45 | r'<!--odt-style="(\w+)"-->', line):
46 | for fromStyle in re.findall( \
47 | r'', line):
48 | line = line.replace(fromStyle, toStyle)
49 | line = line.replace(u'<!--odt-style="%s"-->' \
50 | % toStyle, u'')
51 | self.msg(u'Changing style "%s" to "%s"' % (fromStyle, toStyle))
52 | lines.append(line)
53 | content = u'\n'.join(lines)
54 |
55 | #print content
56 | self.msg(u'Writing ...')
57 | archive.writestr('content.xml', safe_encode(content))
58 | archive.close()
59 | self.msg(u'Done')
60 |
--------------------------------------------------------------------------------
/academicmarkdown/_Pandoc.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | This file is part of zoteromarkdown.
5 |
6 | zoteromarkdown is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | zoteromarkdown is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with zoteromarkdown. If not, see .
18 | """
19 |
20 | import subprocess
21 | import os
22 | from academicmarkdown import BaseParser
23 | from academicmarkdown.py3compat import *
24 |
25 | class Pandoc(BaseParser):
26 |
27 | def __init__(self, css=None, csl=None, template=None, standalone=True, \
28 | verbose=False):
29 |
30 | """
31 | Constructor.
32 |
33 | Keyword arguments:
34 | css -- A path to a `.css` file or None for no stylesheet.
35 | (default=None)
36 | csl -- A path to a `.csl` file to specify a citation format
37 | or None for a default citation format. (default=None)
38 | template -- The HTML template to be used. (default=None)
39 | standalone -- Indicates whether the --standalone and --self-contained
40 | arguments should be passed to pandoc. (default=True)
41 | verbose -- Indicates whether verbose output should be generated.
42 | (default=False)
43 | """
44 |
45 | self.css = css
46 | self.csl = csl
47 | self.template = template
48 | self.standalone = standalone
49 | super(Pandoc, self).__init__(verbose=verbose)
50 |
51 | def docx(self, md, output, docxRef=None):
52 |
53 | """
54 | Generates a .docx document.
55 |
56 | Arguments:
57 | md -- A Markdown string.
58 | output -- The name of the output file.
59 | """
60 |
61 | self.odt(md, output, odtRef=docxRef)
62 |
63 | def epub(self, md, output):
64 |
65 | """
66 | Generates an .epub document.
67 |
68 | Arguments:
69 | md -- A Markdown string.
70 | output -- The name of the output file.
71 | """
72 |
73 | self.msg(u'Invoking pandoc')
74 | cmd = u'pandoc --smart -t epub --toc -o %s' % output
75 | if os.path.exists(u'.bibliography.json'):
76 | cmd += u' --bibliography .bibliography.json'
77 | if self.csl != None:
78 | cmd += u' --csl %s' % self.csl
79 | ps = subprocess.Popen(cmd.split(), stdin=subprocess.PIPE, \
80 | stdout=subprocess.PIPE)
81 | print(safe_decode(ps.communicate(safe_encode(md)[0])))
82 |
83 | def html(self, md, output):
84 |
85 | """
86 | Generates an .html document.
87 |
88 | Argument:
89 | md -- A Markdown string.
90 | output -- The name of the output file.
91 | """
92 |
93 | open(output, 'w').write(safe_encode(self.parse(md)))
94 |
95 | def odt(self, md, output, odtRef=None):
96 |
97 | """
98 | Generates an .odt document.
99 |
100 | Arguments:
101 | md -- A Markdown string.
102 | output -- The name of the output file.
103 |
104 | Keyword arguments:
105 | odtRef -- A reference ODT for styling. (default=None)
106 | """
107 |
108 | self.msg(u'Invoking pandoc')
109 | cmd = u'pandoc --standalone'
110 | if os.path.exists(u'.bibliography.json'):
111 | cmd += u' --bibliography .bibliography.json'
112 | if self.csl != None:
113 | cmd += u' --csl %s' % self.csl
114 | if odtRef != None:
115 | # Since this function is also used to render docx, we should make
116 | # sure that we pass the correct reference argument.
117 | if odtRef.endswith(u'.docx'):
118 | cmd += u' --reference-docx=%s' % odtRef
119 | else:
120 | cmd += u' --reference-odt=%s' % odtRef
121 | cmd += u' -o'
122 | ps = subprocess.Popen(cmd.split() + [output], stdin=subprocess.PIPE, \
123 | stdout=subprocess.PIPE)
124 | print(safe_decode(ps.communicate(safe_encode(md))[0]))
125 |
126 | def parse(self, md):
127 |
128 | """See BaseParser.parse()."""
129 |
130 | self.msg(u'Invoking pandoc')
131 | cmd = u'pandoc -f markdown+header_attributes+markdown_attribute -t html5'
132 | if self.standalone:
133 | # cmd += u' --standalone --self-contained'
134 | cmd += u' --standalone --self-contained'
135 | if self.css != None:
136 | cmd += u' --css %s' % self.css
137 | if self.template != None:
138 | cmd += u' --template %s' % self.template
139 | if os.path.exists(u'.bibliography.json'):
140 | cmd += u' --bibliography .bibliography.json'
141 | if self.csl != None:
142 | cmd += u' --csl %s' % self.csl
143 | print(cmd)
144 | ps = subprocess.Popen(cmd.split(), stdin=subprocess.PIPE, \
145 | stdout=subprocess.PIPE)
146 | return safe_decode(ps.communicate(safe_encode(md))[0])
147 |
--------------------------------------------------------------------------------
/academicmarkdown/_PythonParser.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | This file is part of zoteromarkdown.
5 |
6 | zoteromarkdown is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | zoteromarkdown is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with zoteromarkdown. If not, see .
18 | """
19 |
20 | from academicmarkdown import YAMLParser
21 | from academicmarkdown.py3compat import *
22 | import subprocess
23 | import shlex
24 |
25 | _globals = {}
26 | img_nr = 0
27 |
28 | class PythonParser(YAMLParser):
29 |
30 | """
31 | The `python` block embeds the output (i.e. whatever is printed to stdout)
32 | of a Python script into your document. For example, the following block
33 | embeds the docstring of the `PythonParser` class (i.e. what you're reading
34 | now):
35 |
36 | %--
37 | python: |
38 | import inspect
39 | from academicmarkdown import PythonParser
40 | print inspect.getdoc(PythonParser)
41 | --%
42 |
43 | Note that the `|` symbol is YAML syntax, and allows you to have a multiline
44 | string.
45 | """
46 |
47 | def __init__(self, verbose=False):
48 |
49 | """See YAMLParser.__init__()."""
50 |
51 | super(PythonParser, self).__init__(_object=u'python', verbose=verbose)
52 |
53 | def parseObject(self, md, _yaml, d):
54 |
55 | """See YAMLParser.parseObject()."""
56 |
57 | global img_nr
58 |
59 | if not isinstance(d, basestring):
60 | return u'Expecting a string, not "%s"' % d
61 |
62 | import sys
63 | try:
64 | from io import StringIO # Py 3
65 | except ImportError:
66 | from StringIO import StringIO # Py 2
67 | img = False
68 | code = d
69 | if d.endswith('\nplt.show()\n'):
70 | d = d[:-len('\nplt.show()\n')]
71 | img_nr += 1
72 | d = 'from matplotlib import pyplot as plt\nplt.clf()\n{}\nplt.savefig("img/{}.png")\n'.format(
73 | d,
74 | img_nr
75 | )
76 | img = True
77 | self.msg(u'Python: %s' % d)
78 | buffer = StringIO()
79 | sys.stdout = buffer
80 | if safe_str(d).startswith('# should-raise\n'):
81 | code = code.replace('# should-raise\n', '')
82 | try:
83 | exec('#-*- coding:utf-8 -*-\n%s' % safe_str(d), _globals)
84 | except Exception as e:
85 | print('{0}: {1}'.format(e.__class__.__name__, e))
86 | # import traceback
87 | # traceback.print_exc(file=buffer, limit=1)
88 | else:
89 | raise ValueError('Code should raise exception but didn\t')
90 | else:
91 | exec('#-*- coding:utf-8 -*-\n%s' % safe_str(d), _globals)
92 | sys.stdout = sys.__stdout__
93 | output = buffer.getvalue()
94 | self.msg(u'Returns: %s' % output)
95 | s = u"""
96 | ~~~ .python
97 | %s
98 | ~~~
99 | """ % safe_str(code)
100 |
101 | if output:
102 | s += """
103 | __Output:__
104 |
105 | ~~~ .text
106 | %s
107 | ~~~
108 | """ % output
109 | if img:
110 | s += '\n\n'.format(img_nr)
111 | return md.replace(_yaml, s)
112 |
--------------------------------------------------------------------------------
/academicmarkdown/_TOCParser.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | This file is part of zoteromarkdown.
5 |
6 | zoteromarkdown is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | zoteromarkdown is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with zoteromarkdown. If not, see .
18 | """
19 |
20 | from academicmarkdown import YAMLParser
21 | import re
22 | import string
23 |
24 | class TOCParser(YAMLParser):
25 |
26 | """
27 | The `toc` block automatically generates a table of contents from the
28 | headings, assuming that headings are indicated using the `#` style and not
29 | the underlining style. You can indicate headings to be excluded from the
30 | table of contents as well.
31 |
32 | %--
33 | toc:
34 | mindepth: 1
35 | maxdepth: 2
36 | exclude: [Contents, Contact]
37 | --%
38 |
39 | All attributes are optional.
40 | """
41 |
42 | def __init__(self, anchorHeaders=False, appendHeaderRefs=False,
43 | verbose=False):
44 |
45 | """
46 | Constructor.
47 |
48 | Keyword arguments:
49 | anchorHeaders -- Indicates whether headers should be turned into
50 | clickable anchors, mostly useful for HTML pages.
51 | (default=False)
52 | appendHeaderRefs -- Indicates whether headers references should be
53 | appended to the document, so that you can
54 | directly refer to header links in the text. This
55 | is only necessary when using a Markdown parser
56 | that doesn't do this automatically.
57 | (default=False)
58 | verbose -- Indicates whether verbose output should be
59 | generated. (default=False)
60 | """
61 |
62 | self.anchorHeaders = anchorHeaders
63 | self.appendHeaderRefs = appendHeaderRefs
64 | self._uniqueId = u''
65 | super(TOCParser, self).__init__(_object=u'toc', verbose=verbose)
66 |
67 | def parseObject(self, md, _yaml, d):
68 |
69 | """See YAMLParser.parseObject()."""
70 |
71 | if u'exclude' not in d:
72 | d[u'exclude'] = []
73 | if u'maxdepth' not in d:
74 | d[u'maxdepth'] = 3
75 | if u'mindepth' not in d:
76 | d[u'mindepth'] = 1
77 | headers = []
78 | appends = []
79 | # Because script can have hashtags as code comments, we should ignore
80 | # script when searching for headers.
81 | # - Remove standard ~~~ style script blocks
82 | mdNoScript = re.sub(r'~~~(.*?)~~~', u'DUMMY', md, re.M, re.S)
83 | # - Remove jekyll style script blocks
84 | mdNoScript = re.sub(r'{% highlight (\w*) %}(.*?){% endhighlight %}',
85 | u'DUMMY', mdNoScript, re.M, re.S)
86 | for i in re.finditer(r'^#(.*)', mdNoScript, re.M):
87 | h = i.group()
88 | for level in range(100, -1, -1):
89 | if h.startswith(u'#' * level):
90 | break
91 | if level not in range(d[u'mindepth'], d[u'maxdepth']+1):
92 | self.msg(u'Header level not in range: %s' % h)
93 | continue
94 | label = h[level:].strip()
95 | _id = self.labelId(label)
96 | if label not in d[u'exclude']:
97 | headers.append( (level, h, label, _id) )
98 | self.msg(u'%s {#%s} (%d)' % (h, _id, level))
99 | if self.appendHeaderRefs:
100 | appends.append(u'[%s]: #%s' % (label, _id))
101 | _md = u'\n'
102 | lRep = []
103 | for level, h, label, _id in headers:
104 | print(h)
105 | _md += u'\t' * (level-d[u'mindepth']) # Indent
106 | _md += u'- [%s](#%s)\n' % (label, _id)
107 | if self.anchorHeaders:
108 | md = md.replace(h, u'%s [%s](#%s) {#%s}' % (u'#'*level, label, \
109 | _id, _id))
110 | _md += u'\n'
111 | return md.replace(_yaml, _md) + u'\n' + u'\n'.join(appends)
112 |
113 | def labelId(self, label):
114 |
115 | """
116 | Generates and ID for a label, simulating the IDs generated by Pandoc.
117 |
118 | IDs should match the IDs that generated by Pandoc. The basic
119 | algorithm appears to be that spaces are converted to dashes, dashes are
120 | left in and all non-alphanumeric characters are ignored. Avoid dashes
121 | at the beginning and end of the ID and also avoid double dashes.
122 |
123 | BUG: Pandoc doesn't properly parse (at least) Chinese characters,
124 | so links in Chinese TOCs will be broken.
125 |
126 | Arguments:
127 | label -- A label.
128 |
129 | Returns:
130 | An ID.
131 | """
132 |
133 | _id = u''
134 | for ch in label:
135 | if ch in string.ascii_letters + string.digits + u'-_':
136 | _id += ch.lower()
137 | elif ch.isspace() and len(_id) > 0 and _id[-1] != u'-':
138 | _id += u'-'
139 | _id = _id.strip(u'-.')
140 | # Make sure that the ID is not empty and starts with a letter
141 | if len(_id) == 0 or not _id[0].isalpha():
142 | _id = self.uniqueId() + _id
143 | return _id
144 |
145 | def uniqueId(self):
146 |
147 | """
148 | Returns:
149 | A unique letter id.
150 | """
151 |
152 | self._uniqueId += u'a'
153 | return self._uniqueId
154 |
--------------------------------------------------------------------------------
/academicmarkdown/_TableParser.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | This file is part of zoteromarkdown.
5 |
6 | zoteromarkdown is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | zoteromarkdown is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with zoteromarkdown. If not, see .
18 | """
19 |
20 | import os
21 | import yaml
22 | from academicmarkdown import YAMLParser
23 | from academicmarkdown.py3compat import *
24 | import subprocess
25 | import sys
26 |
27 | tableTemplate = {u'html5': u"""
28 | NOT IMPLEMENTED
29 | """,
30 | u'kramdown': u"""
31 | %(table)s
32 |
33 | __Table %(nTbl)d.__ %(caption)s\n{: .tbl-caption #%(id)s}
34 | """,
35 | u'pandoc': u"""
36 |
37 |
46 | """}
47 |
48 |
49 | class TableParser(YAMLParser):
50 |
51 | """
52 | The `table` block reads a table from a `.csv` file and embed it into the
53 | document. The source file needs to be a utf-8 encoded file that is
54 | comma separated and double quoted.
55 |
56 | %--
57 | table:
58 | id: MyTable
59 | source: my_table.csv
60 | caption: "My table caption."
61 | ndigits: 4
62 | --%
63 | """
64 |
65 | def __init__(self, style=u'inline', template=u'kramdown', verbose= \
66 | False):
67 |
68 | """
69 | Constructor.
70 |
71 | Keyword arguments:
72 | style -- Can be u'inline' or u'below' to indicate whether figures
73 | should be placed in or below the text.
74 | (default=u'inline')
75 | template -- Indicates the output format, which can be 'odt' or
76 | 'html5'. (default=u'html5')
77 | verbose -- Indicates whether verbose output should be generated.
78 | (default=False)
79 | """
80 |
81 | self.style = style
82 | self.template = template
83 | super(TableParser, self).__init__(_object=u'table', required=['id', \
84 | 'source'], verbose=verbose)
85 |
86 | def parse(self, md):
87 |
88 | """See BaseParser.parse()."""
89 |
90 | self.nTbl = 0
91 | return super(TableParser, self).parse(md)
92 |
93 | def parseObject(self, md, _yaml, d):
94 |
95 | """See YAMLParser.parseObject()."""
96 |
97 | self.nTbl += 1
98 | d['nTbl'] = self.nTbl
99 | self.msg(u'Found table: %s (%d)' % (d['id'], self.nTbl))
100 | d[u'source'] = self.getPath(d[u'source'])
101 | if u'caption' not in d:
102 | d[u'caption'] = u''
103 | if u'ndigits' not in d:
104 | d[u'ndigits'] = 4
105 | # Read table and turn it into a kramdown-style table
106 | s = u''
107 | import csv
108 | i = 0
109 | with open(d[u'source'], u'r') as csvFile:
110 | csvReader = csv.reader(csvFile, delimiter=',', quotechar='"')
111 | for row in csvReader:
112 | # Pandoc requires a row of alignment indicators below the
113 | # header. See also:
114 | # -
115 | if self.template in (u'pandoc', u'kramdown'):
116 | if i == 1:
117 | alignList = []
118 | for col in row:
119 | try:
120 | float(col)
121 | alignList.append(u'--:')
122 | except:
123 | alignList.append(u':--')
124 | s += (u'| ' + u' | '.join(alignList) + u' |\n')
125 | _row = []
126 | for col in row:
127 | try:
128 | # If a value is numeric, we need to round it. If the
129 | # rounde value is 0, we indicated this with a smaller
130 | # than sign.
131 | float(col)
132 | col = round(float(col), d[u'ndigits'])
133 | if col == 0:
134 | col = u'< 0.' + u'0'*(d[u'ndigits']-1) + u'1'
135 | else:
136 | # Use a somethat convoluted string formatting
137 | # operation to make sure that we don't lose trailing
138 | # zeros.
139 | col = (u'%%.%df' % d[u'ndigits']) % col
140 | _row.append(col)
141 | except:
142 | if not col.strip():
143 | col = u' '
144 | _row.append(safe_decode(col))
145 | s += (u'| ' + u' | '.join(_row) + u' |\n')
146 | i += 1
147 | d[u'table'] = s
148 | tbl = tableTemplate[self.template] % d
149 | # Insert/ append table into document
150 | if self.style == u'inline':
151 | md = md.replace(_yaml, tbl)
152 | else:
153 | md = md.replace(_yaml, u'')
154 | md += tbl
155 | # Replace reference to table
156 | md = md.replace(u'%%%s' % d[u'id'], u'[Table %d](#%s)' % (self.nTbl, \
157 | d[u'id']))
158 | return md
159 |
--------------------------------------------------------------------------------
/academicmarkdown/_VideoParser.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | This file is part of zoteromarkdown.
5 |
6 | zoteromarkdown is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | zoteromarkdown is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with zoteromarkdown. If not, see .
18 | """
19 |
20 | import os
21 | import yaml
22 | from academicmarkdown import YAMLParser
23 | import subprocess
24 | import sys
25 |
26 | videoTemplate = {
27 | u'vimeo' : u"""
28 |
30 |
Video %(nVid)d. %(caption)s
31 | """,
32 | u'youtube' : u"""
33 |
35 |
Video %(nVid)d. %(caption)s
36 | """
37 | }
38 |
39 | class VideoParser(YAMLParser):
40 |
41 | """
42 | Embeds a video. Currently, YouTube and Vimeo sources are supported. The
43 | keywords `width`, `height`, and `caption` are optional.
44 |
45 | %--
46 | video:
47 | id: VidRefresh
48 | source: vimeo
49 | videoid: 24216910
50 | width: 640
51 | height: 240
52 | caption: "A figure caption"
53 | --%
54 |
55 | """
56 |
57 | def __init__(self, verbose=False):
58 |
59 | """
60 | Constructor.
61 |
62 | Keyword arguments:
63 | verbose -- Indicates whether verbose output should be generated.
64 | (default=False)
65 | """
66 |
67 | super(VideoParser, self).__init__(_object=u'video', required=['id', \
68 | 'source', 'videoid'], verbose=verbose)
69 |
70 | def parse(self, md):
71 |
72 | """See BaseParser.parse()."""
73 |
74 | self.nVid = 0
75 | return super(VideoParser, self).parse(md)
76 |
77 | def parseObject(self, md, _yaml, d):
78 |
79 | """See YAMLParser.parseObject()."""
80 |
81 | self.nVid += 1
82 | d['nVid'] = self.nVid
83 | self.msg(u'Found video: %s (%d)' % (d['id'], self.nVid))
84 | if u'caption' not in d:
85 | d[u'caption'] = u''
86 | if u'width' not in d:
87 | d[u'width'] = 640
88 | if u'height' not in d:
89 | d[u'height'] = 320
90 | vid = videoTemplate[d[u'source']] % d
91 | md = md.replace(_yaml, vid)
92 | md = md.replace(u'%%%s' % d[u'id'], u'[Video %d](#%s)' % (self.nVid, \
93 | d[u'id']))
94 | return md
95 |
96 |
--------------------------------------------------------------------------------
/academicmarkdown/_WcParser.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | This file is part of zoteromarkdown.
5 |
6 | zoteromarkdown is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | zoteromarkdown is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with zoteromarkdown. If not, see .
18 | """
19 |
20 | from academicmarkdown.py3compat import *
21 | from academicmarkdown import YAMLParser
22 | import subprocess
23 | import shlex
24 |
25 | class WcParser(YAMLParser):
26 |
27 | """
28 | The `wc` block insert the word count for a particular document. This is
29 | convenient if you have split the text across multiple documents, and want to
30 | have a separate word count for each document.
31 |
32 | %-- wc: method-section.md --%
33 | """
34 |
35 | def __init__(self, verbose=False):
36 |
37 | """See YAMLParser.__init__()."""
38 |
39 | super(WcParser, self).__init__(_object=u'wc', verbose=verbose)
40 |
41 | def parseObject(self, md, _yaml, d):
42 |
43 | """See YAMLParser.parseObject()."""
44 |
45 | if not isinstance(d, str):
46 | return u'Expecting a string, not "%s"' % d
47 | s = safe_decode(open(self.getPath(d)).read())
48 | wc = str(len(s.split()))
49 | self.msg(u'Word count: %s words in %s' % (wc, d))
50 | return md.replace(_yaml, wc)
51 |
--------------------------------------------------------------------------------
/academicmarkdown/_WkHtmlToPdf.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | This file is part of zoteromarkdown.
5 |
6 | zoteromarkdown is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | zoteromarkdown is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with zoteromarkdown. If not, see .
18 | """
19 |
20 | import re
21 | import shlex
22 | import subprocess
23 | from academicmarkdown import BaseParser
24 | from academicmarkdown.py3compat import *
25 |
26 | # From
27 | feaderTmpl = """
28 |
29 |
30 |
31 |
43 | %css%
44 |
45 |
%feader%
46 | """
47 |
48 | class WkHtmlToPdf(BaseParser):
49 |
50 | def __init__(self, css=None, fix00=False, margins=(20, 20, 30, 20),
51 | spacing=(10, 10), header=None, footer=u'%page% / %topage%',
52 | verbose=False, args=''):
53 |
54 | """
55 | Constructor.
56 |
57 | Keyword arguments:
58 | css -- A path to a css stylesheet or None. (default=None)
59 | fix00 -- Indicates whether #00 corruptions should be fixed.
60 | (default=True)
61 | margins -- A T,R,B,L tuple of page margins. (default=20,20,30,20)
62 | spacing -- A (header spacing, footer spacing) tuple.
63 | (default=10,10)
64 | header -- A header text, or None. (default=None)
65 | footer -- A footer text, or None. (default=u'%page% / %topage%')
66 | verbose -- Indicates whether verbose output should be generated.
67 | (default=False)
68 | args --
69 | """
70 |
71 | self.css = css
72 | self.args = args
73 | self.fix00 = fix00
74 | self.margins = margins
75 | self.spacing = spacing
76 | self.header = header
77 | self.footer = footer
78 | super(WkHtmlToPdf, self).__init__(verbose=verbose)
79 |
80 | def createFeader(self, s, cssClass):
81 |
82 | """
83 | Builds a header or footer html file.
84 |
85 | Arguments:
86 | s -- The contents for the header/ footer.
87 | cssClass -- The css class to be used for the div containing the
88 | contents.
89 |
90 | Returns:
91 | An HTML string containing the header/ footer.
92 | """
93 |
94 | regEx = r'%(?P[a-z]+)%'
95 | for r in re.finditer(regEx, s):
96 | s = s.replace(u'%%%s%%' % r.group('var'), \
97 | u'' % r.group('var'))
98 | feader = feaderTmpl.replace(u'%feader%', s)
99 | if self.css != None:
100 | feader = feader.replace(u'%css%', \
101 | u'' % \
102 | self.css)
103 | else:
104 | feader = feader.replace(u'%css%', u'')
105 | feader = feader.replace(u'%class%', cssClass)
106 | return feader
107 |
108 | def parse(self, html, target):
109 |
110 | """See BaseParser.parse()."""
111 |
112 | self.msg(u'Invoking wkhtmltopdf')
113 | cmd = u'wkhtmltopdf -T %s -R %s -B %s -L %s --dpi 96 --disable-smart-shrinking' % self.margins
114 | if self.header != None:
115 | open('.header.html', 'wb').write(safe_encode(self.createFeader(
116 | self.header, u'header')))
117 | cmd += u' --header-html .header.html --header-spacing %s' % \
118 | self.spacing[0]
119 | if self.footer != None:
120 | open('.footer.html', 'wb').write(safe_encode(
121 | self.createFeader(self.footer, u'footer')))
122 | cmd += u' --footer-html .footer.html --footer-spacing %s' % \
123 | self.spacing[1]
124 | cmd += u' ' + self.args
125 | cmd += u' %s "%s"' % (html, target)
126 | self.msg(cmd)
127 | if not py3:
128 | cmd = safe_encode(cmd)
129 | subprocess.call(shlex.split(cmd))
130 | if self.fix00:
131 | # Due to a bug in wkhtmltopdf, the PDF may contain #00 strings,
132 | # which cause Acrobat Reader to choke (but not other PDF readers).
133 | # This happens mostly when filenames are very long, in which case
134 | # anchors are hashed, and the resulting hashes sometimes contain #00
135 | # values. Here we simply replace all #00 strings, which seems to
136 | # work.
137 | self.msg(u'Checking for #00')
138 | pdf = open(target, 'rb').read()
139 | if safe_encode('#00') in pdf:
140 | self.msg(u'Fixing #00!')
141 | pdf = pdf.replace('#00', '#01')
142 | open(target, u'wb').write(pdf)
143 |
--------------------------------------------------------------------------------
/academicmarkdown/_YAMLParser.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | This file is part of zoteromarkdown.
5 |
6 | zoteromarkdown is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | zoteromarkdown is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with zoteromarkdown. If not, see .
18 | """
19 |
20 | import re
21 | import yaml
22 | from academicmarkdown import BaseParser
23 |
24 | class YAMLParser(BaseParser):
25 |
26 | def __init__(self, _object, required=[], verbose=False):
27 |
28 | """
29 | Constructor.
30 |
31 | Arguments:
32 | _object -- Indicates which objects should be parsed.
33 |
34 | Keyword arguments:
35 | required -- A list of required options in the YAML block.
36 | (default=[])
37 | verbose -- Indicates whether verbose output should be generated.
38 | (default=False)
39 | """
40 |
41 | self._object = _object
42 | self.required = required
43 | super(YAMLParser, self).__init__(verbose=verbose)
44 |
45 | def parse(self, md):
46 |
47 | """See BaseParser.parse()."""
48 |
49 | for r in re.finditer(u'%--(.*?)--%', md, re.M|re.S):
50 | try:
51 | d = yaml.load(r.groups()[0])
52 | except:
53 | self.msg(u'Invalid YAML block: %s' % r.groups()[0])
54 | continue
55 | if not isinstance(d, dict):
56 | continue
57 | obj = list(d.keys())[0]
58 | if obj.lower() != self._object:
59 | continue
60 | keys = d[obj]
61 | for key in self.required:
62 | if key not in keys:
63 | raise Exception( \
64 | u'"%s" is a required option for %s objects' % (key, \
65 | self._object))
66 | md = self.parseObject(md, r.group(), keys)
67 | return md
68 |
69 | def parseObject(self, md, _yaml, d):
70 |
71 | """
72 | Parses a specific YAML object found in a Markdown text.
73 |
74 | Arguments:
75 | md -- The full markdown text.
76 | yaml -- The yaml block.
77 | d -- The yaml block already parsed into a dictionary.
78 |
79 | Returns:
80 | The parsed Markdown text.
81 | """
82 |
83 | return md
84 |
--------------------------------------------------------------------------------
/academicmarkdown/_ZoteroParser.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | This file is part of zoteromarkdown.
5 |
6 | zoteromarkdown is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | zoteromarkdown is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with zoteromarkdown. If not, see .
18 | """
19 |
20 | try:
21 | from pyzotero import zotero
22 | except:
23 | zotero = None
24 |
25 | from academicmarkdown import BaseParser
26 | from academicmarkdown.py3compat import *
27 | import os
28 | import re
29 | import json
30 | import pickle
31 |
32 |
33 | class ZoteroParser(BaseParser):
34 |
35 | cachePath = u'.zoteromarkdown.cache'
36 |
37 | def __init__(self, libraryId, apiKey, libraryType=u'user',
38 | clearCache=False, headerText=u'References', headerLevel=1,
39 | odtStyle=None, fixDOI=True, fixAuthorNames=True, verbose=False,
40 | removeURL=True):
41 |
42 | """
43 | Constructor.
44 |
45 | Arguments:
46 | libraryId -- The libraryId, available from your Zotero profile.
47 | apiKey -- The API key, available from your Zotero profile.
48 |
49 | Keyword arguments:
50 | libraryType -- The library type. Can be 'user' or 'group'.
51 | (default=u'user')
52 | clearCache -- Indicates whether the cache should be cleared.
53 | (default=False)
54 | headerText -- Indicates the text to be used for the header.
55 | (default=u'References')
56 | headerLevel -- Indicates the header level for the references.
57 | (default=1)
58 | odtStyle -- Indicates the style to be used for ODT output. This
59 | style is indicated as an HTML comment, so that it
60 | does not affect HTML output. (default=None)
61 | fixDOI -- Indicates that the DOI field should be remapped and
62 | cleaned up for proper rendering. (default=True)
63 | fixAuthorNames -- Indicates that first names of authors should be
64 | converted to clean initials, to avoid one author
65 | appearing as multiple. (default=True)
66 | removeURL -- Removes the URL from the references, because some
67 | styles insist on adding it. The URL is only removed
68 | when a journal (i.e. `container-title` field) is
69 | available. (default=True)
70 | verbose -- Indicates whether verbose output should be printed.
71 | (default=False)
72 | """
73 |
74 | if zotero == None:
75 | raise Exception(u'pyzotero is not available!')
76 | super(ZoteroParser, self).__init__(verbose=verbose)
77 | self.zotero = None
78 | self.libraryId = libraryId
79 | self.apiKey = apiKey
80 | self.libraryType = libraryType
81 | self.headerText = headerText
82 | self.headerLevel = headerLevel
83 | self.odtStyle = odtStyle
84 | self.fixDOI = fixDOI
85 | self.fixAuthorNames = fixAuthorNames
86 | self.removeURL = removeURL
87 | self.refCount = 0
88 | if not os.path.exists(self.cachePath) or clearCache:
89 | self.cache = {}
90 | else:
91 | fd = open(self.cachePath, 'rb')
92 | try:
93 | self.cache = pickle.load(fd)
94 | except:
95 | self.msg(u'Failed to open cache.')
96 | self.cache = {}
97 | fd.close()
98 |
99 | def connect(self):
100 |
101 | """Connects to the Zotero API."""
102 |
103 | self.msg(u'Connecting to Zotero server.')
104 | self.zotero = zotero.Zotero(self.libraryId, self.libraryType, \
105 | self.apiKey)
106 |
107 | def getYear(self, s):
108 |
109 | """
110 | Extracts the year from a string in a clever way.
111 |
112 | Arguments:
113 | s -- A string.
114 |
115 | Returns:
116 | A best guess of the year.
117 | """
118 |
119 | try:
120 | from dateutil import parser
121 | except:
122 | self.msg(u'dateutil is not available to guess the year.')
123 | return s
124 | try:
125 | return parser.parse(s).year
126 | except:
127 | self.msg(u'failed to parse date %s' % s)
128 | return s
129 |
130 | def parse(self, md):
131 |
132 | """
133 | Parses pandoc-style citations from the documents and adds a
134 | corresponding bibliography as YAML to the documents.
135 |
136 | Arguments:
137 | md -- A string containing MarkDown text.
138 |
139 | Returns:
140 | The Markdown text with bibliography added.
141 | """
142 |
143 | items = []
144 | oldQueries = []
145 | regexp = r'@([^ ?!,.\t\n\r\f\v\]\[;]+)'
146 | for r in re.finditer(regexp, md):
147 | queryString = r.groups()[0]
148 | self.msg(u'Found citation (#%d) "%s"' % (self.refCount,
149 | queryString))
150 | if queryString in oldQueries:
151 | continue
152 | self.refCount += 1
153 | matches = self.bestMatch(queryString)
154 | if len(matches) == 0:
155 | self.msg(u'No matches for "%s"!' % queryString)
156 | continue
157 | if len(matches) > 1:
158 | for match in matches:
159 | print(match)
160 | raise Exception( \
161 | u'Multiple Zotero matches (%d) for "@%s". Be more specific!' % \
162 | (len(matches), queryString))
163 | match = matches[0]
164 | if match in items and queryString not in oldQueries:
165 | for _queryString in sorted(oldQueries):
166 | print(u'Ref: %s' % _queryString)
167 | raise Exception(
168 | ('"%s" refers to an existent reference with a different name. Please use consistent references (see list above)!' \
169 | % queryString))
170 | match[u'id'] = queryString
171 | if self.odtStyle != None:
172 | match[u'title'] += u'' % self.odtStyle
173 | items.append(match)
174 | oldQueries.append(queryString)
175 | # TODO Placing the citation info in the YAML block doesn't appear to
176 | # work. So for now save it as a JSON file.
177 | fd = open(u'.bibliography.json', u'w')
178 | json.dump(items, fd, indent=1)
179 | fd.close()
180 | if self.headerText == None or self.headerLevel == None:
181 | return md
182 | md = md.replace(u'%rc%', str(self.refCount))
183 | return md + u'\n\n%s %s\n\n' % (u'#' * self.headerLevel, \
184 | self.headerText)
185 |
186 | def bestMatch(self, queryString):
187 |
188 | """
189 | Retrieves a matching item for a given query. Queries
190 |
191 | Arguments:
192 | queryString -- A query string.
193 |
194 |
195 | Returns:
196 | A csljson-style dictionary for the matching item.
197 | """
198 |
199 | query = self.splitCitation(queryString)
200 | if query[0] in self.cache:
201 | self.msg(u'Retrieving "%s" from cache.' % query[0])
202 | items = self.cache[query[0]]
203 | else:
204 | self.msg(u'Retrieving "%s" from Zotero API.' % query[0])
205 | if self.zotero == None:
206 | self.connect()
207 | try:
208 | items = self.zotero.top(q=safe_encode(query[0]),
209 | limit=100, content=u'csljson')
210 | except:
211 | self.msg(u'Failed to query Zotero server!')
212 | return []
213 | if len(items) == 0:
214 | return []
215 | self.cache[query[0]] = items
216 | fd = open(self.cachePath, u'wb')
217 | pickle.dump(self.cache, fd)
218 | fd.close()
219 | matches = []
220 | for item in items:
221 | match = True
222 | matchPhase = 0
223 | for i in range(len(query)):
224 | # Determine whether we are matching a year or an author name
225 | term = query[i].lower()
226 | try:
227 | term = int(term)
228 | matchPhase += 1
229 | except:
230 | pass
231 | # Check authors
232 | if matchPhase == 0:
233 | if 'author' not in item:
234 | break
235 | if i >= len(item[u'author']):
236 | match = False
237 | break
238 | if term not in item[u'author'][i][u'family'].lower():
239 | match = False
240 | break
241 | # Check year
242 | elif matchPhase == 1:
243 | if u'issued' not in item:
244 | match = False
245 | break
246 | else:
247 | if u'year' in item[u'issued'] and not item[u'issued']['year'] == u'date unknown':
248 | year = item[u'issued'][u'year']
249 | elif u'raw' in item[u'issued']:
250 | year = item[u'issued'][u'raw']
251 | elif u'date-parts' in item[u'issued']:
252 | year = item[u'issued'][u'date-parts'][0][0]
253 | elif u'literal' in item[u'issued']:
254 | year = item[u'issued'][u'literal']
255 | else:
256 | raise Exception(u'Invalid issued field: %s' % item)
257 | try:
258 | year = int(year)
259 | except:
260 | pass
261 | if type(year) == int:
262 | if term != year:
263 | match = False
264 | break
265 | else:
266 | if term != 0:
267 | match = False
268 | break
269 | matchPhase += 1
270 | # Check title or publication
271 | elif matchPhase == 2:
272 | if not (u'title' in item and term in \
273 | item[u'title'].lower()) and not (u'container-title' in \
274 | item and term in item[u'container-title'].lower()):
275 | match = False
276 | break
277 | # Sometimes, the year of publication is stored as issued.raw,
278 | # instead of issued.year. Fix this, if this is the case. We need to
279 | # explictly remove the 'raw' entry as well.
280 | if u'issued' in item and u'year' not in item[u'issued']:
281 | if u'raw' in item[u'issued']:
282 | item[u'issued'][u'year'] = self.getYear(
283 | item[u'issued'][u'raw']
284 | )
285 | elif u'date-parts' in item[u'issued']:
286 | item[u'issued'][u'year'] = self.getYear(
287 | item[u'issued'][u'date-parts'][0][0]
288 | )
289 | else:
290 | item[u'issued'][u'year'] = u'date unknown'
291 | # Make sure that both a lowercase (doi) and uppercase (DOI) key is
292 | # present, remove prefixed 'doi:' strings, and lowercase the url.
293 | if self.fixDOI:
294 | if u'DOI' in item:
295 | doi = item[u'DOI'][:]
296 | elif u'doi' in item:
297 | doi = item[u'doi'][:]
298 | else:
299 | doi = None
300 | self.msg('Missing DOI: %s' % item[u'title'])
301 | if doi is not None:
302 | if doi.startswith(u'doi:'):
303 | doi = doi[4:]
304 | doi = doi.lower()
305 | item[u'doi'] = item[u'DOI'] = doi
306 | # Remove URL field
307 | if self.removeURL:
308 | if u'URL' in item.keys() and (u'container-title' in \
309 | item.keys() or u'publisher' in item.keys()):
310 | del item[u'URL']
311 | # Convert initials to 'A.B.C.' style to avoid mixups.
312 | if self.fixAuthorNames and u'author' in item:
313 | _author = []
314 | for author in item[u'author']:
315 | if u'given' not in author or u'family' not in author:
316 | continue
317 | given = author[u'given']
318 | family = author[u'family']
319 | # First replace dots by spaces
320 | given = given.replace(u'.', u' ')
321 | # Concatenate a list of capitalized initials
322 | given = u''.join([i[0].upper() for i in given.split()])
323 | # Add dots after each initial
324 | given = u'. '.join(given) + u'.'
325 | _author.append({u'family' : family, u'given': given})
326 | item[u'author'] = _author
327 | # Remove empty fields
328 | for field in item:
329 | if isinstance(item[field], str) and \
330 | item[field].strip() == u'':
331 | self.msg(u'Removing empty field: %s' % field)
332 | del item[field]
333 | if match:
334 | matches.append(item)
335 | return matches
336 |
337 | def splitCitation(self, s):
338 |
339 | """
340 | Splits a citation string, like Land1999WhyAnimals, and returns each
341 | element of the string in a list.
342 |
343 | Arguments:
344 | s -- The citation string, e.g. 'Land1999WhyAnimals'.
345 |
346 | Returns:
347 | A list of citation elements, e.g. ['land', '1999', 'why', 'animals'].
348 | """
349 |
350 | # First, split underscore-style citations, like '@land_1999_why_animals'
351 | if u'_' in s:
352 | return s.split(u'_')
353 | regexp = r'([A-Z][^ 0-9A-Z?!,.\t\n\r\f\v\]\[;]*)'
354 | # Otherwise, split camelcase-style citations, like @Land1999WhyAnimals.
355 | l = []
356 | for t in re.split(regexp, s):
357 | if t != u'':
358 | l.append(t.lower().replace(u'+', u' '))
359 | return l
360 |
--------------------------------------------------------------------------------
/academicmarkdown/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | This file is part of zoteromarkdown.
5 |
6 | zoteromarkdown is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | zoteromarkdown is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with zoteromarkdown. If not, see .
18 |
19 | ---
20 | desc: |
21 | *Who knew writing could be so nerdy?*
22 |
23 | version %-- python: "from academicmarkdown import version; print(version)" --%
24 |
25 | Copyright 2013-2015 Sebastiaan Mathôt
26 |
27 | [](https://travis-ci.org/smathot/academicmarkdown)
28 |
29 | ## Contents
30 |
31 | %--
32 | toc:
33 | mindepth: 1
34 | maxdepth: 3
35 | exclude: [Contents]
36 | --%
37 |
38 | ## About
39 |
40 | Academic Markdown is a Python module for generating `.md`, `.html`, `.pdf`,
41 | `.docx`, and `.odt` files from Markdown source. [Pandoc] is used for most of
42 | the heavy lifting, so refer to the Pandoc website for detailed information
43 | about writing in Pandoc Markdown. However, Academic Markdown offers some
44 | additional functionality that is useful for writing scientific documents,
45 | such as integration with [Zotero references], and a number of useful
46 | [Academic Markdown extensions].
47 |
48 | At present, the main target for Academic Markdown is the OpenSesame
49 | documentation site, , although it may in time grow
50 | into a more comprehensive and user-friendly tool.
51 |
52 | ## Examples
53 |
54 | A basic example can be found in the `example` sub folder, included with the source.
55 |
56 | The following manuscripts have been written in Academic Markdown:
57 |
58 | - Mathôt S, Dalmaijer ES, Grainger J, Van der Stigchel S. (2014) The pupillary light response reflects exogenous attention and inhibition of return. *PeerJ PrePrints* 2:e422v1 ([source](https://github.com/smathot/materials_for_P0009.1/tree/master/manuscript))
59 | - Mathôt S, van der Linden L, Grainger J, Vitu F. (2014) The pupillary light response reflects eye-movement preparation. *PeerJ PrePrints* 2:e238v2 ([source](https://github.com/smathot/materials_for_P0001/tree/master/manuscript))
60 |
61 | ## Download
62 |
63 | You can download the latest release of Academic Markdown here:
64 |
65 | -
66 |
67 | Ubuntu users can install Academic Markdown from the Cogsci.nl PPA:
68 |
69 | sudo add-apt-repository ppa:smathot/cogscinl
70 | sudo apt-get update
71 | sudo apt-get install python-academicmarkdown
72 |
73 | ## Basic usage
74 |
75 | Academic Markdown assumes that input files are encoded with `utf-8` encoding.
76 |
77 | ~~~ {.python}
78 | from academicmarkdown import build
79 | build.HTML(u'input.md', u'output.html')
80 | build.HTML(u'input.md', u'output.html', standalone=False)
81 | build.PDF(u'input.md', u'output.pdf')
82 | build.DOCX(u'input.md', u'output.docx')
83 | build.ODT(u'input.md', u'output.odt')
84 | ~~~
85 |
86 | A number of options can be specified by setting attributes of the `build` module, like so
87 |
88 | ~~~ {.python}
89 | build.spacing = 30, 0
90 | ~~~
91 |
92 | The full list of options is available in `academicmarkdown/constants.py`, or see [academicmarkdown.constants].
93 |
94 | ## Dependencies
95 |
96 | Academic Markdown has been tested exclusively on Ubuntu Linux. The following dependencies are required:
97 |
98 | - [pandoc] is used for most of the heavy lifting. At the time of writing, the Ubuntu repositories do not contain a sufficiently recent version of Pandoc. Therefore, if you encounter trouble, try installing the latest version of Pandoc manually.
99 | - [pyzotero] is necessary for extracting Zotero references.
100 | - [wkhtmltopdf] is necessary for converting to `.pdf`. For best results, use the latest statically linked release, instead of the version from the Ubuntu repositories.
101 |
102 | ## Zotero references
103 |
104 | ### Pandoc citation style
105 |
106 | Since the basic Markdown conversion is performed by Pandoc, citations should be formatted as described on the Pandoc site:
107 |
108 | -
109 |
110 | ### Zotero API key and library ID
111 |
112 | You can automatically extract references from your Zotero library by setting the `zoteroApiKey` and `zoteroLibraryId` properties. Your references are not extracted from your local Zotero database, but through the web API of . This means that you need to have a Zotero account and synchronize your local database with your account, in order to use this feature. You can find your your API key and library ID online on your Zotero profile page.
113 |
114 | ~~~ {.python}
115 | from academicmarkdown import build
116 | build.zoteroApiKey = u'myapikey'
117 | build.zoteroLibraryId = u'mylibraryid'
118 | build.PDF(u'input.md', u'output.pdf')
119 | ~~~
120 |
121 | ### Citation identifiers
122 |
123 | Citations are split into separate terms using camelcase or undescore logic. An example of an underscore-style citation is `@bárány_halldén_1948`. And example of a camelcase-style citation is `@Land1999WhyAnimals`. Each citation is interpreted as a series of author names, followed by the year of publication, optionally followed by terms that match either the publication title, or the article title. So the following reference ...
124 |
125 | Land, M., Mennie, N., & Rusted, J. (1999). The roles of vision and eye movements in the control of activities of daily living. *Perception*, *28*(11), 1311–1328.
126 |
127 | ... matches any of the following terms:
128 |
129 | - `@Land1999`
130 | - `@Land1999Roles`
131 | - `@LandMennie1999`
132 | - `@LandRusted1999Percept`
133 | - `@land_rusted_1999_percept`
134 | - etc.
135 |
136 | If a name contains spaces, you can indicate this using a `+` character. So the following reference ...
137 |
138 | Van Zoest, W., & Donk, M. (2005). The effects of salience on saccadic target selection. *Visual Cognition*, *12*(2), 353–375.
139 |
140 | ... matches any of the following terms:
141 |
142 | - `@Van+zoestDonk2005`
143 | - `@van+zoest_donk_2005`
144 |
145 | Note that you must consistently use the same citation to refer to a single reference in one document. If a citation matched multiple references from your Zotero database, one citation will be chosen at random.
146 |
147 | ### Sorting citations
148 |
149 | Pandoc does not allow you to sort your references, which can be annoying. To get around this, Academic Markdown allows you to explicitly sort your citations by linking chains of citations with a `+` character:
150 |
151 | [@Zzz2014]+[@Aaa2014]
152 |
153 | ### Clearing cache
154 |
155 | Previous references will be cached automatically. To refresh, remove the file `.zoteromarkdown.cache` or run your Python script with the command-line argument: `--clear-cache`.
156 |
157 | ## Academic Markdown extensions
158 |
159 | Academic Markdown provides certain extensions to regular Markdown, in the form of YAML blocks embedded in `%-- --%` tags. You can which, and the order in which, extensions are called by settings the `extensions` list:
160 |
161 | ~~~ {.python}
162 | from academicmarkdown import build
163 | # First call the include extension, second call the figure extension
164 | build.extensions = [u'include', u'figure']
165 | ~~~
166 |
167 | ### `code`: code listings
168 |
169 | %--
170 | python: |
171 | import inspect
172 | from academicmarkdown import CodeParser
173 | print inspect.getdoc(CodeParser)
174 | --%
175 |
176 | ### `constant`: define constants
177 |
178 | %--
179 | python: |
180 | import inspect
181 | from academicmarkdown import ConstantParser
182 | print inspect.getdoc(ConstantParser)
183 | --%
184 |
185 | ### `exec`: external commands
186 |
187 | %--
188 | python: |
189 | import inspect
190 | from academicmarkdown import ExecParser
191 | print inspect.getdoc(ExecParser)
192 | --%
193 |
194 | ### `figure`: figures
195 |
196 | %--
197 | python: |
198 | import inspect
199 | from academicmarkdown import FigureParser
200 | print inspect.getdoc(FigureParser)
201 | --%
202 |
203 | ### `include`: include other Markdown files
204 |
205 | %--
206 | python: |
207 | import inspect
208 | from academicmarkdown import IncludeParser
209 | print inspect.getdoc(IncludeParser)
210 | --%
211 |
212 | ### `python`: python code
213 |
214 | %--
215 | python: |
216 | import inspect
217 | from academicmarkdown import PythonParser
218 | print inspect.getdoc(PythonParser)
219 | --%
220 |
221 | ### `table`: table
222 |
223 | %--
224 | python: |
225 | import inspect
226 | from academicmarkdown import TableParser
227 | print inspect.getdoc(TableParser)
228 | --%
229 |
230 | ### `toc`: table of contents
231 |
232 | %--
233 | python: |
234 | import inspect
235 | from academicmarkdown import TOCParser
236 | print inspect.getdoc(TOCParser)
237 | --%
238 |
239 | ### `wc`: word count
240 |
241 | %--
242 | python: |
243 | import inspect
244 | from academicmarkdown import WcParser
245 | print inspect.getdoc(WcParser)
246 | --%
247 |
248 | ### Magic variables
249 |
250 | Magic variables are automatically replaced by certain values, and are indicated like this: `%varname%`. The following magic variables are available:
251 |
252 | - `%wc%`: Word count
253 | - `%cc%`: Character count
254 | - `%rc%`: Reference count
255 |
256 | ## License
257 |
258 | Academic Markdown is available under the GNU General Public License 3. For more information, see the included file `COPYING`.
259 |
260 | [pandoc]: http://johnmacfarlane.net/pandoc/
261 | [pyzotero]: http://pyzotero.readthedocs.org/
262 | [zotero]: http://www.zotero.org/
263 | [wkhtmltopdf]: https://code.google.com/p/wkhtmltopdf/
264 | ---
265 | """
266 |
267 | version = u'0.9.1'
268 |
269 | from academicmarkdown._BaseParser import BaseParser
270 | from academicmarkdown._YAMLParser import YAMLParser
271 | from academicmarkdown._ZoteroParser import ZoteroParser
272 | from academicmarkdown._FigureParser import FigureParser
273 | from academicmarkdown._CodeParser import CodeParser
274 | from academicmarkdown._ConstantParser import ConstantParser
275 | from academicmarkdown._ExecParser import ExecParser
276 | from academicmarkdown._PythonParser import PythonParser
277 | from academicmarkdown._IncludeParser import IncludeParser
278 | from academicmarkdown._TOCParser import TOCParser
279 | from academicmarkdown._VideoParser import VideoParser
280 | from academicmarkdown._TableParser import TableParser
281 | from academicmarkdown._Pandoc import Pandoc
282 | from academicmarkdown._ODTFixer import ODTFixer
283 | from academicmarkdown._WkHtmlToPdf import WkHtmlToPdf
284 | from academicmarkdown._WcParser import WcParser
285 | from academicmarkdown._GitHubParser import GitHubParser
286 | from academicmarkdown import build, constants
287 |
--------------------------------------------------------------------------------
/academicmarkdown/build.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | This file is part of zoteromarkdown.
5 |
6 | zoteromarkdown is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | zoteromarkdown is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with zoteromarkdown. If not, see .
18 |
19 | ---
20 | desc:
21 | Contains functions to build documents from Markdown source.
22 | ---
23 | """
24 |
25 | import os
26 | import sys
27 | import subprocess
28 | from academicmarkdown import FigureParser, Pandoc, ZoteroParser, ODTFixer, \
29 | ExecParser, IncludeParser, TOCParser, HTMLFilter, MDFilter, WkHtmlToPdf, \
30 | CodeParser, WcParser, VideoParser, TableParser, PythonParser, \
31 | tools, ConstantParser, GitHubParser
32 | from academicmarkdown.constants import *
33 | from academicmarkdown.py3compat import *
34 |
35 | def HTML(src, target=None, standalone=True):
36 |
37 | """
38 | desc: |
39 | Builds an HTML file from a Markdown source.
40 |
41 | %--
42 | constant:
43 | arg_src:
44 | The Markdown source. If it is a path to an existing file, the
45 | contents of this file are read. Otherwise, the string itself
46 | it used. Should be in utf-8 encoding.
47 | --%
48 |
49 | arguments:
50 | src:
51 | desc: "%arg_src"
52 | type: [str, unicode]
53 |
54 | keywords:
55 | target:
56 | desc: The name of an HTML target file or None to skip saving.
57 | type: [str, unicode, NoneType]
58 | standalone:
59 | desc: Indicates whether a full HTML5 document should be generated,
60 | which embeds all content, or whether the document should
61 | be rendered without `` and `` tags, etc.
62 | type: bool
63 |
64 | returns:
65 | desc: The HTML file as a unicode string.
66 | type: unicode
67 | """
68 |
69 | md = MD(src)
70 | # Count words
71 | print(u'Document statistics:')
72 | print(u'Word count: %d' % len(md.split()))
73 | print(u'Character count: %d' % len(md))
74 | # And finally convert the Markdown to HTML
75 | pd = Pandoc(css=css, csl=csl, template=html5Ref, standalone=standalone, \
76 | verbose=True)
77 | html = pd.parse(md)
78 | for flt in htmlFilters:
79 | fltFunc = getattr(HTMLFilter, flt)
80 | html = fltFunc(html)
81 | if target != None:
82 | open(target, u'wb').write(safe_encode(html))
83 | print(u'Done!')
84 | return html
85 |
86 | def MD(src, target=None):
87 |
88 | """
89 | desc:
90 | Builds a Markdown file from a Markdown source.
91 |
92 | arguments:
93 | src:
94 | desc: "%arg_src"
95 | type: [str, unicode]
96 |
97 | keywords:
98 | target:
99 | desc: The name of a Markdown target file or None to skip saving.
100 | type: [str, unicode, NoneType]
101 |
102 | returns:
103 | desc: The compiled Markdown file as a unicode string.
104 | type: unicode
105 | """
106 |
107 | if os.path.exists(src):
108 | md = safe_decode(open(src).read())
109 | print(u'Building %s from %s ...' % (target, src))
110 | else:
111 | md = src
112 | print(u'Building from string ...')
113 | # Apply pre-processing Markdown Filters
114 | for flt in preMarkdownFilters:
115 | fltFunc = getattr(MDFilter, flt)
116 | md = fltFunc(md)
117 | # Apply all extensions
118 | for ext in extensions:
119 | print(u'Parsing with %s extension ...' % ext)
120 | if u'include' == ext:
121 | md = IncludeParser(verbose=True).parse(md)
122 | elif u'toc' == ext:
123 | md = TOCParser(anchorHeaders=TOCAnchorHeaders, appendHeaderRefs= \
124 | TOCAppendHeaderRefs, verbose=True).parse(md)
125 | elif u'figure' == ext:
126 | md = FigureParser(verbose=True, style=figureStyle, template= \
127 | figureTemplate, margins=pdfMargins).parse(md)
128 | elif u'video' == ext:
129 | md = VideoParser(verbose=True).parse(md)
130 | elif u'table' == ext:
131 | md = TableParser(style=tableStyle, template=tableTemplate, verbose= \
132 | True).parse(md)
133 | elif u'code' == ext:
134 | md = CodeParser(verbose=True, style=codeStyle, template=codeTemplate) \
135 | .parse(md)
136 | elif u'wc' == ext:
137 | md = WcParser(verbose=True).parse(md)
138 | elif u'exec' == ext:
139 | md = ExecParser(verbose=True).parse(md)
140 | elif u'python' == ext:
141 | md = PythonParser(verbose=True).parse(md)
142 | elif u'constant' == ext:
143 | md = ConstantParser(verbose=True).parse(md)
144 | elif u'github' == ext:
145 | md = GitHubParser(verbose=True).parse(md)
146 | else:
147 | raise Exception(u'Unknown Academic Markdown extension: %s' % ext)
148 | # Parse Zotero references
149 | if zoteroApiKey != None and zoteroLibraryId != None:
150 | clearCache = '--clear-cache' in sys.argv
151 | md = ZoteroParser(verbose=True, apiKey=zoteroApiKey, libraryId= \
152 | zoteroLibraryId, headerText=zoteroHeaderText, headerLevel= \
153 | zoteroHeaderLevel, clearCache=clearCache).parse(md)
154 | # Apply post-processing Markdown Filters
155 | for flt in postMarkdownFilters:
156 | fltFunc = getattr(MDFilter, flt)
157 | md = fltFunc(md)
158 | if target != None:
159 | open(target, u'wb').write(safe_encode(md))
160 | return md
161 |
162 | def PDF(src, target, lineNumbers=False, args=''):
163 |
164 | """
165 | desc:
166 | Builds a PDF file from a Markdown source.
167 |
168 | arguments:
169 | src:
170 | desc: "%arg_src"
171 | type: [str, unicode]
172 | target:
173 | desc: The name of a PDF target file.
174 | type: [str, unicode]
175 |
176 | keywords:
177 | lineNumbers:
178 | desc: Determines whether line numbers should be added. This is
179 | currently quite a complicated process, which may break.
180 | type: bool
181 | args:
182 | desc: Indicates extra arguments to be passed onto wkhtmltopdf.
183 | type: [str, unicode]
184 | """
185 |
186 | print(u'Building %s from %s ...' % (target, src))
187 | HTML(src, u'.tmp.html')
188 | wk = WkHtmlToPdf(css=css, margins=pdfMargins, spacing=pdfSpacing, \
189 | header=pdfHeader, footer=pdfFooter, verbose=True, args=args)
190 | if lineNumbers:
191 | _target = u'.tmp.pdf'
192 | else:
193 | _target = target
194 | wk.parse(u'.tmp.html', _target)
195 | if lineNumbers:
196 | tools.addLineNumbersToPDF(_target, target)
197 | os.remove(_target)
198 |
199 | def ODT(src, target):
200 |
201 | """
202 | desc:
203 | Builds an ODT file from a Markdown source.
204 |
205 | arguments:
206 | src:
207 | desc: "%arg_src"
208 | type: [str, unicode]
209 | target:
210 | desc: The name of an ODT target file.
211 | type: [str, unicode]
212 | """
213 |
214 | global figureTemplate
215 | tmp = figureTemplate
216 | figureTemplate = u'odt'
217 | md = MD(src)
218 | pd = Pandoc(csl=csl, verbose=True)
219 | pd.odt(md, target, odtRef=odtRef)
220 | ODTFixer(verbose=True).fix(target)
221 | figureTemplate = tmp
222 |
223 | def DOC(src, target):
224 |
225 | """
226 | desc:
227 | Builds a DOC file from a Markdown source.
228 |
229 | arguments:
230 | src:
231 | desc: "%arg_src"
232 | type: [str, unicode]
233 | target:
234 | desc: The name of a DOC target file.
235 | type: [str, unicode]
236 | """
237 |
238 | # Since pandoc doesn't support DOC output, we convert first to ODT and from
239 | # there use unoconv to convert to DOC.
240 | ODT(src, u'.tmp.odt')
241 | print(u'Converting from .odt to .doc ...')
242 | cmd = [u'unoconv', u'-f', u'doc', u'.tmp.odt']
243 | subprocess.call(cmd)
244 | print(u'Done!')
245 | os.rename(u'.tmp.doc', target)
246 |
247 | def DOCX(src, target):
248 |
249 | """
250 | desc:
251 | Builds a DOCX file from a Markdown source.
252 |
253 | arguments:
254 | src:
255 | desc: "%arg_src"
256 | type: [str, unicode]
257 | target:
258 | desc: The name of a DOCX target file.
259 | type: [str, unicode]
260 | """
261 |
262 | global figureTemplate
263 | tmp = figureTemplate
264 | figureTemplate = u'markdown'
265 | md = MD(src)
266 | pd = Pandoc(csl=csl, verbose=True)
267 | pd.docx(md, target, docxRef=docxRef)
268 | figureTemplate = tmp
269 |
270 | def setStyle(style):
271 |
272 | """
273 | desc:
274 | Automatically sets a style.
275 |
276 | arguments:
277 | style:
278 | desc: The style name. This should be the name of a folder that
279 | contains style files. See the `academicmarkdown\styles`
280 | subfolder for examples.
281 | type: [str, unicode]
282 | """
283 |
284 | global css, csl, html5Ref, odtRef, docxRef
285 | moduleFolder = safe_decode(os.path.dirname(__file__),
286 | enc=sys.getfilesystemencoding())
287 | if os.path.exists(style):
288 | stylePath = style
289 | elif os.path.exists(os.path.join(moduleFolder, u'styles', style)):
290 | stylePath = os.path.join(moduleFolder, u'styles', style)
291 | else:
292 | raise Exception(u'There is no style folder named "%s"' % style)
293 | print(u'Using style folder: %s' % stylePath)
294 | css = os.path.join(stylePath, u'stylesheet.css')
295 | if not os.path.exists(css):
296 | css = None
297 | csl = os.path.join(stylePath, u'citation-style.csl')
298 | if not os.path.exists(csl):
299 | csl = None
300 | html5Ref = os.path.join(stylePath, u'template.html')
301 | if not os.path.exists(html5Ref):
302 | html5Ref = None
303 | odtRef = os.path.join(stylePath, u'reference.odt')
304 | if not os.path.exists(odtRef):
305 | odtRef = None
306 | docxRef = os.path.join(stylePath, u'reference.docx')
307 | if not os.path.exists(docxRef):
308 | docxRef = None
309 |
--------------------------------------------------------------------------------
/academicmarkdown/constants.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | This file is part of zoteromarkdown.
4 |
5 | zoteromarkdown is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | zoteromarkdown is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with zoteromarkdown. If not, see .
17 |
18 | ---
19 | desc: |
20 | Contains the settings, which are imported into `academicmarkdown.build`. You
21 | can change these settings in the `build` module, as shown below.
22 |
23 | __Module source:__
24 |
25 | %--
26 | code:
27 | id: LstConstants
28 | syntax: python
29 | source: academicmarkdown/constants.py
30 | --%
31 |
32 | example: |
33 | from academicmarkdown import build
34 | build.pdfHeader = u'A header for my PDF'
35 | ---
36 | """
37 |
38 | from academicmarkdown.py3compat import *
39 | import os, sys
40 |
41 | # A list of folders that are searched for figures, scripts, etc.
42 | path = [safe_decode(os.getcwd(), enc=sys.getfilesystemencoding())]
43 |
44 | # Parameters for Zotero integration
45 | zoteroApiKey = None
46 | zoteroLibraryId = None
47 | zoteroHeaderText = u'References'
48 | zoteroHeaderLevel = 1
49 |
50 | # Options for the appearance of figures, blocks, and tables
51 | figureTemplate = u'html5'
52 | figureStyle = u'inline'
53 | codeTemplate = u'pandoc'
54 | codeStyle = u'inline'
55 | tableTemplate = u'html5'
56 | tableStyle = u'inline'
57 |
58 | # Indicates whether headers should be turned into clickable anchors by TOCParser
59 | TOCAnchorHeaders = False
60 | # Indicates whether references to header ids should be automatically appended
61 | # to the main text.
62 | TOCAppendHeaderRefs = True
63 |
64 | # Paths to files that determine the document's appearance. For more information,
65 | # see the Pandoc documentation.
66 | css = None # CSS stylesheet
67 | csl = None # CSL citation style
68 | html5Ref = None # HTML5 template
69 | odtRef = None # ODT reference document
70 | docxRef = None # DOCX reference document
71 |
72 | # A list of filters from academicmarkdown.HTMLFilter that should be performed
73 | # after an HTML document has been genertated.
74 | htmlFilters = [u'DOI', u'citationGlue']
75 |
76 | # A list of filters from academicmarkdown.MDFilter that should be performed
77 | # on the Markdown source, prior to any processing.
78 | preMarkdownFilters = []
79 | # A list of filters from academicmarkdown.MDFilter that should be performed
80 | # on the Markdown source, after all other processing has been performed
81 | postMarkdownFilters = [u'autoItalics', u'pageBreak', u'magicVars', u'highlight',
82 | u'arrows']
83 |
84 | # A list of extensions that are enabled.
85 | extensions = [u'include', u'exec', u'python', u'toc', u'code', u'video', \
86 | u'table', u'figure', u'constant', u'wc', u'github']
87 |
88 | # The page margins
89 | pdfMargins = 30, 20, 30, 20
90 |
91 | # The spacing between the content and the header and footer
92 | pdfSpacing = 10, 10
93 |
94 | # Header and footer text
95 | pdfHeader = u'%section%'
96 | pdfFooter = u'%page% of %topage%'
97 |
--------------------------------------------------------------------------------
/academicmarkdown/git.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | """
5 | This file is part of academicmarkdown.
6 |
7 | academicmarkdown is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | academicmarkdown is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with academicmarkdown. If not, see .
19 | """
20 |
21 | from academicmarkdown import build
22 | from subprocess import check_output, call
23 | import os
24 | import shlex
25 |
26 | exportFolder = u'export'
27 | exportFormats = u'odt', u'pdf', u'doc'
28 |
29 | def commitHash():
30 |
31 | """
32 | Gets the latest commit hash.
33 |
34 | Returns:
35 | A unicode string with the latest hash.
36 | """
37 |
38 | cmd = [u'git', u'log', u'--pretty=format:#%h', u'-1']
39 | return check_output(cmd)
40 |
41 | def snapshot(src, msg=u'snapshot', pdfArgs={}):
42 |
43 | """
44 | Commits the current state of the repository and exports a snapshot of the
45 | current documents.
46 |
47 | Arguments:
48 | src -- The source Markdown document.
49 |
50 | Keyword arguments:
51 | msg -- A commit message. (default=u'snapshot')
52 | """
53 |
54 | cmd = [u'git', u'commit', u'-am', msg]
55 | print(u'Committing (msg: %s)' % msg)
56 | call(cmd)
57 | cmd = [u'git', u'log', u'--pretty=format:[%cd #%h] %s', u'--date=iso', \
58 | u'-1']
59 | tag = check_output(cmd).decode()
60 | folder = os.path.join(exportFolder, tag)
61 | print(u'Exporting to %s' % folder)
62 | if os.path.exists(folder):
63 | raise Exception( \
64 | u'Folder %s already exists! There is probably nothing new to export.' \
65 | % folder)
66 | os.mkdir(folder)
67 | if u'pdf' in exportFormats:
68 | build.PDF(src, os.path.join(folder, u'export.pdf'), **pdfArgs)
69 | if u'doc' in exportFormats:
70 | build.DOC(src, os.path.join(folder, u'export.doc'))
71 | if u'docx' in exportFormats:
72 | build.DOCX(src, os.path.join(folder, u'export.docx'))
73 | if u'odt' in exportFormats:
74 | build.ODT(src, os.path.join(folder, u'export.odt'))
75 | if u'html' in exportFormats:
76 | build.HTML(src, os.path.join(folder, u'export.html'))
77 |
--------------------------------------------------------------------------------
/academicmarkdown/py3compat.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | This file is part of zoteromarkdown.
5 |
6 | zoteromarkdown is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | zoteromarkdown is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with zoteromarkdown. If not, see .
18 | """
19 |
20 | import sys
21 |
22 | if sys.version_info >= (3,0,0):
23 | py3 = True
24 | basestring = str
25 | else:
26 | bytes = str
27 | str = unicode
28 | py3 = False
29 |
30 | def safe_decode(s, enc='utf-8', errors='strict'):
31 | if isinstance(s, str):
32 | return s
33 | return s.decode(enc, errors)
34 |
35 | def safe_encode(s, enc='utf-8', errors='strict'):
36 | if isinstance(s, bytes):
37 | return s
38 | return s.encode(enc, errors)
39 |
40 | __all__ = ['py3', 'safe_decode', 'safe_encode', 'safe_str']
41 | if not py3:
42 | safe_str = safe_encode
43 | __all__ += ['str', 'bytes']
44 | else:
45 | safe_str = safe_decode
46 | __all__ += ['basestring']
47 |
--------------------------------------------------------------------------------
/academicmarkdown/styles/apa/citation-style.csl:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/academicmarkdown/styles/apa/reference.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smathot/academicmarkdown/696592d035e341e170b6c3e86a0855a8cc4d0f29/academicmarkdown/styles/apa/reference.docx
--------------------------------------------------------------------------------
/academicmarkdown/styles/apa/reference.odt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smathot/academicmarkdown/696592d035e341e170b6c3e86a0855a8cc4d0f29/academicmarkdown/styles/apa/reference.odt
--------------------------------------------------------------------------------
/academicmarkdown/styles/apa/stylesheet.css:
--------------------------------------------------------------------------------
1 | * {
2 | font-family: "Times New Roman";
3 | font-size: 12pt;
4 | line-height: 200%;
5 | text-align: left;
6 | margin: 0px;
7 | }
8 |
9 | p {
10 | text-indent: 32px;
11 | text-align: justify;
12 | }
13 |
14 | a {
15 | text-decoration: none;
16 | }
17 |
18 | a, em, strong {
19 | font-size: 1em;
20 | font-family: inherit;
21 | }
22 |
23 | em, strong {
24 | color: inherit;
25 | }
26 |
27 | code {
28 | font-family: 'Courier New';
29 | white-space: pre-wrap;
30 | }
31 |
32 | blockquote {
33 | margin: 16px 48px 16px 48px;
34 | font-style: italic;
35 | }
36 |
37 | blockquote p {
38 | text-indent: 0px!important;
39 | }
40 |
41 | sup {
42 | font-size: 0.7em;
43 | }
44 |
45 | h1 {
46 | text-align: center;
47 | font-size: 1.0em;
48 | font-weight: normal;
49 | page-break-after: avoid;
50 | }
51 |
52 | h1, h2, h3 {
53 | color: #204a87;
54 | margin: 15px 0px 5px 0px;
55 | }
56 |
57 | h2 {
58 | font-weight: normal;
59 | font-size: 1.0em;
60 | font-style: italic;
61 | }
62 |
63 | h3 {
64 | font-weight: normal;
65 | font-size: 1.0em;
66 | font-style: italic;
67 | text-indent: 32px;
68 | }
69 |
70 | #titlepage .title,
71 | #titlepage .correspondence,
72 | #titlepage .authornote,
73 | #titlepage .author,
74 | #titlepage .affiliation p {
75 | text-align: center!important;
76 | text-indent: 0px!important;
77 | }
78 |
79 | #titlepage .authornote {
80 | margin-top: 32px;
81 | }
82 |
83 | #titlepage .authornote p,
84 | #titlepage .correspondence p {
85 | text-indent: 0px!important;
86 | }
87 |
88 | #titlepage .runninghead {
89 | text-indent: 0px!important;
90 | margin-bottom: 32px;
91 | }
92 |
93 | #titlepage .affiliation,
94 | #titlepage .title {
95 | margin-bottom: 100px;
96 | }
97 |
98 | #titlepage .author {
99 | margin-bottom: 25px;
100 | }
101 |
102 | figcaption strong {
103 | color: #204a87;
104 | }
105 |
106 | figure {
107 | display: table;
108 | margin: 25px 0px 25px 0px;
109 | page-break-inside: avoid;
110 | caption-side: bottom;
111 | width: 100%;
112 | }
113 |
114 | figure img {
115 | }
116 |
117 | figure figcaption {
118 | display: table-caption;
119 | font-style: italic;
120 | font-size: 0.9em;
121 | width: 100%;
122 | }
123 |
124 | a,
125 | .citation {
126 | color: #204a87;
127 | }
128 |
129 | figure figcaption .citation {
130 | font-size: 0.9em;
131 | }
132 |
133 | *[id^='ref-'],
134 | .references p {
135 | text-align: left;
136 | text-indent: -25px;
137 | margin-left: 25px;
138 | }
139 |
140 | .footer, .header {
141 | text-align: right;
142 | font-size: 12pt;
143 | }
144 |
145 | .footer span, .header span {
146 | font-size: 1em;
147 | }
148 |
149 | .table {
150 | margin: 25px 0px 25px 0px;
151 | page-break-inside: avoid;
152 | }
153 |
154 | .table table th {
155 | border-top: solid 1px;
156 | border-bottom: solid 1px;
157 | font-style: normal;
158 | font-weight: bold;
159 | font-size: 1em;
160 | }
161 |
162 | .table table {
163 | width: 100%;
164 | border-collapse: collapse;
165 | padding: 0px;
166 | }
167 |
168 | .table p {
169 | text-indent: 0px;
170 | }
171 |
172 | .table .table-id {
173 | color: #204a87;
174 | font-size: 0.9em;
175 | }
176 |
177 | .table table td {
178 | font-size: 0.9em;
179 | }
180 |
181 | .table .caption {
182 | font-style: italic;
183 | font-size: 0.9em;
184 | }
185 |
186 | .highlight {
187 | background-color: #ffffaf;
188 | }
189 |
--------------------------------------------------------------------------------
/academicmarkdown/styles/apa/template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | $for(author-meta)$
7 |
8 | $endfor$
9 | $if(date-meta)$
10 |
11 | $endif$
12 | $if(title-prefix)$$title-prefix$ - $endif$$pagetitle$
13 |
14 | $if(quotes)$
15 |
16 | $endif$
17 | $if(highlighting-css)$
18 |
21 | $endif$
22 | $for(css)$
23 |
24 | $endfor$
25 | $if(math)$
26 | $math$
27 | $endif$
28 | $for(header-includes)$
29 | $header-includes$
30 | $endfor$
31 |
32 |
33 |
34 |
35 | $if(title)$
36 |
37 |
38 | $if(runninghead)$
39 |
50 | $endfor$
51 |
52 |
53 | $endif$
54 |
55 |
56 | $body$
57 | $for(include-after)$
58 | $include-after$
59 | $endfor$
60 |
61 |
62 |
--------------------------------------------------------------------------------
/academicmarkdown/tools.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | This file is part of zoteromarkdown.
5 |
6 | zoteromarkdown is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | zoteromarkdown is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with zoteromarkdown. If not, see .
18 | """
19 |
20 | import re
21 | import os
22 | from academicmarkdown.py3compat import *
23 |
24 | def wordCount(s, excludeYAML=True, clean=True):
25 |
26 | """
27 | Returns the word count of a file or a string of text.
28 |
29 | Arguments:
30 | s -- A filename or a string of text. This paramater can also
31 | be a list of filenames or a list of strings of text, in
32 | which case the summed word count will be returned.
33 |
34 | Keyword arguments:
35 | excludeYAML -- Indicates whether the contents of %-- --% YAML blocks
36 | should be excluded from the word count. (default=True)
37 | clean -- Indicates whether the text should be cleaned of things
38 | that you probably don't want to count, such as `##`
39 | characters. (default=True)
40 |
41 | Returns:
42 | A word count.
43 | """
44 |
45 | if isinstance(s, list):
46 | wc = 0
47 | for _s in s:
48 | wc += wordCount(_s, excludeYAML=excludeYAML, clean=clean)
49 | return wc
50 | if os.path.exists(s):
51 | s = safe_decode(open(s).read())
52 | if excludeYAML:
53 | s = re.sub(u'%--(.*?)--%', lambda x: u'', s, flags=re.M|re.S)
54 | if clean:
55 | s = re.sub(u'^#+\s', lambda x: u'', s, flags=re.M)
56 | l = []
57 | for w in s.split():
58 | if clean:
59 | w = re.sub(r'[^a-zA-Z0-9]', u'', w)
60 | if len(w) > 0:
61 | l.append(w)
62 | return len(l)
63 |
64 | def addLineNumbersToPDF(inFile, outFile, color='#d3d7cf'):
65 |
66 | """
67 | desc:
68 | Adds line numbers to a PDF file.
69 |
70 | arguments:
71 | inFile:
72 | desc: The name of the input PDF.
73 | type: str, unicode
74 | outFile:
75 | desc: The name of the output PDF.
76 | type: str, unicode
77 |
78 | keywords:
79 | color:
80 | desc: An HTML-style color name.
81 | type: str, unicode
82 | """
83 |
84 | import os
85 | import shutil
86 | import subprocess
87 | try:
88 | from scipy.ndimage import imread
89 | except ImportError:
90 | from imageio import imread
91 | import numpy as np
92 | from PIL import Image, ImageDraw, ImageFont
93 |
94 | #fontFile = '/usr/share/fonts/truetype/msttcorefonts/Times_New_Roman.ttf'
95 | fontFile = '/usr/share/fonts/truetype/freefont/FreeSans.ttf'
96 | fontSize = 20
97 | tmpFolder = u'line-numbers-tmp'
98 | pageFolder = u'%s/page' % tmpFolder
99 | watermarkFolder = u'%s/watermark' % tmpFolder
100 |
101 | try:
102 | shutil.rmtree(tmpFolder)
103 | except:
104 | pass
105 | os.makedirs(pageFolder)
106 | os.makedirs(watermarkFolder)
107 |
108 | print(u'Adding line numbers to PDF')
109 | print(u'Converting ...')
110 | cmd = u'convert -density 150 %s %s' % (inFile, os.path.join(pageFolder,
111 | u'%03d.png'))
112 | subprocess.call(cmd.split())
113 | print(u'Done!')
114 | # Create watermarks for all pages
115 | for path in os.listdir(pageFolder):
116 | try:
117 | im = imread(os.path.join(pageFolder, path), flatten=True)
118 | except TypeError:
119 | im = imread(os.path.join(pageFolder, path), as_gray=True)
120 | # Create a list of indices that have text on them
121 | nonEmptyRows = np.where(im.mean(axis=1) != 255)[0]
122 | # Store the rows (i.e.) y coordinates of all to-be-numbered-rows
123 | numberRows =[]
124 | firstRow = None
125 | for row in nonEmptyRows:
126 | if im[row-1].mean() == 255:
127 | numberRows.append(row)
128 | print(u'Found %d lines!' % len(numberRows))
129 | # Create watermark image
130 | print(u'Creating watermark ...')
131 | font = ImageFont.truetype(fontFile, fontSize)
132 | wm = Image.new('RGBA', (im.shape[1], im.shape[0]))
133 | dr = ImageDraw.Draw(wm)
134 | i = 1
135 | for row in numberRows:
136 | dr.text((32, row), '%s' % i, font=font, fill=color)
137 | i += 1
138 | wm.save(os.path.join(watermarkFolder, path))
139 | print(u'Done!')
140 |
141 | print(u'Creating watermark pdf ...')
142 | cmd = 'convert %s/*.png watermark.pdf' % watermarkFolder
143 | subprocess.call(cmd.split())
144 | print(u'Done!')
145 |
146 | print(u'Merging watermark and source document ...')
147 | cmd = u'pdftk %s multibackground watermark.pdf output %s' \
148 | % (inFile, outFile)
149 | subprocess.call(cmd.split())
150 | print(u'Done!')
151 |
152 | print(u'Cleaning up ...')
153 | shutil.rmtree(tmpFolder)
154 | print(u'Done')
155 |
--------------------------------------------------------------------------------
/debian/changelog:
--------------------------------------------------------------------------------
1 | python-academicmarkdown (0.8.1-ubuntu1) trusty; urgency=medium
2 |
3 | * Fix a bug in TOC generation for non-ascii headers
4 |
5 | -- Sebastiaan Mathot Mon, 10 Nov 2014 14:59:44 +0100
6 |
7 | python-academicmarkdown (0.8.0-ubuntu1) trusty; urgency=medium
8 |
9 | * Various improvements and fixes
10 |
11 | -- Sebastiaan Mathot Thu, 30 Oct 2014 12:23:02 +0100
12 |
13 | python-academicmarkdown (0.7.2-ubuntu1) saucy; urgency=low
14 |
15 | * Fix and guess year in ZoteroParser
16 | * Implement pandoc-style table
17 |
18 | -- Sebastiaan Mathot Wed, 19 Mar 2014 15:22:28 +0100
19 |
20 | python-academicmarkdown (0.7.1-ubuntu1) saucy; urgency=low
21 |
22 | * Also format X2(X) = X and t = X style statistics
23 | * Remove URL from book references
24 |
25 | -- Sebastiaan Mathot Mon, 17 Mar 2014 16:26:22 +0100
26 |
27 | python-academicmarkdown (0.7.0-ubuntu1) saucy; urgency=low
28 |
29 | * Add Python parser
30 | * Accept URL paths
31 |
32 | -- Sebastiaan Mathot Thu, 13 Mar 2014 18:09:50 +0100
33 |
34 | python-academicmarkdown (0.6.0-ubuntu1) saucy; urgency=low
35 |
36 | * Add word-count parser (WcParser)
37 | * Print out used references when duplicates are encountered
38 | * Move magicVars to postMarkDownFilers
39 |
40 | -- Sebastiaan Mathot Mon, 20 Jan 2014 13:18:15 +0100
41 |
42 | python-academicmarkdown (0.5.1-ubuntu1) saucy; urgency=low
43 |
44 | * Fix TOC header links
45 | * Fix deep-level headers in TOC
46 |
47 | -- Sebastiaan Mathot Fri, 03 Jan 2014 14:00:13 +0100
48 |
49 | python-academicmarkdown (0.5.0-ubuntu1) saucy; urgency=low
50 |
51 | * Various improvements and bugfixes
52 |
53 | -- Sebastiaan Mathot Sun, 08 Dec 2013 16:23:06 +0100
54 |
55 | python-academicmarkdown (0.4.0-ubuntu4) saucy; urgency=low
56 |
57 | * Various improvements and bugfixes
58 |
59 | -- Sebastiaan Mathot Wed, 06 Nov 2013 17:09:01 +0100
60 |
61 | python-academicmarkdown (0.3.0-ubuntu1) saucy; urgency=low
62 |
63 | * Various improvements and bugfixes
64 |
65 | -- Sebastiaan Mathot Mon, 28 Oct 2013 12:06:07 +0100
66 |
67 | python-academicmarkdown (0.2.0-ubuntu1) saucy; urgency=low
68 |
69 | * Add pageBreak filter
70 | * Various bugfixes
71 |
72 | -- Sebastiaan Mathot Mon, 21 Oct 2013 17:41:12 +0200
73 |
74 | python-academicmarkdown (0.1.1-ubuntu3) raring; urgency=low
75 |
76 | * Initial release
77 |
78 | -- Sebastiaan Mathot Sun, 20 Oct 2013 14:02:10 +0200
79 |
--------------------------------------------------------------------------------
/debian/compat:
--------------------------------------------------------------------------------
1 | 7
2 |
--------------------------------------------------------------------------------
/debian/control:
--------------------------------------------------------------------------------
1 | Source: python-academicmarkdown
2 | Section: science
3 | Priority: extra
4 | Maintainer: Sebastiaan Mathot
5 | Build-Depends: debhelper (>= 7.0.50~), python-support, python-all,
6 | python-setuptools, python-simplejson, python-yaml
7 | XS-Python-Version: >= 2.6
8 | Standards-Version: 3.9.3
9 | Vcs-Git: git://github.com:smathot/academicmarkdown.git
10 | Homepage: https://github.com/smathot/academicmarkdown
11 |
12 | Package: python-academicmarkdown
13 | Architecture: all
14 | XB-Python-Version: ${python:Versions}
15 | Depends: ${misc:Depends}, ${python:Depends}
16 | Recommends: zotero-standalone, pandoc, wkhtmltopdf, python-pyzotero, unoconv,
17 | python-dateutil
18 | Description: A Python package for generating scientific documents using
19 | Markdown.
20 |
21 |
--------------------------------------------------------------------------------
/debian/copyright:
--------------------------------------------------------------------------------
1 | Format: Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
2 | Upstream-Name: python-academic markdown
3 | Maintainer-Contact: Sebastiaan Mathot
4 | Source: https://github.com/smathot/academicmarkdown
5 |
6 | Files: *
7 | Copyright: 2013, Sebastiaan Mathôt
8 | Licenses: GPL-3
9 | On Debian systems, the full text of the GNU General Public License version 3
10 | can be found in the file `/usr/share/common-licenses/GPL-3'.
11 |
--------------------------------------------------------------------------------
/debian/rules:
--------------------------------------------------------------------------------
1 | #!/usr/bin/make -f
2 | %:
3 | dh $@
4 |
--------------------------------------------------------------------------------
/example/compile.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #-*- coding:utf-8 -*-
3 |
4 | """
5 | This file is part of zoteromarkdown.
6 |
7 | zoteromarkdown is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | zoteromarkdown is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with zoteromarkdown. If not, see .
19 | """
20 |
21 | import academicmarkdown
22 | from academicmarkdown import build
23 | import myZoteroCredentials
24 | build.path.append(u'example/src')
25 | build.zoteroLibraryId = myZoteroCredentials.zoteroLibraryId
26 | build.zoteroApiKey = myZoteroCredentials.zoteroApiKey
27 | build.setStyle('modern')
28 | build.pdfHeader = u'Generated with academicmarkdown %s' \
29 | % academicmarkdown.version
30 | build.PDF('example/src/example.md', 'example/example.pdf')
31 | build.HTML('example/src/example.md', 'example/example.html')
32 |
--------------------------------------------------------------------------------
/example/example.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smathot/academicmarkdown/696592d035e341e170b6c3e86a0855a8cc4d0f29/example/example.pdf
--------------------------------------------------------------------------------
/example/src/example.md:
--------------------------------------------------------------------------------
1 | ---
2 | title:
3 | A bit about birds looking sideways
4 | author:
5 | Sebastiaan Mathôt^1,\*^
6 | affiliation:
7 | ^1^Aix-Marseille Université, CNRS, Laboratoire de Psychologie Cognitive
8 | correspondence:
9 | ^\*^Correspondence should be addressed to
10 | ---
11 |
12 | This afternoon I was eating a sub in the Plymouth harbor, finally enjoying a bit of sun, which we haven't seen much of this summer. I was joined by a seagull chick. It was presumably hoping to score a piece of my sub...
13 |
14 | # The fovea
15 |
16 | As you probably know, we see only a small part of our surroundings with high resolution and in color [@Betts2013Anatomy]. This is the part that falls onto our fovea, a small, extra dense part of the retina (see %FigFA). Foveal vision corresponds to about the size of a thumb at arm's length. Yet we feel as though we have a complete and full-color perception of our entire visual field. In large part, this is because our eyes are mobile: If we think about something, we immediately look at it (bring it into foveal vision) to get a crisp view of the object in question.
17 |
18 | %--
19 | figure:
20 | id: FigFA
21 | source: foveal_acuity.png
22 | caption: "Visual acuity drops of rapidly with distance from the fovea (source: [Wikipedia](http://en.wikipedia.org/wiki/Fovea_centralis))"
23 | width: 30%
24 | --%
25 |
26 | So what does this have to do with the gull chick? Well, for birds it's much the same, but with a twist. Many birds don't have a single fovea (per eye), like we do, but two [@LandNilsson2002]. (The details differ between species, but I believe the following applies to many species except birds of prey.) They have a temporal fovea, which is like ours in the sense that it looks straight ahead and offers binocular vision (i.e. the temporal foveas of both eyes point in the same direction). But birds also have a central fovea, which points sideways and is, obviously, monocular (i.e., the central foveas of both eyes look in opposite directions).
27 |
28 | %--
29 | figure:
30 | id: FigGV
31 | source: gullvision.png
32 | caption: "Some birds can look in front of them and sideways (Adapted from [Wikipedia](http://en.wikipedia.org/wiki/File:Seagulls_Talking.JPG))"
33 | width: 30%
34 | --%
35 |
36 | So when a bird wants to look at something it has a choice: It can look straight ahead with its temporal foveas, to the left with the central fovea of its left eye, or to the right with the central fovea of its right eye (see %FigGV). And this is not a hypothetical possibility: Birds actually do switch between foveas all the time [@Dawkins2002AnimBehav]! This is why they tend to swing their heads erratically in turns of about 90°, as you can see in the video above. And this is also why, according to Michael F. Land [-@Land1999RolesHead] "it is frustratingly difficult to tell what a bird is actually attending to." (This quote is actually taken a bit out of context, but it applies quite well anyway.)
37 |
38 | # Vision science
39 |
40 | As a vision scientist, I find this intriguing, because it completely goes against our (totally anthropocentric) ideas about vision. Basically all theories about vision assume that there is a single fovea, used to look at one object at a time, and that consequently we attend to only a single object (more or less) at a time. But how well does this translate to birds? Do birds have a more distributed awareness of their environment, as a consequence of their having multiple foveas? There is some research, notably by Robert Cook [-@Cook2000CurrDirPsychSci], that suggests that perception and attention in birds is actually similar to that in humans, but, in general, I believe that this is an open (and interesting!) question.
41 |
42 | *%-- exec: "date +'Generated: %x'" --%*
43 |
--------------------------------------------------------------------------------
/example/src/foveal_acuity.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smathot/academicmarkdown/696592d035e341e170b6c3e86a0855a8cc4d0f29/example/src/foveal_acuity.png
--------------------------------------------------------------------------------
/example/src/gullvision.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smathot/academicmarkdown/696592d035e341e170b6c3e86a0855a8cc4d0f29/example/src/gullvision.png
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # *module* academicmarkdown
4 |
5 | *Who knew writing could be so nerdy?*
6 |
7 | version 0.8.1
8 |
9 |
10 | Copyright 2013-2014 Sebastiaan Mathôt
11 |
12 | ## Contents
13 |
14 |
15 | - [*module* academicmarkdown](#module-academicmarkdown)
16 | - [About](#about)
17 | - [Examples](#examples)
18 | - [Download](#download)
19 | - [Basic usage](#basic-usage)
20 | - [Dependencies](#dependencies)
21 | - [Zotero references](#zotero-references)
22 | - [Pandoc citation style](#pandoc-citation-style)
23 | - [Zotero API key and library ID](#zotero-api-key-and-library-id)
24 | - [Citation identifiers](#citation-identifiers)
25 | - [Sorting citations](#sorting-citations)
26 | - [Clearing cache](#clearing-cache)
27 | - [Academic Markdown extensions](#academic-markdown-extensions)
28 | - [`code`: code listings](#code-code-listings)
29 | - [`constant`: define constants](#constant-define-constants)
30 | - [`exec`: external commands](#exec-external-commands)
31 | - [`figure`: figures](#figure-figures)
32 | - [`include`: include other Markdown files](#include-include-other-markdown-files)
33 | - [`python`: python code](#python-python-code)
34 | - [`table`: table](#table-table)
35 | - [`toc`: table of contents](#toc-table-of-contents)
36 | - [`wc`: word count](#wc-word-count)
37 | - [Magic variables](#magic-variables)
38 | - [License](#license)
39 | - [*module* academicmarkdown.build](#module-academicmarkdownbuild)
40 | - [function __academicmarkdown\.build\.DOC__\(src, target\)](#function-__academicmarkdownbuilddoc__src-target)
41 | - [function __academicmarkdown\.build\.DOCX__\(src, target\)](#function-__academicmarkdownbuilddocx__src-target)
42 | - [function __academicmarkdown\.build\.HTML__\(src, target=None, standalone=True\)](#function-__academicmarkdownbuildhtml__src-targetnone-standalonetrue)
43 | - [function __academicmarkdown\.build\.MD__\(src, target=None\)](#function-__academicmarkdownbuildmd__src-targetnone)
44 | - [function __academicmarkdown\.build\.ODT__\(src, target\)](#function-__academicmarkdownbuildodt__src-target)
45 | - [function __academicmarkdown\.build\.PDF__\(src, target, args=u'', lineNumbers=False\)](#function-__academicmarkdownbuildpdf__src-target-argsu-linenumbersfalse)
46 | - [function __academicmarkdown\.build\.setStyle__\(style\)](#function-__academicmarkdownbuildsetstyle__style)
47 | - [*module* academicmarkdown.constants](#module-academicmarkdownconstants)
48 |
49 |
50 |
51 | ## About
52 |
53 | Academic Markdown is a Python module for generating `.md`, `.html`, `.pdf`,
54 | `.docx`, and `.odt` files from Markdown source. [Pandoc] is used for most of
55 | the heavy lifting, so refer to the Pandoc website for detailed information
56 | about writing in Pandoc Markdown. However, Academic Markdown offers some
57 | additional functionality that is useful for writing scientific documents,
58 | such as integration with [Zotero references], and a number of useful
59 | [Academic Markdown extensions].
60 |
61 | At present, the main target for Academic Markdown is the OpenSesame
62 | documentation site, , although it may in time grow
63 | into a more comprehensive and user-friendly tool.
64 |
65 | ## Examples
66 |
67 | A basic example can be found in the `example` sub folder, included with the source.
68 |
69 | The following manuscripts have been written in Academic Markdown:
70 |
71 | - Mathôt S, Dalmaijer ES, Grainger J, Van der Stigchel S. (2014) The pupillary light response reflects exogenous attention and inhibition of return. *PeerJ PrePrints* 2:e422v1 ([source](https://github.com/smathot/materials_for_P0009.1/tree/master/manuscript))
72 | - Mathôt S, van der Linden L, Grainger J, Vitu F. (2014) The pupillary light response reflects eye-movement preparation. *PeerJ PrePrints* 2:e238v2 ([source](https://github.com/smathot/materials_for_P0001/tree/master/manuscript))
73 |
74 | ## Download
75 |
76 | You can download the latest release of Academic Markdown here:
77 |
78 | -
79 |
80 | Ubuntu users can install Academic Markdown from the Cogsci.nl PPA:
81 |
82 | sudo add-apt-repository ppa:smathot/cogscinl
83 | sudo apt-get update
84 | sudo apt-get install python-academicmarkdown
85 |
86 | ## Basic usage
87 |
88 | Academic Markdown assumes that input files are encoded with `utf-8` encoding.
89 |
90 | ~~~ {.python}
91 | from academicmarkdown import build
92 | build.HTML(u'input.md', u'output.html')
93 | build.HTML(u'input.md', u'output.html', standalone=False)
94 | build.PDF(u'input.md', u'output.pdf')
95 | build.DOCX(u'input.md', u'output.docx')
96 | build.ODT(u'input.md', u'output.odt')
97 | ~~~
98 |
99 | A number of options can be specified by setting attributes of the `build` module, like so
100 |
101 | ~~~ {.python}
102 | build.spacing = 30, 0
103 | ~~~
104 |
105 | The full list of options is available in `academicmarkdown/constants.py`, or see [academicmarkdown.constants].
106 |
107 | ## Dependencies
108 |
109 | Academic Markdown has been tested exclusively on Ubuntu Linux. The following dependencies are required:
110 |
111 | - [pandoc] is used for most of the heavy lifting. At the time of writing, the Ubuntu repositories do not contain a sufficiently recent version of Pandoc. Therefore, if you encounter trouble, try installing the latest version of Pandoc manually.
112 | - [pyzotero] is necessary for extracting Zotero references.
113 | - [wkhtmltopdf] is necessary for converting to `.pdf`. For best results, use the latest statically linked release, instead of the version from the Ubuntu repositories.
114 |
115 | ## Zotero references
116 |
117 | ### Pandoc citation style
118 |
119 | Since the basic Markdown conversion is performed by Pandoc, citations should be formatted as described on the Pandoc site:
120 |
121 | -
122 |
123 | ### Zotero API key and library ID
124 |
125 | You can automatically extract references from your Zotero library by setting the `zoteroApiKey` and `zoteroLibraryId` properties. Your references are not extracted from your local Zotero database, but through the web API of . This means that you need to have a Zotero account and synchronize your local database with your account, in order to use this feature. You can find your your API key and library ID online on your Zotero profile page.
126 |
127 | ~~~ {.python}
128 | from academicmarkdown import build
129 | build.zoteroApiKey = u'myapikey'
130 | build.zoteroLibraryId = u'mylibraryid'
131 | build.PDF(u'input.md', u'output.pdf')
132 | ~~~
133 |
134 | ### Citation identifiers
135 |
136 | Citations are split into separate terms using camelcase or undescore logic. An example of an underscore-style citation is `@bárány_halldén_1948`. And example of a camelcase-style citation is `@Land1999WhyAnimals`. Each citation is interpreted as a series of author names, followed by the year of publication, optionally followed by terms that match either the publication title, or the article title. So the following reference ...
137 |
138 | Land, M., Mennie, N., & Rusted, J. (1999). The roles of vision and eye movements in the control of activities of daily living. *Perception*, *28*(11), 1311–1328.
139 |
140 | ... matches any of the following terms:
141 |
142 | - `@Land1999`
143 | - `@Land1999Roles`
144 | - `@LandMennie1999`
145 | - `@LandRusted1999Percept`
146 | - `@land_rusted_1999_percept`
147 | - etc.
148 |
149 | If a name contains spaces, you can indicate this using a `+` character. So the following reference ...
150 |
151 | Van Zoest, W., & Donk, M. (2005). The effects of salience on saccadic target selection. *Visual Cognition*, *12*(2), 353–375.
152 |
153 | ... matches any of the following terms:
154 |
155 | - `@Van+zoestDonk2005`
156 | - `@van+zoest_donk_2005`
157 |
158 | Note that you must consistently use the same citation to refer to a single reference in one document. If a citation matched multiple references from your Zotero database, one citation will be chosen at random.
159 |
160 | ### Sorting citations
161 |
162 | Pandoc does not allow you to sort your references, which can be annoying. To get around this, Academic Markdown allows you to explicitly sort your citations by linking chains of citations with a `+` character:
163 |
164 | [@Zzz2014]+[@Aaa2014]
165 |
166 | ### Clearing cache
167 |
168 | Previous references will be cached automatically. To refresh, remove the file `.zoteromarkdown.cache` or run your Python script with the command-line argument: `--clear-cache`.
169 |
170 | ## Academic Markdown extensions
171 |
172 | Academic Markdown provides certain extensions to regular Markdown, in the form of YAML blocks embedded in `%-- --%` tags. You can which, and the order in which, extensions are called by settings the `extensions` list:
173 |
174 | ~~~ {.python}
175 | from academicmarkdown import build
176 | # First call the include extension, second call the figure extension
177 | build.extensions = [u'include', u'figure']
178 | ~~~
179 |
180 | ### `code`: code listings
181 |
182 | The `code` blocks embeds a code listing in the text, quite to similar to the
183 | `figure` block.
184 |
185 | %--
186 | code:
187 | id: CodeA
188 | source: my_script.py
189 | syntax: python
190 | caption: "A simple Python script"
191 | --%
192 |
193 | The `caption` and `syntax` attributes are optional.
194 |
195 |
196 | ### `constant`: define constants
197 |
198 | The `constant` block allows you to define constants. For example, if you
199 | define MyConstant1 (as below), all occurrences of "%MyConstant1" in the text
200 | will be replcated by "You can refer to this as %MyConstant1".
201 |
202 | %--
203 | constant:
204 | MyConstant1: "You can refer to this as %MyConstant1"
205 | MyConstant2: "You can refer to this as %MyConstant2"
206 | --%
207 |
208 |
209 | ### `exec`: external commands
210 |
211 | The `exec` block inserts the return value of an external command in the
212 | text. For example, the following block embeds something like
213 | 'Generated on 10/18/2013':
214 |
215 | %-- exec: "date +'Generated on %x'" --%
216 |
217 |
218 | ### `figure`: figures
219 |
220 | The `figure` block embeds a Figure in the text. Figures are numbered
221 | automatically. The ID can be used to refer to the Figure in the text, using
222 | a `%` character. So the following figure would be referred to as `%FigFA`.
223 |
224 | %--
225 | figure:
226 | id: FigFA
227 | source: foveal_acuity.svg
228 | caption: "Visual acuity drops of rapidly with distance from the fovea."
229 | width: 100
230 | --%
231 |
232 | The `caption` and `width` attributes are optional.
233 |
234 |
235 | ### `include`: include other Markdown files
236 |
237 | The `include` block includes an other Markdown file. For example:
238 |
239 | %-- include: example/intro.md --%
240 |
241 |
242 | ### `python`: python code
243 |
244 | The `python` block embeds the output (i.e. whatever is printed to stdout)
245 | of a Python script into your document. For example, the following block
246 | embeds the docstring of the `PythonParser` class (i.e. what you're reading
247 | now):
248 |
249 | %--
250 | python: |
251 | import inspect
252 | from academicmarkdown import PythonParser
253 | print inspect.getdoc(PythonParser)
254 | --%
255 |
256 | Note that the `|` symbol is YAML syntax, and allows you to have a multiline
257 | string.
258 |
259 |
260 | ### `table`: table
261 |
262 | The `table` block reads a table from a `.csv` file and embed it into the
263 | document. The source file needs to be a utf-8 encoded file that is
264 | comma separated and double quoted.
265 |
266 | %--
267 | table:
268 | id: MyTable
269 | source: my_table.csv
270 | caption: "My table caption."
271 | ndigits: 4
272 | --%
273 |
274 |
275 | ### `toc`: table of contents
276 |
277 | The `toc` block automatically generates a table of contents from the
278 | headings, assuming that headings are indicated using the `#` style and not
279 | the underlining style. You can indicate headings to be excluded from the
280 | table of contents as well.
281 |
282 | %--
283 | toc:
284 | mindepth: 1
285 | maxdepth: 2
286 | exclude: [Contents, Contact]
287 | --%
288 |
289 | All attributes are optional.
290 |
291 |
292 | ### `wc`: word count
293 |
294 | The `wc` block insert the word count for a particular document. This is
295 | convenient if you have split the text across multiple documents, and want to
296 | have a separate word count for each document.
297 |
298 | %-- wc: method-section.md --%
299 |
300 |
301 | ### Magic variables
302 |
303 | Magic variables are automatically replaced by certain values, and are indicated like this: `%varname%`. The following magic variables are available:
304 |
305 | - `%wc%`: Word count
306 | - `%cc%`: Character count
307 | - `%rc%`: Reference count
308 |
309 | ## License
310 |
311 | Academic Markdown is available under the GNU General Public License 3. For more information, see the included file `COPYING`.
312 |
313 | [pandoc]: http://johnmacfarlane.net/pandoc/
314 | [pyzotero]: http://pyzotero.readthedocs.org/
315 | [zotero]: http://www.zotero.org/
316 | [wkhtmltopdf]: https://code.google.com/p/wkhtmltopdf/
317 |
318 |
319 |
320 | ## *module* academicmarkdown.build
321 |
322 | Contains functions to build documents from Markdown source.
323 |
324 |
325 |
326 | ### function __academicmarkdown\.build\.DOC__\(src, target\)
327 |
328 | Builds a DOC file from a Markdown source.
329 |
330 | __Arguments:__
331 |
332 | - `src` -- The Markdown source. If it is a path to an existing file, the contents of this file are read. Otherwise, the string itself it used. Should be in utf-8 encoding.
333 | - Type: str, unicode
334 | - `target` -- The name of a DOC target file.
335 | - Type: str, unicode
336 |
337 |
338 |
339 | [academicmarkdown.build.DOC]: #academicmarkdown-build-DOC
340 | [build.DOC]: #academicmarkdown-build-DOC
341 | [DOC]: #academicmarkdown-build-DOC
342 |
343 |
344 |
345 | ### function __academicmarkdown\.build\.DOCX__\(src, target\)
346 |
347 | Builds a DOCX file from a Markdown source.
348 |
349 | __Arguments:__
350 |
351 | - `src` -- The Markdown source. If it is a path to an existing file, the contents of this file are read. Otherwise, the string itself it used. Should be in utf-8 encoding.
352 | - Type: str, unicode
353 | - `target` -- The name of a DOCX target file.
354 | - Type: str, unicode
355 |
356 |
357 |
358 | [academicmarkdown.build.DOCX]: #academicmarkdown-build-DOCX
359 | [build.DOCX]: #academicmarkdown-build-DOCX
360 | [DOCX]: #academicmarkdown-build-DOCX
361 |
362 |
363 |
364 | ### function __academicmarkdown\.build\.HTML__\(src, target=None, standalone=True\)
365 |
366 | Builds an HTML file from a Markdown source.
367 |
368 |
369 |
370 | __Arguments:__
371 |
372 | - `src` -- The Markdown source. If it is a path to an existing file, the contents of this file are read. Otherwise, the string itself it used. Should be in utf-8 encoding.
373 | - Type: str, unicode
374 |
375 | __Keywords:__
376 |
377 | - `target` -- The name of an HTML target file or None to skip saving.
378 | - Default: None
379 | - Type: str, unicode, NoneType
380 | - `standalone` -- Indicates whether a full HTML5 document should be generated, which embeds all content, or whether the document should be rendered without `` and `` tags, etc.
381 | - Default: True
382 | - Type: bool
383 |
384 | __Returns:__
385 |
386 | The HTML file as a unicode string.
387 |
388 | - Type: unicode
389 |
390 |
391 |
392 | [academicmarkdown.build.HTML]: #academicmarkdown-build-HTML
393 | [build.HTML]: #academicmarkdown-build-HTML
394 | [HTML]: #academicmarkdown-build-HTML
395 |
396 |
397 |
398 | ### function __academicmarkdown\.build\.MD__\(src, target=None\)
399 |
400 | Builds a Markdown file from a Markdown source.
401 |
402 | __Arguments:__
403 |
404 | - `src` -- The Markdown source. If it is a path to an existing file, the contents of this file are read. Otherwise, the string itself it used. Should be in utf-8 encoding.
405 | - Type: str, unicode
406 |
407 | __Keywords:__
408 |
409 | - `target` -- The name of a Markdown target file or None to skip saving.
410 | - Default: None
411 | - Type: str, unicode, NoneType
412 |
413 | __Returns:__
414 |
415 | The compiled Markdown file as a unicode string.
416 |
417 | - Type: unicode
418 |
419 |
420 |
421 | [academicmarkdown.build.MD]: #academicmarkdown-build-MD
422 | [build.MD]: #academicmarkdown-build-MD
423 | [MD]: #academicmarkdown-build-MD
424 |
425 |
426 |
427 | ### function __academicmarkdown\.build\.ODT__\(src, target\)
428 |
429 | Builds an ODT file from a Markdown source.
430 |
431 | __Arguments:__
432 |
433 | - `src` -- The Markdown source. If it is a path to an existing file, the contents of this file are read. Otherwise, the string itself it used. Should be in utf-8 encoding.
434 | - Type: str, unicode
435 | - `target` -- The name of an ODT target file.
436 | - Type: str, unicode
437 |
438 |
439 |
440 | [academicmarkdown.build.ODT]: #academicmarkdown-build-ODT
441 | [build.ODT]: #academicmarkdown-build-ODT
442 | [ODT]: #academicmarkdown-build-ODT
443 |
444 |
445 |
446 | ### function __academicmarkdown\.build\.PDF__\(src, target, args=u'', lineNumbers=False\)
447 |
448 | Builds a PDF file from a Markdown source.
449 |
450 | __Arguments:__
451 |
452 | - `src` -- The Markdown source. If it is a path to an existing file, the contents of this file are read. Otherwise, the string itself it used. Should be in utf-8 encoding.
453 | - Type: str, unicode
454 | - `target` -- The name of a PDF target file.
455 | - Type: str, unicode
456 |
457 | __Keywords:__
458 |
459 | - `args` -- Indicates extra arguments to be passed onto wkhtmltopdf.
460 | - Default: ''
461 | - Type: str, unicode
462 | - `lineNumbers` -- Determines whether line numbers should be added. This is currently quite a complicated process, which may break.
463 | - Default: False
464 | - Type: bool
465 |
466 |
467 |
468 | [academicmarkdown.build.PDF]: #academicmarkdown-build-PDF
469 | [build.PDF]: #academicmarkdown-build-PDF
470 | [PDF]: #academicmarkdown-build-PDF
471 |
472 |
473 |
474 | ### function __academicmarkdown\.build\.setStyle__\(style\)
475 |
476 | Automatically sets a style.
477 |
478 | __Arguments:__
479 |
480 | - `style` -- The style name. This should be the name of a folder that contains style files. See the `academicmarkdown\styles` subfolder for examples.
481 | - Type: str, unicode
482 |
483 |
484 |
485 | [academicmarkdown.build.setStyle]: #academicmarkdown-build-setStyle
486 | [build.setStyle]: #academicmarkdown-build-setStyle
487 | [setStyle]: #academicmarkdown-build-setStyle
488 |
489 |
490 |
491 | [academicmarkdown.build]: #academicmarkdown-build
492 | [build]: #academicmarkdown-build
493 |
494 |
495 |
496 | ## *module* academicmarkdown.constants
497 |
498 | Contains the settings, which are imported into `academicmarkdown.build`. You
499 | can change these settings in the `build` module, as shown below.
500 |
501 | __Module source:__
502 |
503 |
504 | ~~~ {.python}
505 | # -*- coding: utf-8 -*-
506 | """
507 | This file is part of zoteromarkdown.
508 |
509 | zoteromarkdown is free software: you can redistribute it and/or modify
510 | it under the terms of the GNU General Public License as published by
511 | the Free Software Foundation, either version 3 of the License, or
512 | (at your option) any later version.
513 |
514 | zoteromarkdown is distributed in the hope that it will be useful,
515 | but WITHOUT ANY WARRANTY; without even the implied warranty of
516 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
517 | GNU General Public License for more details.
518 |
519 | You should have received a copy of the GNU General Public License
520 | along with zoteromarkdown. If not, see .
521 |
522 | ---
523 | desc: |
524 | Contains the settings, which are imported into `academicmarkdown.build`. You
525 | can change these settings in the `build` module, as shown below.
526 |
527 | __Module source:__
528 |
529 | %--
530 | code:
531 | id: LstConstants
532 | syntax: python
533 | source: academicmarkdown/constants.py
534 | --%
535 |
536 | example: |
537 | from academicmarkdown import build
538 | build.pdfHeader = u'A header for my PDF'
539 | ---
540 | """
541 |
542 | import os, sys
543 |
544 | # A list of folders that are searched for figures, scripts, etc.
545 | path = [os.getcwd().decode(sys.getfilesystemencoding())]
546 |
547 | # Parameters for Zotero integration
548 | zoteroApiKey = None
549 | zoteroLibraryId = None
550 | zoteroHeaderText = u'References'
551 | zoteroHeaderLevel = 1
552 |
553 | # Options for the appearance of figures, blocks, and tables
554 | figureTemplate = u'html5'
555 | figureStyle = u'inline'
556 | codeTemplate = u'pandoc'
557 | codeStyle = u'inline'
558 | tableTemplate = u'html5'
559 | tableStyle = u'inline'
560 |
561 | # Indicates whether headers should be turned into clickable anchors by TOCParser
562 | TOCAnchorHeaders = False
563 | # Indicates whether references to header ids should be automatically appended
564 | # to the main text.
565 | TOCAppendHeaderRefs = True
566 |
567 | # Paths to files that determine the document's appearance. For more information,
568 | # see the Pandoc documentation.
569 | css = None # CSS stylesheet
570 | csl = None # CSL citation style
571 | html5Ref = None # HTML5 template
572 | odtRef = None # ODT reference document
573 | docxRef = None # DOCX reference document
574 |
575 | # A list of filters from academicmarkdown.HTMLFilter that should be performed
576 | # after an HTML document has been genertated.
577 | htmlFilters = [u'DOI', u'citationGlue']
578 |
579 | # A list of filters from academicmarkdown.MDFilter that should be performed
580 | # on the Markdown source, prior to any processing.
581 | preMarkdownFilters = []
582 | # A list of filters from academicmarkdown.MDFilter that should be performed
583 | # on the Markdown source, after all other processing has been performed
584 | postMarkdownFilters = [u'autoItalics', u'pageBreak', u'magicVars', u'highlight']
585 |
586 | # A list of extensions that are enabled.
587 | extensions = [u'include', u'exec', u'python', u'toc', u'code', u'video', \
588 | u'table', u'figure', u'constant', u'wc']
589 |
590 | # The page margins
591 | pdfMargins = 30, 20, 30, 20
592 |
593 | # The spacing between the content and the header and footer
594 | pdfSpacing = 10, 10
595 |
596 | # Header and footer text
597 | pdfHeader = u'%section%'
598 | pdfFooter = u'%page% of %topage%'
599 | ~~~
600 |
601 |
602 | __Example:__
603 |
604 | ~~~ .python
605 | from academicmarkdown import build
606 | build.pdfHeader = u'A header for my PDF'
607 | ~~~
608 |
609 |
610 |
611 | [academicmarkdown.constants]: #academicmarkdown-constants
612 | [constants]: #academicmarkdown-constants
613 |
614 |
615 |
616 | [academicmarkdown]: #academicmarkdown
617 |
618 |
619 | [*module* academicmarkdown]: #module-academicmarkdown
620 | [Contents]: #contents
621 | [About]: #about
622 | [Examples]: #examples
623 | [Download]: #download
624 | [Basic usage]: #basic-usage
625 | [Dependencies]: #dependencies
626 | [Zotero references]: #zotero-references
627 | [Pandoc citation style]: #pandoc-citation-style
628 | [Zotero API key and library ID]: #zotero-api-key-and-library-id
629 | [Citation identifiers]: #citation-identifiers
630 | [Sorting citations]: #sorting-citations
631 | [Clearing cache]: #clearing-cache
632 | [Academic Markdown extensions]: #academic-markdown-extensions
633 | [`code`: code listings]: #code-code-listings
634 | [`constant`: define constants]: #constant-define-constants
635 | [`exec`: external commands]: #exec-external-commands
636 | [`figure`: figures]: #figure-figures
637 | [`include`: include other Markdown files]: #include-include-other-markdown-files
638 | [`python`: python code]: #python-python-code
639 | [`table`: table]: #table-table
640 | [`toc`: table of contents]: #toc-table-of-contents
641 | [`wc`: word count]: #wc-word-count
642 | [Magic variables]: #magic-variables
643 | [License]: #license
644 | [*module* academicmarkdown.build]: #module-academicmarkdownbuild
645 | [function __academicmarkdown\.build\.DOC__\(src, target\)]: #function-__academicmarkdownbuilddoc__src-target
646 | [function __academicmarkdown\.build\.DOCX__\(src, target\)]: #function-__academicmarkdownbuilddocx__src-target
647 | [function __academicmarkdown\.build\.HTML__\(src, target=None, standalone=True\)]: #function-__academicmarkdownbuildhtml__src-targetnone-standalonetrue
648 | [function __academicmarkdown\.build\.MD__\(src, target=None\)]: #function-__academicmarkdownbuildmd__src-targetnone
649 | [function __academicmarkdown\.build\.ODT__\(src, target\)]: #function-__academicmarkdownbuildodt__src-target
650 | [function __academicmarkdown\.build\.PDF__\(src, target, args=u'', lineNumbers=False\)]: #function-__academicmarkdownbuildpdf__src-target-argsu-linenumbersfalse
651 | [function __academicmarkdown\.build\.setStyle__\(style\)]: #function-__academicmarkdownbuildsetstyle__style
652 | [*module* academicmarkdown.constants]: #module-academicmarkdownconstants
--------------------------------------------------------------------------------
/readme.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | """
5 | This file is part of academicmarkdown.
6 |
7 | academicmarkdown is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | academicmarkdown is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with academicmarkdown. If not, see .
19 | """
20 |
21 | import yamldoc
22 | import academicmarkdown
23 | from academicmarkdown.py3compat import *
24 |
25 | df = yamldoc.DocFactory(academicmarkdown)
26 | academicmarkdown.build.extensions = [u'toc', u'exec', u'code', u'constant',
27 | u'python']
28 | academicmarkdown.build.postMarkdownFilters = []
29 | academicmarkdown.build.MD(str(df), u'readme.md')
30 | academicmarkdown.build.PDF(str(df), u'readme.pdf')
31 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | """
5 | This file is part of zoteromarkdown.
6 |
7 | zoteromarkdown is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | zoteromarkdown is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with zoteromarkdown. If not, see .
19 | """
20 |
21 | from academicmarkdown import version
22 | from setuptools import setup, find_packages
23 |
24 | setup(
25 | name=u'python-academicmarkdown',
26 | version=version,
27 | description= \
28 | u'A Python package for generating scientific documents using Markdown.',
29 | author=u'Sebastiaan Mathôt',
30 | author_email=u's.mathot@cogsci.nl',
31 | license=u'GNU GPL Version 3',
32 | url=u'https://github.com/smathot/academicmarkdown',
33 | packages=find_packages('.')
34 | )
35 |
--------------------------------------------------------------------------------