pelican-asciidoctor/asciidoctor.py

184 lines
6.0 KiB
Python
Raw Permalink Normal View History

2019-06-02 10:17:13 +00:00
# -*- coding: utf-8 -*-
"""
AsciiDoctor
===========
This plugin allows you to use AsciiDoctor to write your posts.
File extension should be ``.asc``, ``.adoc``, or ``asciidoc``.
This plugin is based on
https://github.com/getpelican/pelican-plugins/tree/master/asciidoc_reader
and
https://github.com/marienfressinaud/Pelidoc
"""
from pelican.readers import BaseReader
from pelican.generators import Generator
from pelican import signals
import os
import os.path
2019-06-02 10:17:13 +00:00
import re
import subprocess
import sys
import logging
from subprocess import check_call, check_output
logger = logging.getLogger(__name__)
pattern = re.compile("Asciidoctor.*asciidoctor.org")
class AsciiDoctorReader(BaseReader):
""" Reader for AsciiDoc files. """
file_extensions = ['asc', 'adoc', 'asciidoc']
def read(self, source_path):
"""Parse content and metadata of AsciiDoc files."""
cmd = self.settings.get('ASCIIDOCTOR_CMD', 'asciidoctor')
cmd_version = check_output(['asciidoctor', '--version']).decode('utf-8')
if not pattern.match(cmd_version):
logger.error("AsciiDoctorReader: {} is not asciidoctor executable".format(cmd))
return
content = ""
if cmd:
content = check_output(
[cmd] + self.settings.get('ASCIIDOCTOR_EXTRA_OPTIONS', []) +
['--doctype=article'] + ['--no-header-footer'] +
['--out-file', '-', source_path]
).decode('utf-8')
metadata = self._read_metadata(source_path)
return content, metadata
def _read_metadata(self, source_path):
"""Parses the AsciiDoc file at the given `source_path`
and returns found metadata."""
metadata = {}
with open(source_path) as fi:
prev = ""
for line in fi.readlines():
# Parse for doc title.
if 'title' not in metadata.keys():
title = ""
if line.startswith("= "):
title = line[2:].strip()
elif line.count("=") == len(prev.strip()):
title = prev.strip()
if title:
metadata['title'] = self.process_metadata('title', title)
regexp = re.compile(r"^:[\-A-z]+:\s*")
if regexp.search(line):
toks = line.split(":", 2)
key = toks[1].strip().lower()
val = toks[2].strip()
metadata[key] = self.process_metadata(key, val)
prev = line
if (not 'summary' in metadata) or (metadata['summary'] is None):
metadata['summary'] = ''
return metadata
class AsciiDoctorPdfGenerator(Generator):
"""The AsciiDoctorPdf generator.
"""
def guess_format(self, content):
"""Return the format used by a given content.
"""
formats = {
'.adoc': 'asciidoc',
'.asciidoc': 'asciidoc',
'.asc': 'asciidoc',
}
file_name, file_extension = os.path.splitext(content.source_path)
return formats[file_extension]
def check_output_dir(self, dir_):
"""Check and create if needed the given directory."""
if not os.path.isdir(dir_):
try:
os.mkdir(dir_)
except OSError:
return False
return True
def generate_files(self, content):
"""Generates the list of files for a given content.
:param content: the content to generate.
:type content: pelican.contents.Content
"""
cmd = self.settings.get('ASCIIDOCTOR_CMD', 'asciidoctor')
cmd_version = check_output(['asciidoctor', '--version']).decode('utf-8')
if not pattern.match(cmd_version):
logger.error("AsciiDoctorPdfGenerator: {} is not asciidoctor executable".format(cmd))
return
if self.settings.get('PDF_PROCESSOR', False) == False:
return
try:
from_format = self.guess_format(content)
except KeyError:
return
list_outputs = self.settings.get('ASCIIDOCTOR_PDF_OUTPUT_DIR', 'pdf')
output_dir = os.path.join(self.output_path, list_outputs)
if not self.check_output_dir(output_dir):
logger.error("Couldn't create the PDF output "
"folder in {dir}".format(dir=output_dir))
return
filename = "{id_file}.pdf".format(id_file=content.slug)
filepath = os.path.join(output_dir, filename)
if ((not os.path.isfile(filepath)) or
(os.path.getmtime(filepath) < os.path.getmtime(content.source_path))):
check_call([cmd] + self.settings.get('ASCIIDOCTOR_EXTRA_OPTIONS', []) +
['--require', 'asciidoctor-pdf', '--backend', 'pdf'] +
['--doctype=article'] +
['--out-file', filepath, content.source_path])
logger.info("[ok] writing {filepath}".format(filepath=filepath))
2019-06-02 10:17:13 +00:00
def generate_output(self, writer=None):
"""Generate files for each articles and pages.
If ASCIIDOCTOR_EXPORT_ARTICLES is False, articles are not generated.
If ASCIIDOCTOR_EXPORT_PAGES is False, pages are not generated.
We don't use the writer passed as argument since we write our own
files ((c) PDF plugin :)).
"""
contents_to_export = []
if self.settings.get('ASCIIDOCTOR_EXPORT_ARTICLES', True):
contents_to_export += self.context['articles']
if self.settings.get('ASCIIDOCTOR_EXPORT_PAGES', True):
contents_to_export += self.context['pages']
for content_to_export in contents_to_export:
self.generate_files(content_to_export)
def add_reader(readers):
for ext in AsciiDoctorReader.file_extensions:
readers.reader_classes[ext] = AsciiDoctorReader
def get_generator(generator):
return AsciiDoctorPdfGenerator
def register():
signals.readers_init.connect(add_reader)
signals.get_generators.connect(get_generator)