mirror of
https://github.com/iluvcapra/ptulsconv.git
synced 2025-12-31 08:50:48 +00:00
Dumped PDF code from my jupyter notebook into source
This commit is contained in:
191
ptulsconv/pdf/common.py
Normal file
191
ptulsconv/pdf/common.py
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
from reportlab.pdfbase.pdfmetrics import (getAscent, getDescent)
|
||||||
|
|
||||||
|
class GRect:
|
||||||
|
def __init__(self, x, y, width, height, debug_name=None):
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
self.debug_name = debug_name
|
||||||
|
self.normalize()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def min_x(self):
|
||||||
|
return self.x
|
||||||
|
|
||||||
|
@property
|
||||||
|
def min_y(self):
|
||||||
|
return self.y
|
||||||
|
|
||||||
|
@property
|
||||||
|
def max_x(self):
|
||||||
|
return self.x + self.width
|
||||||
|
|
||||||
|
@property
|
||||||
|
def max_y(self):
|
||||||
|
return self.y + self.height
|
||||||
|
|
||||||
|
@property
|
||||||
|
def center_x(self):
|
||||||
|
return self.x + self.width / 2
|
||||||
|
|
||||||
|
@property
|
||||||
|
def center_y(self):
|
||||||
|
return self.y + self.height / 2
|
||||||
|
|
||||||
|
def normalize(self):
|
||||||
|
if self.width < 0.:
|
||||||
|
self.width = abs(self.width)
|
||||||
|
self.x = self.x - self.width
|
||||||
|
|
||||||
|
if self.height < 0.:
|
||||||
|
self.height = abs(self.height)
|
||||||
|
self.y = self.y - self.height
|
||||||
|
|
||||||
|
def split_x(self, at, direction='l'):
|
||||||
|
if at >= self.width:
|
||||||
|
return None, self
|
||||||
|
elif at <= 0:
|
||||||
|
return self, None
|
||||||
|
else:
|
||||||
|
return (GRect(self.min_x, self.min_y, at, self.height),
|
||||||
|
GRect(self.min_x + at, self.y, self.width - at, self.height))
|
||||||
|
|
||||||
|
def split_y(self, at, direction='u'):
|
||||||
|
if at >= self.height:
|
||||||
|
return None, self
|
||||||
|
elif at <= 0:
|
||||||
|
return self, None
|
||||||
|
else:
|
||||||
|
if direction == 'u':
|
||||||
|
return (GRect(self.x, self.y, self.width, at),
|
||||||
|
GRect(self.x, self.y + at, self.width, self.height - at))
|
||||||
|
else:
|
||||||
|
return (GRect(self.x, self.max_y - at, self.width, at),
|
||||||
|
GRect(self.x, self.y, self.width, self.height - at))
|
||||||
|
|
||||||
|
def inset_xy(self, dx, dy):
|
||||||
|
return GRect(self.x + dx, self.y + dy, self.width - dx * 2, self.height - dy * 2)
|
||||||
|
|
||||||
|
def inset(self, d):
|
||||||
|
return self.inset_xy(d, d)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<GRect x=%f y=%f width=%f height=%f>" % (self.x, self.y, self.width, self.height)
|
||||||
|
|
||||||
|
def divide_x(self, x_list, reverse=False):
|
||||||
|
ret_list = list()
|
||||||
|
|
||||||
|
rem = self
|
||||||
|
for item in x_list:
|
||||||
|
s, rem = rem.split_x(item)
|
||||||
|
ret_list.append(s)
|
||||||
|
|
||||||
|
return ret_list, rem
|
||||||
|
|
||||||
|
def divide_y(self, y_list, direction='u'):
|
||||||
|
ret_list = list()
|
||||||
|
|
||||||
|
rem = self
|
||||||
|
for item in y_list:
|
||||||
|
s, rem = rem.split_y(item, direction)
|
||||||
|
ret_list.append(s)
|
||||||
|
|
||||||
|
return ret_list, rem
|
||||||
|
|
||||||
|
def draw_debug(self, canvas):
|
||||||
|
canvas.saveState()
|
||||||
|
canvas.setFont("Courier", 8)
|
||||||
|
canvas.rect(self.x, self.y, self.width, self.height)
|
||||||
|
canvas.drawString(self.x, self.y, self.debug_name or self.__repr__())
|
||||||
|
canvas.restoreState()
|
||||||
|
|
||||||
|
def draw_border(self, canvas, edge):
|
||||||
|
|
||||||
|
def draw_border_impl(en):
|
||||||
|
if en == 'min_x':
|
||||||
|
coordinates = ((self.min_x, self.min_y), (self.min_x, self.max_y))
|
||||||
|
elif en == 'max_x':
|
||||||
|
coordinates = ((self.max_x, self.min_y), (self.max_x, self.max_y))
|
||||||
|
elif en == 'min_y':
|
||||||
|
coordinates = ((self.min_x, self.min_y), (self.max_x, self.min_y))
|
||||||
|
elif en == 'max_y':
|
||||||
|
coordinates = ((self.min_x, self.max_y), (self.max_x, self.max_y))
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
s = canvas.beginPath()
|
||||||
|
s.moveTo(*coordinates[0])
|
||||||
|
s.lineTo(*coordinates[1])
|
||||||
|
canvas.drawPath(s)
|
||||||
|
|
||||||
|
if type(edge) is str:
|
||||||
|
edge = [edge]
|
||||||
|
|
||||||
|
for e in edge:
|
||||||
|
draw_border_impl(e)
|
||||||
|
|
||||||
|
def draw_text_cell(self, canvas, text, font_name, font_size, vertical_align='t', force_baseline=None, inset_x=0.,
|
||||||
|
inset_y=0., draw_baseline=False):
|
||||||
|
canvas.saveState()
|
||||||
|
|
||||||
|
inset_rect = self.inset_xy(inset_x, inset_y)
|
||||||
|
|
||||||
|
if vertical_align == 'm':
|
||||||
|
y = inset_rect.center_y - getAscent(font_name, font_size) / 2.
|
||||||
|
elif vertical_align == 't':
|
||||||
|
y = inset_rect.max_y - getAscent(font_name, font_size)
|
||||||
|
else:
|
||||||
|
y = inset_rect.min_y - getDescent(font_name, font_size)
|
||||||
|
|
||||||
|
if force_baseline is not None:
|
||||||
|
y = self.min_y + force_baseline
|
||||||
|
|
||||||
|
cp = canvas.beginPath()
|
||||||
|
cp.rect(self.min_x, self.min_y, self.width, self.height)
|
||||||
|
canvas.clipPath(cp, stroke=0, fill=0)
|
||||||
|
|
||||||
|
canvas.setFont(font_name, font_size)
|
||||||
|
tx = canvas.beginText()
|
||||||
|
tx.setTextOrigin(inset_rect.min_x, y)
|
||||||
|
tx.textLine(text)
|
||||||
|
canvas.drawText(tx)
|
||||||
|
|
||||||
|
if draw_baseline:
|
||||||
|
canvas.setDash([3.0, 1.0, 2.0, 1.0])
|
||||||
|
canvas.setLineWidth(0.5)
|
||||||
|
bl = canvas.beginPath()
|
||||||
|
bl.moveTo(inset_rect.min_x, y - 1.)
|
||||||
|
bl.lineTo(inset_rect.max_x, y - 1.)
|
||||||
|
canvas.drawPath(bl)
|
||||||
|
|
||||||
|
canvas.restoreState()
|
||||||
|
|
||||||
|
def draw_flowable(self, canvas, flowable, inset_x=0., inset_y=0., draw_baselines=False):
|
||||||
|
canvas.saveState()
|
||||||
|
|
||||||
|
inset_rect = self.inset_xy(inset_x, inset_y)
|
||||||
|
|
||||||
|
cp = canvas.beginPath()
|
||||||
|
cp.rect(self.min_x, self.min_y, self.width, self.height)
|
||||||
|
canvas.clipPath(cp, stroke=0, fill=0)
|
||||||
|
|
||||||
|
w, h = flowable.wrap(inset_rect.width, inset_rect.height)
|
||||||
|
|
||||||
|
flowable.drawOn(canvas, inset_rect.x, inset_rect.max_y - h)
|
||||||
|
|
||||||
|
if draw_baselines:
|
||||||
|
canvas.setDash([3.0, 1.0, 2.0, 1.0])
|
||||||
|
canvas.setLineWidth(0.5)
|
||||||
|
leading = flowable.style.leading
|
||||||
|
|
||||||
|
y = inset_rect.max_y - flowable.style.fontSize - 1.
|
||||||
|
while y > inset_rect.min_x:
|
||||||
|
bl = canvas.beginPath()
|
||||||
|
bl.moveTo(inset_rect.min_x, y)
|
||||||
|
bl.lineTo(inset_rect.max_x, y)
|
||||||
|
canvas.drawPath(bl)
|
||||||
|
y = y - leading
|
||||||
|
|
||||||
|
canvas.restoreState()
|
||||||
|
|
||||||
198
ptulsconv/pdf/supervisor_1pg.py
Normal file
198
ptulsconv/pdf/supervisor_1pg.py
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
from reportlab.pdfgen.canvas import Canvas
|
||||||
|
|
||||||
|
from reportlab.pdfbase import pdfmetrics
|
||||||
|
from reportlab.pdfbase.ttfonts import TTFont
|
||||||
|
|
||||||
|
from reportlab.lib.units import inch
|
||||||
|
from reportlab.lib.pagesizes import letter
|
||||||
|
|
||||||
|
from reportlab.lib.styles import getSampleStyleSheet
|
||||||
|
from reportlab.platypus import Paragraph
|
||||||
|
|
||||||
|
from .common import GRect
|
||||||
|
|
||||||
|
|
||||||
|
def draw_header_block(canvas, rect):
|
||||||
|
rect.draw_text_cell(canvas, "CUE002", "Helvetica", 44, vertical_align='m')
|
||||||
|
|
||||||
|
|
||||||
|
def draw_title_block(canvas, rect):
|
||||||
|
(supervisor, client,), title = rect.divide_y([16., 16., ])
|
||||||
|
title.draw_text_cell(canvas, "This is the title", "Futura", 18, inset_y=2.)
|
||||||
|
client.draw_text_cell(canvas, "This is the client", "Futura", 11, inset_y=2.)
|
||||||
|
supervisor.draw_text_cell(canvas, "This is the supervisor", "Futura", 11, inset_y=2.)
|
||||||
|
|
||||||
|
|
||||||
|
def draw_character_row(canvas, rect):
|
||||||
|
label_frame, value_frame = rect.split_x(1.25 * inch)
|
||||||
|
label_frame.draw_text_cell(canvas, "CHARACTER", "Futura", 10, force_baseline=9.)
|
||||||
|
value_frame.draw_text_cell(canvas, "1 / Luke Skywalker / Mark Hamill", "Futura", 12, force_baseline=9.)
|
||||||
|
rect.draw_border(canvas, ['min_y', 'max_y'])
|
||||||
|
|
||||||
|
|
||||||
|
def draw_cue_number_block(canvas, rect):
|
||||||
|
(label_frame, number_frame,), aux_frame = rect.divide_y([0.20 * inch, 0.375 * inch], direction='d')
|
||||||
|
label_frame.draw_text_cell(canvas, "CUE NUMBER", "Futura", 10, inset_y=5., vertical_align='t')
|
||||||
|
number_frame.draw_text_cell(canvas, "CUE1001", "Futura", 14, inset_x=10., inset_y=2., draw_baseline=True)
|
||||||
|
aux_frame.draw_text_cell(canvas, "TV OPT ADLIB EFF", "Futura", 10, inset_x=10., inset_y=2., vertical_align='t')
|
||||||
|
rect.draw_border(canvas, 'max_x')
|
||||||
|
|
||||||
|
|
||||||
|
def draw_timecode_block(canvas, rect):
|
||||||
|
(in_label_frame, in_frame, out_label_frame, out_frame), _ = rect.divide_y(
|
||||||
|
[0.20 * inch, 0.25 * inch, 0.20 * inch, 0.25 * inch], direction='d')
|
||||||
|
|
||||||
|
in_label_frame.draw_text_cell(canvas, "IN", "Futura", 10, vertical_align='t', inset_y=5., inset_x=5.)
|
||||||
|
in_frame.draw_text_cell(canvas, "01:00:00:00", "Futura", 14, inset_x=10., inset_y=2., draw_baseline=True)
|
||||||
|
out_label_frame.draw_text_cell(canvas, "OUT", "Futura", 10, vertical_align='t', inset_y=5., inset_x=5.)
|
||||||
|
out_frame.draw_text_cell(canvas, "01:01:00:00", "Futura", 14, inset_x=10., inset_y=2., draw_baseline=True)
|
||||||
|
|
||||||
|
rect.draw_border(canvas, 'max_x')
|
||||||
|
|
||||||
|
|
||||||
|
def draw_reason_block(canvas, rect):
|
||||||
|
reason_cell, notes_cell = rect.split_y(24., direction='d')
|
||||||
|
reason_label, reason_value = reason_cell.split_x(.75 * inch)
|
||||||
|
notes_label, notes_value = notes_cell.split_x(.75 * inch)
|
||||||
|
|
||||||
|
reason_label.draw_text_cell(canvas, "Reason:", "Futura", 12, inset_x=5., inset_y=5., vertical_align='b')
|
||||||
|
reason_value.draw_text_cell(canvas, "Low level", "Futura", 12, inset_x=5., inset_y=5., draw_baseline=True,
|
||||||
|
vertical_align='b')
|
||||||
|
notes_label.draw_text_cell(canvas, "Note:", "Futura", 12, inset_x=5., inset_y=5., vertical_align='t')
|
||||||
|
|
||||||
|
style = getSampleStyleSheet()['BodyText']
|
||||||
|
style.fontName = 'Futura'
|
||||||
|
style.fontSize = 12
|
||||||
|
style.leading = 14
|
||||||
|
|
||||||
|
p = Paragraph("""This is a long note line, for use during spotting to elaborate on a cue's justification.""", style)
|
||||||
|
|
||||||
|
notes_value.draw_flowable(canvas, p, draw_baselines=True, inset_x=5., inset_y=5.)
|
||||||
|
|
||||||
|
|
||||||
|
def draw_prompt(canvas, rect, prompt= ""):
|
||||||
|
label, block = rect.split_y(0.20 * inch, direction='d')
|
||||||
|
|
||||||
|
label.draw_text_cell(canvas, "PROMPT", "Futura", 10, vertical_align='t', inset_y=5., inset_x=0.)
|
||||||
|
|
||||||
|
style = getSampleStyleSheet()['BodyText']
|
||||||
|
style.fontName = 'Futura'
|
||||||
|
style.fontSize = 14
|
||||||
|
|
||||||
|
style.leading = 24
|
||||||
|
style.leftIndent = 1.5 * inch
|
||||||
|
style.rightIndent = 1.5 * inch
|
||||||
|
|
||||||
|
block.draw_flowable(canvas, prompt, draw_baselines=True)
|
||||||
|
|
||||||
|
rect.draw_border(canvas, 'max_y')
|
||||||
|
|
||||||
|
|
||||||
|
def draw_notes(canvas, rect, note=""):
|
||||||
|
label, block = rect.split_y(0.20 * inch, direction='d')
|
||||||
|
|
||||||
|
label.draw_text_cell(canvas, "NOTES", "Futura", 10, vertical_align='t', inset_y=5., inset_x=0.)
|
||||||
|
|
||||||
|
style = getSampleStyleSheet()['BodyText']
|
||||||
|
style.fontName = 'Futura'
|
||||||
|
style.fontSize = 14
|
||||||
|
style.leading = 24
|
||||||
|
|
||||||
|
prompt = Paragraph(note, style)
|
||||||
|
|
||||||
|
block.draw_flowable(canvas, prompt, draw_baselines=True)
|
||||||
|
|
||||||
|
rect.draw_border(canvas, ['max_y', 'min_y'])
|
||||||
|
|
||||||
|
|
||||||
|
def draw_take_grid(canvas, rect):
|
||||||
|
canvas.saveState()
|
||||||
|
|
||||||
|
cp = canvas.beginPath()
|
||||||
|
cp.rect(rect.min_x, rect.min_y, rect.width, rect.height)
|
||||||
|
canvas.clipPath(cp, stroke=0, fill=0)
|
||||||
|
|
||||||
|
canvas.setDash([3.0, 2.0])
|
||||||
|
|
||||||
|
for xi in range(1, 10):
|
||||||
|
x = xi * (rect.width / 10)
|
||||||
|
if xi % 5 == 0:
|
||||||
|
canvas.setDash(1, 0)
|
||||||
|
else:
|
||||||
|
canvas.setDash([2, 5])
|
||||||
|
|
||||||
|
ln = canvas.beginPath()
|
||||||
|
ln.moveTo(rect.min_x + x, rect.min_y)
|
||||||
|
ln.lineTo(rect.min_x + x, rect.max_y)
|
||||||
|
canvas.drawPath(ln)
|
||||||
|
|
||||||
|
for yi in range(1, 10):
|
||||||
|
y = yi * (rect.height / 6)
|
||||||
|
if yi % 2 == 0:
|
||||||
|
canvas.setDash(1, 0)
|
||||||
|
else:
|
||||||
|
canvas.setDash([2, 5])
|
||||||
|
|
||||||
|
ln = canvas.beginPath()
|
||||||
|
ln.moveTo(rect.min_x, rect.min_y + y)
|
||||||
|
ln.lineTo(rect.max_x, rect.min_y + y)
|
||||||
|
canvas.drawPath(ln)
|
||||||
|
|
||||||
|
rect.draw_border(canvas, 'max_x')
|
||||||
|
|
||||||
|
canvas.restoreState()
|
||||||
|
|
||||||
|
|
||||||
|
def draw_aux_block(canvas, rect):
|
||||||
|
rect.draw_border(canvas, 'min_x')
|
||||||
|
|
||||||
|
content_rect = rect.inset_xy(10., 10.)
|
||||||
|
lines, last_line = content_rect.divide_y([12., 12., 24., 24., 24., 24.], direction='d')
|
||||||
|
|
||||||
|
lines[0].draw_text_cell(canvas, "Time for this line: 6 mins", "Futura", 9.)
|
||||||
|
lines[1].draw_text_cell(canvas, "Running time: 6 mins", "Futura", 9.)
|
||||||
|
lines[2].draw_text_cell(canvas, "Actual Start: ______________", "Futura", 9., vertical_align='b')
|
||||||
|
lines[3].draw_text_cell(canvas, "Record Date: ______________", "Futura", 9., vertical_align='b')
|
||||||
|
lines[4].draw_text_cell(canvas, "Engineer: ______________", "Futura", 9., vertical_align='b')
|
||||||
|
lines[5].draw_text_cell(canvas, "Location: ______________", "Futura", 9., vertical_align='b')
|
||||||
|
|
||||||
|
|
||||||
|
def draw_footer(canvas, rect):
|
||||||
|
rect.draw_border(canvas, 'max_y')
|
||||||
|
rect.draw_text_cell(canvas, "2021-01-01 19:14:21 - Spotting: 2021-01-01 - Page 1 of 1", font_name="Futura",
|
||||||
|
font_size=10., inset_y=2.)
|
||||||
|
|
||||||
|
|
||||||
|
def output_report():
|
||||||
|
page = GRect(0, 0, letter[0], letter[1])
|
||||||
|
|
||||||
|
page = page.inset(inch * 0.5)
|
||||||
|
|
||||||
|
(header_row, char_row, data_row, prompt_row, notes_row, takes_row), footer = \
|
||||||
|
page.divide_y([0.875 * inch, 0.375 * inch, inch, 3.0 * inch, 1.5 * inch, 3 * inch], direction= 'd')
|
||||||
|
|
||||||
|
cue_header_block, title_header_block = header_row.split_x(4.0 * inch)
|
||||||
|
(cue_number_block, timecode_block), reason_block = data_row.divide_x([1.5 * inch, 1.5 * inch])
|
||||||
|
(take_grid_block), aux_block = takes_row.split_x(5.25 * inch)
|
||||||
|
|
||||||
|
pdfmetrics.registerFont(TTFont('Futura', 'Futura.ttc') )
|
||||||
|
|
||||||
|
c = Canvas("out.pdf", pagesize=letter)
|
||||||
|
|
||||||
|
draw_header_block(c, cue_header_block)
|
||||||
|
draw_title_block(c, title_header_block)
|
||||||
|
draw_character_row(c, char_row)
|
||||||
|
draw_cue_number_block(c, cue_number_block)
|
||||||
|
draw_timecode_block(c, timecode_block)
|
||||||
|
draw_reason_block(c, reason_block)
|
||||||
|
|
||||||
|
draw_prompt(c, prompt_row)
|
||||||
|
draw_notes(c, notes_row)
|
||||||
|
|
||||||
|
draw_take_grid(c, take_grid_block)
|
||||||
|
draw_aux_block(c, aux_block)
|
||||||
|
|
||||||
|
draw_footer(c, footer)
|
||||||
|
|
||||||
|
c.showPage()
|
||||||
|
c.save()
|
||||||
6
requirements.txt
Normal file
6
requirements.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
ptulsconv~=0.6.0
|
||||||
|
setuptools~=56.2.0
|
||||||
|
reportlab~=3.5.67
|
||||||
|
ffmpeg~=1.4
|
||||||
|
parsimonious~=0.8.1
|
||||||
|
tqdm~=4.60.0
|
||||||
2
setup.py
2
setup.py
@@ -30,7 +30,7 @@ setup(name='ptulsconv',
|
|||||||
"Topic :: Text Processing :: Markup :: XML"],
|
"Topic :: Text Processing :: Markup :: XML"],
|
||||||
packages=['ptulsconv'],
|
packages=['ptulsconv'],
|
||||||
keywords='text-processing parsers film tv editing editorial',
|
keywords='text-processing parsers film tv editing editorial',
|
||||||
install_requires=['parsimonious', 'tqdm'],
|
install_requires=['parsimonious', 'tqdm', 'reportlab'],
|
||||||
package_data={
|
package_data={
|
||||||
"ptulsconv": ["xslt/*.xsl"]
|
"ptulsconv": ["xslt/*.xsl"]
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user