0
# Command Line Interface
1
2
CLI tools for running panflute as a Pandoc filter with automatic filter discovery and execution capabilities. These tools enable panflute to be used as a standalone filter processor or as part of complex document processing pipelines.
3
4
## Capabilities
5
6
### Basic Filter Execution
7
8
Run panflute as a standard Pandoc filter that processes stdin/stdout.
9
10
```python { .api }
11
def main():
12
"""
13
Entry point for panflute CLI when used as a Pandoc filter.
14
15
Reads JSON from stdin, processes with filters specified in metadata,
16
and writes results to stdout. Filters are specified in document
17
metadata under 'panflute-filters' key.
18
19
Example usage:
20
pandoc document.md --filter panflute -o output.html
21
22
Document metadata can specify filters:
23
---
24
panflute-filters:
25
- emphasis_filter
26
- link_processor
27
panflute-path:
28
- ./filters
29
- ~/.pandoc/filters
30
---
31
"""
32
```
33
34
### Advanced Filter Execution
35
36
Run panflute with command-line arguments for complex filter pipelines.
37
38
```python { .api }
39
def panfl():
40
"""
41
Advanced CLI for panflute with command-line filter specification.
42
43
Supports multiple modes:
44
1. Pandoc filter mode (single filter, no arguments)
45
2. Pipeline mode (multiple filters with --to option)
46
47
Command-line options:
48
- filters: Filter names/paths (positional arguments)
49
- --to/-t: Output format for pipeline mode
50
- --dir/-d: Additional search directories (multiple allowed)
51
- --data-dir: Include default Pandoc data directories
52
- --no-sys-path: Exclude Python sys.path from search
53
54
Example usage:
55
# Pipeline mode
56
pandoc -t json | panfl filter1 filter2 --to html | pandoc -f json
57
58
# Pandoc filter mode
59
pandoc document.md --filter panfl -o output.html
60
61
# Custom search directories
62
panfl --dir ./custom-filters --data-dir filter1 filter2 --to latex
63
"""
64
```
65
66
### Core Processing Function
67
68
Low-level document processing with filter execution.
69
70
```python { .api }
71
def stdio(filters=None,
72
search_dirs=None,
73
data_dir=True,
74
sys_path=True,
75
panfl_=False,
76
input_stream=None,
77
output_stream=None):
78
"""
79
Process document from stdin with automatic filter discovery and execution.
80
81
Parameters:
82
- filters: list of filter names/paths (None = read from metadata)
83
- search_dirs: list of directories to search for filters (None = read from metadata)
84
- data_dir: include Pandoc data directories in search (default: True)
85
- sys_path: include Python sys.path in search (default: True)
86
- panfl_: use panfl behavior vs standard panflute (default: False)
87
- input_stream: input source (default: stdin)
88
- output_stream: output destination (default: stdout)
89
90
The function reads document metadata for configuration:
91
- 'panflute-filters': list/string of filter names
92
- 'panflute-path': list/string of search directories
93
- 'panflute-verbose': enable debug output
94
- 'panflute-echo': display debug message
95
96
Special metadata values:
97
- '--data-dir' in panflute-path: enable data_dir
98
- '--no-sys-path' in panflute-path: disable sys_path
99
100
Example usage in filter:
101
import panflute as pf
102
103
# Custom processing with specific filters
104
pf.stdio(
105
filters=['my_filter', 'cleanup_filter'],
106
search_dirs=['./filters', '~/.pandoc/filters'],
107
input_stream=open('input.json'),
108
output_stream=open('output.json', 'w')
109
)
110
"""
111
```
112
113
### Filter Discovery
114
115
Find and manage filter files in the filesystem.
116
117
```python { .api }
118
def get_filter_dirs(hardcoded=True) -> list:
119
"""
120
Get directories where panflute searches for filters.
121
122
Parameters:
123
- hardcoded: use predefined paths vs querying Pandoc (default: True)
124
125
Returns:
126
list: Directory paths to search for filters
127
128
Default search locations:
129
- Linux/macOS: ~/.local/share/pandoc/filters, ~/.pandoc/filters (older Pandoc)
130
- Windows: %APPDATA%/pandoc/filters
131
132
Example:
133
import panflute as pf
134
135
# Get default filter directories
136
dirs = pf.get_filter_dirs()
137
print("Searching for filters in:", dirs)
138
139
# Query Pandoc directly for current directories
140
current_dirs = pf.get_filter_dirs(hardcoded=False)
141
"""
142
143
```
144
145
## Usage Examples
146
147
### Document Metadata Configuration
148
149
Configure filters through document frontmatter:
150
151
```yaml
152
---
153
title: "My Document"
154
author: "John Doe"
155
156
# Panflute configuration
157
panflute-filters:
158
- emphasis_processor
159
- link_enhancer
160
- bibliography_generator
161
162
panflute-path:
163
- ./my-filters
164
- ~/.local/share/pandoc/filters
165
- --data-dir
166
167
panflute-verbose: true
168
panflute-echo: "Processing with custom filters..."
169
---
170
171
# Document Content
172
173
This document will be processed by the specified filters.
174
```
175
176
### Command Line Usage Patterns
177
178
```bash
179
# Basic filter usage
180
pandoc document.md --filter panflute -o output.html
181
182
# Advanced pipeline with multiple filters
183
pandoc document.md -t json | \
184
panfl filter1.py custom_processor --to json | \
185
pandoc -f json -o output.pdf
186
187
# Using custom search directories
188
panfl --dir ./project-filters --dir ~/.pandoc/filters \
189
emphasis_filter link_processor --to html
190
191
# Filter development and testing
192
echo '{"pandoc-api-version":[1,23],"meta":{},"blocks":[]}' | \
193
panfl --data-dir my_test_filter --to json
194
```
195
196
### Custom Filter Runner
197
198
Create a custom filter execution environment:
199
200
```python
201
import panflute as pf
202
import sys
203
import io
204
205
def run_custom_pipeline():
206
"""Run a custom filter pipeline with error handling."""
207
208
# Define filter sequence
209
filters = [
210
'preprocessing_filter',
211
'content_enhancer',
212
'formatting_cleaner'
213
]
214
215
# Set up search paths
216
search_dirs = [
217
'./filters',
218
'~/.local/share/pandoc/filters',
219
'/usr/local/share/pandoc/filters'
220
]
221
222
try:
223
# Load document
224
doc = pf.load()
225
226
# Add processing metadata
227
doc.processing_start = pf.meta2builtin(doc.get_metadata('date', ''))
228
doc.filter_count = 0
229
230
# Process with filters
231
pf.stdio(
232
filters=filters,
233
search_dirs=search_dirs,
234
data_dir=True,
235
sys_path=True,
236
input_stream=sys.stdin,
237
output_stream=sys.stdout
238
)
239
240
except Exception as e:
241
pf.debug(f"Filter pipeline failed: {e}")
242
sys.exit(1)
243
244
if __name__ == '__main__':
245
run_custom_pipeline()
246
```
247
248
### Filter Discovery and Management
249
250
```python
251
import panflute as pf
252
import os
253
254
def list_available_filters():
255
"""Discover and list all available filters."""
256
257
search_dirs = pf.get_filter_dirs()
258
available_filters = []
259
260
for directory in search_dirs:
261
if os.path.exists(directory):
262
for filename in os.listdir(directory):
263
if filename.endswith('.py'):
264
filter_path = os.path.join(directory, filename)
265
filter_name = filename[:-3] # Remove .py extension
266
267
# Check if it's a valid panflute filter
268
try:
269
with open(filter_path, 'r') as f:
270
content = f.read()
271
if 'def main(' in content and 'panflute' in content:
272
available_filters.append({
273
'name': filter_name,
274
'path': filter_path,
275
'directory': directory
276
})
277
except Exception:
278
continue
279
280
return available_filters
281
282
def validate_filter_environment():
283
"""Check filter environment and dependencies."""
284
285
# Check Pandoc installation
286
try:
287
version_info = pf.run_pandoc(args=['--version'])
288
pf.debug(f"Pandoc version: {version_info.split()[1]}")
289
except Exception as e:
290
pf.debug(f"Pandoc not found: {e}")
291
return False
292
293
# Check filter directories
294
dirs = pf.get_filter_dirs()
295
pf.debug(f"Filter search directories: {dirs}")
296
297
for directory in dirs:
298
if os.path.exists(directory):
299
filter_count = len([f for f in os.listdir(directory) if f.endswith('.py')])
300
pf.debug(f" {directory}: {filter_count} Python files")
301
else:
302
pf.debug(f" {directory}: does not exist")
303
304
return True
305
306
# Usage example
307
if __name__ == '__main__':
308
if validate_filter_environment():
309
filters = list_available_filters()
310
pf.debug(f"Found {len(filters)} available filters:")
311
for filter_info in filters:
312
pf.debug(f" {filter_info['name']} ({filter_info['path']})")
313
```
314
315
### Integration with External Tools
316
317
```python
318
import panflute as pf
319
import subprocess
320
import tempfile
321
import os
322
323
def pandoc_filter_chain():
324
"""Example of complex document processing chain."""
325
326
def preprocess_filter(elem, doc):
327
"""First stage: clean up input."""
328
if isinstance(elem, pf.Str):
329
# Normalize whitespace
330
elem.text = ' '.join(elem.text.split())
331
return elem
332
333
def content_filter(elem, doc):
334
"""Second stage: transform content."""
335
if isinstance(elem, pf.Header) and elem.level == 1:
336
# Add document structure tracking
337
if not hasattr(doc, 'sections'):
338
doc.sections = []
339
doc.sections.append(pf.stringify(elem))
340
return elem
341
342
def postprocess_filter(elem, doc):
343
"""Third stage: final formatting."""
344
if isinstance(elem, pf.Link):
345
# Ensure external links open in new tab
346
if elem.url.startswith('http'):
347
elem.attributes['target'] = '_blank'
348
return elem
349
350
def finalize_doc(doc):
351
"""Add document metadata after processing."""
352
if hasattr(doc, 'sections'):
353
doc.metadata['generated-toc'] = pf.MetaList(
354
*[pf.MetaString(section) for section in doc.sections]
355
)
356
357
# Run the filter chain
358
pf.run_filters(
359
[preprocess_filter, content_filter, postprocess_filter],
360
finalize=finalize_doc
361
)
362
363
if __name__ == '__main__':
364
pandoc_filter_chain()
365
```