Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions screenplain/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import os
import re
import json
import configparser


class Defaults:
FILENAME_CONFIG = 'screenplain.cfg'
PATH_CONFIG = os.path.join(
os.getenv('XDG_CONFIG_HOME') or
os.path.join(os.getenv('HOME'), '.config'),
'screenplain', FILENAME_CONFIG)


class ConfigurationFileError(Exception):
pass


class ConfigurationFile(configparser.ConfigParser):
def __init__(self, path=Defaults.PATH_CONFIG):
super().__init__(interpolation=None,
allow_no_value=True,
converters={
'list': self.__getlist,
})

# Allow brackets in section names
self.SECTCRE = re.compile(
r'^[ \t\r\f\v]*\[(?P<header>.+?)\][ \t\r\f\v]*$')

# Initialize sections and their expected values
self.read_string("""
[export]
format

[[pdf]]
strong: no
font

[[html]]
base: no
css

[font]
""")

try:
self.read(path)
except configparser.Error as e:
raise ConfigurationFileError(
'unable to load configuration file: %s' % e)

def __getlist(self, v):
try:
v = json.loads(v)
except json.JSONDecodeError as e:
raise ConfigurationFileError('unable to decode JSON value: %s' % e)

if not isinstance(v, list):
raise ConfigurationFileError('value is not a list: %s' % type(v))

return v
194 changes: 115 additions & 79 deletions screenplain/export/pdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
from reportlab.lib.units import inch
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_RIGHT
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont

from screenplain.types import (
Action, Dialog, DualDialog, Transition, Slug
Expand All @@ -44,74 +46,80 @@
bottom_margin = page_height - top_margin - frame_height


default_style = ParagraphStyle(
'default',
fontName='Courier',
fontSize=font_size,
leading=line_height,
spaceBefore=0,
spaceAfter=0,
leftIndent=0,
rightIndent=0,
)
centered_style = ParagraphStyle(
'default-centered', default_style,
alignment=TA_CENTER,
)
class ParagraphStyles:
def __init__(self, font_name):
self.default_style = ParagraphStyle(
'default',
fontName=font_name,
fontSize=font_size,
leading=line_height,
spaceBefore=0,
spaceAfter=0,
leftIndent=0,
rightIndent=0,
)
self.centered_style = ParagraphStyle(
'default-centered', self.default_style,
alignment=TA_CENTER,
)

# Screenplay styles
character_style = ParagraphStyle(
'character', default_style,
spaceBefore=line_height,
leftIndent=19 * character_width,
keepWithNext=1,
)
dialog_style = ParagraphStyle(
'dialog', default_style,
leftIndent=9 * character_width,
rightIndent=frame_width - (45 * character_width),
)
parenthentical_style = ParagraphStyle(
'parenthentical', default_style,
leftIndent=13 * character_width,
keepWithNext=1,
)
action_style = ParagraphStyle(
'action', default_style,
spaceBefore=line_height,
)
centered_action_style = ParagraphStyle(
'centered-action', action_style,
alignment=TA_CENTER,
)
slug_style = ParagraphStyle(
'slug', default_style,
spaceBefore=line_height,
spaceAfter=line_height,
keepWithNext=1,
)
transition_style = ParagraphStyle(
'transition', default_style,
spaceBefore=line_height,
spaceAfter=line_height,
alignment=TA_RIGHT,
)
# Screenplay styles
self.character_style = ParagraphStyle(
'character', self.default_style,
spaceBefore=line_height,
leftIndent=19 * character_width,
keepWithNext=1,
)
self.dialog_style = ParagraphStyle(
'dialog', self.default_style,
leftIndent=9 * character_width,
rightIndent=frame_width - (45 * character_width),
)
self.parenthentical_style = ParagraphStyle(
'parenthentical', self.default_style,
leftIndent=13 * character_width,
keepWithNext=1,
)
self.action_style = ParagraphStyle(
'action', self.default_style,
spaceBefore=line_height,
)
self.centered_action_style = ParagraphStyle(
'centered-action', self.action_style,
alignment=TA_CENTER,
)
self.slug_style = ParagraphStyle(
'slug', self.default_style,
spaceBefore=line_height,
spaceAfter=line_height,
keepWithNext=1,
)
self.transition_style = ParagraphStyle(
'transition', self.default_style,
spaceBefore=line_height,
spaceAfter=line_height,
alignment=TA_RIGHT,
)

# Title page styles
title_style = ParagraphStyle(
'title', default_style,
fontSize=24, leading=36,
alignment=TA_CENTER,
)
contact_style = ParagraphStyle(
'contact', default_style,
leftIndent=3.9 * inch,
rightIndent=0,
)
# Title page styles
self.title_style = ParagraphStyle(
'title', self.default_style,
fontSize=24, leading=36,
alignment=TA_CENTER,
)
self.contact_style = ParagraphStyle(
'contact', self.default_style,
leftIndent=3.9 * inch,
rightIndent=0,
)


styles = ParagraphStyles('Courier')


class DocTemplate(BaseDocTemplate):
def __init__(self, *args, **kwargs):
def __init__(self, font_name, *args, **kwargs):
self.font_name = font_name
self.has_title_page = kwargs.pop('has_title_page', False)
frame = Frame(
left_margin, bottom_margin, frame_width, frame_height,
Expand All @@ -126,7 +134,7 @@ def __init__(self, *args, **kwargs):
)

def handle_pageBegin(self):
self.canv.setFont('Courier', font_size, leading=line_height)
self.canv.setFont(self.font_name, font_size, leading=line_height)
if self.has_title_page:
page = self.page # self.page is 0 on first page
else:
Expand Down Expand Up @@ -157,12 +165,15 @@ def add_slug(story, para, style, is_strong):


def add_dialog(story, dialog):
story.append(Paragraph(dialog.character.to_html(), character_style))
global styles
story.append(Paragraph(dialog.character.to_html(), styles.character_style))
for parenthetical, line in dialog.blocks:
if parenthetical:
story.append(Paragraph(line.to_html(), parenthentical_style))
story.append(Paragraph(line.to_html(),
styles.parenthentical_style))
else:
story.append(Paragraph(line.to_html(), dialog_style))
story.append(Paragraph(line.to_html(),
styles.dialog_style))


def add_dual_dialog(story, dual):
Expand Down Expand Up @@ -198,25 +209,29 @@ def add_lines(story, attribute, style, space_before=0):
total_height += height
return space_before + total_height

global styles
title_story = []
title_height = sum((
add_lines(title_story, 'Title', title_style),
add_lines(title_story, 'Title', styles.title_style),
add_lines(
title_story, 'Credit', centered_style, space_before=line_height
title_story, 'Credit', styles.centered_style,
space_before=line_height
),
add_lines(title_story, 'Author', centered_style),
add_lines(title_story, 'Authors', centered_style),
add_lines(title_story, 'Source', centered_style),
add_lines(title_story, 'Author', styles.centered_style),
add_lines(title_story, 'Authors', styles.centered_style),
add_lines(title_story, 'Source', styles.centered_style),
))

lower_story = []
lower_height = sum((
add_lines(lower_story, 'Draft date', default_style),
add_lines(lower_story, 'Draft date', styles.default_style),
add_lines(
lower_story, 'Contact', contact_style, space_before=line_height
lower_story, 'Contact', styles.contact_style,
space_before=line_height
),
add_lines(
lower_story, 'Copyright', centered_style, space_before=line_height
lower_story, 'Copyright', styles.centered_style,
space_before=line_height
),
))

Expand All @@ -242,10 +257,29 @@ def add_lines(story, attribute, style, space_before=0):


def to_pdf(
screenplay, output_filename,
config, screenplay, output_filename,
template_constructor=DocTemplate,
is_strong=False,
):
font_name = 'Courier'

if config.has_option('[pdf]', 'font') and config['[pdf]']['font']:
section_name = '[%s]' % config['[pdf]']['font']
if config.has_section(section_name):
font_name = config.get(section_name, 'name', fallback=section_name)

for font_type in ['regular', 'bold', 'italic', 'bold_italic']:
font_attr = config.getlist(section_name, font_type)
if len(font_attr) != 2:
raise ValueError(('Invalid font attribute: %s is %s, '
'which must be a two-elements list') %
(font_type, font_attr))

pdfmetrics.registerFont(TTFont(*font_attr))

global styles
styles = ParagraphStyles(font_name)

story = get_title_page_story(screenplay)
has_title_page = bool(story)

Expand All @@ -257,19 +291,21 @@ def to_pdf(
elif isinstance(para, Action):
add_paragraph(
story, para,
centered_action_style if para.centered else action_style
styles.centered_action_style if para.centered
else styles.action_style
)
elif isinstance(para, Slug):
add_slug(story, para, slug_style, is_strong)
add_slug(story, para, styles.slug_style, is_strong)
elif isinstance(para, Transition):
add_paragraph(story, para, transition_style)
add_paragraph(story, para, styles.transition_style)
elif isinstance(para, types.PageBreak):
story.append(platypus.PageBreak())
else:
# Ignore unknown types
pass

doc = template_constructor(
font_name,
output_filename,
pagesize=(page_width, page_height),
has_title_page=has_title_page
Expand Down
Loading