#!/bin/env python
#copyright ReportLab Inc. 2001
#see license.txt for license details
#history http://cvs.sourceforge.net/cgi-bin/cvsweb.cgi/docs/tools/rl_doc_utils.py?cvsroot=reportlab
#$Header: /cvsroot/reportlab/reportlab/tools/docco/rl_doc_utils.py,v 1.2 2001/11/13 18:59:50 dinu_gherman Exp $
__version__=''' $Id: rl_doc_utils.py,v 1.2 2001/11/13 18:59:50 dinu_gherman Exp $ '''


__doc__ = """
This module contains utilities for generating guides
"""

import os, sys, glob
import string
import StringIO

from rltemplate import RLDocTemplate
from stylesheet import getStyleSheet
styleSheet = getStyleSheet()

#from reportlab.platypus.doctemplate import SimpleDocTemplate
from reportlab.lib.units import inch
from reportlab.lib.pagesizes import letter, A4, A5, A3	# latter two for testing
from reportlab.rl_config import defaultPageSize
from reportlab.platypus import figures
from reportlab.platypus import Paragraph, Spacer, Preformatted,\
			PageBreak, CondPageBreak, Flowable, Table, TableStyle, \
			NextPageTemplate, KeepTogether, Image, XPreformatted
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib import colors
from reportlab.lib.sequencer import getSequencer

import examples

appmode=0


from t_parse import Template
QFcodetemplate = Template("X$X$", "X")
QFreptemplate = Template("X^X^", "X")
codesubst = "%s<font name=Courier>%s</font>"
QFsubst = "%s<font name=Courier><i>%s</i></font>"


def quickfix(text):
	"""inside text find any subsequence of form $subsequence$.
	   Format the subsequence as code.	If similarly if text contains ^arg^
	   format the arg as replaceable.  The escape sequence for literal
	   $ is $\\$ (^ is ^\\^.
	"""
	from string import join
	for (template,subst) in [(QFcodetemplate, codesubst), (QFreptemplate, QFsubst)]:
		fragment = text
		parts = []
		try:
			while fragment:
				try:
					(matches, index) = template.PARSE(fragment)
				except: raise ValueError
				else:
					[prefix, code] = matches
					if code == "\\":
						part = fragment[:index]
					else:
						part = subst % (prefix, code)
					parts.append(part)
					fragment = fragment[index:]
		except ValueError:
			parts.append(fragment)
		text = join(parts, "")
	return text
#print quickfix("$testing$ testing $one$ ^two^ $three(^four^)$")



H1 = styleSheet['Heading1']
H2 = styleSheet['Heading2']
H3 = styleSheet['Heading3']
H4 = styleSheet['Heading4']
B = styleSheet['BodyText']
BU = styleSheet['Bullet']
Comment = styleSheet['Comment']
Centred = styleSheet['Centred']
Caption = styleSheet['Caption']

#set up numbering
seq = getSequencer()
seq.setFormat('Chapter','1')
seq.setFormat('Section','1')
seq.setFormat('Appendix','A')
seq.setFormat('Figure', '1')
seq.chain('Chapter','Section')
seq.chain('Chapter','Figure')

lessonnamestyle = H2
discussiontextstyle = B
exampletextstyle = styleSheet['Code']
# size for every example
examplefunctionxinches = 5.5
examplefunctionyinches = 3
examplefunctiondisplaysizes = (examplefunctionxinches*inch, examplefunctionyinches*inch)

def getJustFontPaths():
	'''return afm and pfb for Just's files'''
	import reportlab
	folder = os.path.dirname(reportlab.__file__) + os.sep + 'fonts'
	return os.path.join(folder, 'LeERC___.AFM'), os.path.join(folder, 'LeERC___.PFB')

# for testing
def NOP(*x,**y):
	return None

def CPage(inches):
	getStory().append(CondPageBreak(inches*inch))

def newPage():
	getStory().append(PageBreak())

def nextTemplate(templName):
	f = NextPageTemplate(templName)
	getStory().append(f)

def disc(text, klass=Paragraph, style=discussiontextstyle):
	text = quickfix(text)
	P = klass(text, style)
	getStory().append(P)

def restartList():
	getSequencer().reset('list1')

def list(text, doBullet=1):
	text=quickfix(text)
	if doBullet:
		text='<bullet><seq id="list1"/>.</bullet>'+text
	P = Paragraph(text, BU)
	getStory().append(P)

def bullet(text):
	text='<bullet><font name="Symbol">'+chr(183)+'</font></bullet>' + quickfix(text)
	P = Paragraph(text, BU)
	getStory().append(P)

def eg(text,before=0.1,after=0):
	space(before)
	disc(text, klass=Preformatted, style=exampletextstyle)
	space(after)

def space(inches=1./6):
	if inches: getStory().append(Spacer(0,inches*inch))

def EmbeddedCode(code,name='t'):
	eg(code)
	disc("produces")
	exec code+("\ngetStory().append(%s)\n"%name)

def startKeep():
	return len(getStory())

def endKeep(s):
	S = getStory()
	k = KeepTogether(S[s:])
	S[s:] = [k]

def title(text):
	"""Use this for the document title only"""
	disc(text,style=styleSheet['Title'])

#AR 3/7/2000 - defining three new levels of headings; code
#should be swapped over to using them.

def heading1(text):
	"""Use this for chapters.  Lessons within a big chapter
	should now use heading2 instead.  Chapters get numbered."""
	getStory().append(PageBreak())
	p = Paragraph('Chapter <seq id="Chapter"/> ' + quickfix(text), H1)
	getStory().append(p)

def Appendix1(text,):
	global appmode
	getStory().append(PageBreak())
	if not appmode:
		seq.setFormat('Chapter','A')
		seq.reset('Chapter')
		appmode = 1
	p = Paragraph('Appendix <seq id="Chapter"/> ' + quickfix(text), H1)
	getStory().append(p)

def heading2(text):
	"""Used to be 'lesson'"""
	getStory().append(CondPageBreak(inch))
	p = Paragraph('<seq template="%(Chapter)s.%(Section+)s "/>' + quickfix(text), H2)
	getStory().append(p)

def heading3(text):
	"""Used to be most of the plain old 'head' sections"""
	getStory().append(CondPageBreak(inch))
	p = Paragraph(quickfix(text), H3)
	getStory().append(p)

def image(path, width=None, height=None ):
	s = startKeep()
	space(.2)
	getStory().append(Image(os.path.join(os.path.dirname(sys.argv[0]),'..','images',path),width,height))
	space(.2)
	endKeep(s)

def heading4(text):
	"""Used to be most of the plain old 'head' sections"""
	getStory().append(CondPageBreak(inch))
	p = Paragraph(quickfix(text), H4)
	getStory().append(p)

def todo(text):
	"""Used for notes to ourselves"""
	getStory().append(Paragraph(quickfix(text), Comment))

def centred(text):
	getStory().append(Paragraph(quickfix(text), Centred))

def caption(text):
	getStory().append(Paragraph(quickfix(text), Caption))

class Illustration(figures.Figure):
	"""The examples are all presented as functions which do
	something to a canvas, with a constant height and width
	used.  This puts them inside a figure box with a caption."""

	def __init__(self, operation, caption, width=None, height=None):
		stdwidth, stdheight = examplefunctiondisplaysizes
		if not width:
			width = stdwidth
		if not height:
			height = stdheight
		#figures.Figure.__init__(self, stdwidth * 0.75, stdheight * 0.75)
		figures.Figure.__init__(self, width, height,
					'Figure <seq template="%(Chapter)s-%(Figure+)s"/>: ' + quickfix(caption))
		self.operation = operation

	def drawFigure(self):
		#shrink it a little...
		#self.canv.scale(0.75, 0.75)
		self.operation(self.canv)


def illust(operation, caption, width=None, height=None):
	i = Illustration(operation, caption, width=width, height=height)
	getStory().append(i)


class GraphicsDrawing(Illustration):
	"""Lets you include reportlab/graphics drawings seamlessly,
	with the right numbering."""
	def __init__(self, drawing, caption):
		figures.Figure.__init__(self,
								  drawing.width,
								  drawing.height,
					'Figure <seq template="%(Chapter)s-%(Figure+)s"/>: ' + quickfix(caption)
								  )
		self.drawing = drawing

	def drawFigure(self):
		d = self.drawing
		d.wrap(d.width, d.height)
		d.drawOn(self.canv, 0, 0)

def draw(drawing, caption):
	d = GraphicsDrawing(drawing, caption)
	getStory().append(d)

class ParaBox(figures.Figure):
	"""Illustrates paragraph examples, with style attributes on the left"""
	descrStyle = ParagraphStyle('description',
								fontName='Courier',
								fontSize=8,
								leading=9.6)

	def __init__(self, text, style, caption):
		figures.Figure.__init__(self, 0, 0, caption)
		self.text = text
		self.style = style
		self.para = Paragraph(text, style)

		styleText = self.getStyleText(style)
		self.pre = Preformatted(styleText, self.descrStyle)

	def wrap(self, availWidth, availHeight):
		"""Left 30% is for attributes, right 50% for sample,
		10% gutter each side."""
		self.x0 = availWidth * 0.05  #left of box
		self.x1 = availWidth * 0.1	 #left of descriptive text
		self.x2 = availWidth * 0.5	 #left of para itself
		self.x3 = availWidth * 0.9	 #right of para itself
		self.x4 = availWidth * 0.95  #right of box
		self.width = self.x4 - self.x0
		self.dx = 0.5 * (availWidth - self.width)

		paw, self.pah = self.para.wrap(self.x3 - self.x2, availHeight)
		self.pah = self.pah + self.style.spaceBefore + self.style.spaceAfter
		prw, self.prh = self.pre.wrap(self.x2 - self.x1, availHeight)
		self.figureHeight = max(self.prh, self.pah) * 10.0/9.0
		return figures.Figure.wrap(self, availWidth, availHeight)

	def getStyleText(self, style):
		"""Converts style to preformatted block of text"""
		lines = []
		for (key, value) in style.__dict__.items():
			lines.append('%s = %s' % (key, value))
		lines.sort()
		return string.join(lines, '\n')

	def drawFigure(self):

		#now we fill in the bounding box and before/after boxes
		self.canv.saveState()
		self.canv.setFillGray(0.95)
		self.canv.setDash(1,3)
		self.canv.rect(self.x2 - self.x0,
					   self.figureHeight * 0.95 - self.pah,
					   self.x3-self.x2, self.para.height,
					   fill=1,stroke=1)

		self.canv.setFillGray(0.90)
		self.canv.rect(self.x2 - self.x0, #spaceBefore
					   self.figureHeight * 0.95 - self.pah + self.para.height,
					   self.x3-self.x2, self.style.spaceBefore,
					   fill=1,stroke=1)

		self.canv.rect(self.x2 - self.x0, #spaceBefore
					   self.figureHeight * 0.95 - self.pah - self.style.spaceAfter,
					   self.x3-self.x2, self.style.spaceAfter,
					   fill=1,stroke=1)

		self.canv.restoreState()
		#self.canv.setFillColor(colors.yellow)
		self.para.drawOn(self.canv, self.x2 - self.x0,
						 self.figureHeight * 0.95 - self.pah)
		self.pre.drawOn(self.canv, self.x1 - self.x0,
						 self.figureHeight * 0.95 - self.prh)


	def getStyleText(self, style):
		"""Converts style to preformatted block of text"""
		lines = []
		for (key, value) in style.__dict__.items():
			if key not in ('name','parent'):
				lines.append('%s = %s' % (key, value))
		return string.join(lines, '\n')


class ParaBox2(figures.Figure):
	"""Illustrates a paragraph side-by-side with the raw
	text, to show how the XML works."""
	def __init__(self, text, caption):
		figures.Figure.__init__(self, 0, 0, caption)
		descrStyle = ParagraphStyle('description',
								fontName='Courier',
								fontSize=8,
								leading=9.6)
		textStyle = B
		self.text = text
		self.left = Paragraph('<![CDATA[' + text + ']]>', descrStyle)
		self.right = Paragraph(text, B)


	def wrap(self, availWidth, availHeight):
		self.width = availWidth * 0.9
		colWidth = 0.4 * self.width
		lw, self.lh = self.left.wrap(colWidth, availHeight)
		rw, self.rh = self.right.wrap(colWidth, availHeight)
		self.figureHeight = max(self.lh, self.rh) * 10.0/9.0
		return figures.Figure.wrap(self, availWidth, availHeight)

	def drawFigure(self):
		self.left.drawOn(self.canv,
						 self.width * 0.05,
						 self.figureHeight * 0.95 - self.lh
						 )
		self.right.drawOn(self.canv,
						 self.width * 0.55,
						 self.figureHeight * 0.95 - self.rh
						 )

def parabox(text, style, caption):
	p = ParaBox(text, style,
				'Figure <seq template="%(Chapter)s-%(Figure+)s"/>: ' + quickfix(caption)
				)
	getStory().append(p)

def parabox2(text, caption):
	p = ParaBox2(text,
				'Figure <seq template="%(Chapter)s-%(Figure+)s"/>: ' + quickfix(caption)
				)
	getStory().append(p)

def pencilnote():
	getStory().append(examples.NoteAnnotation())


from reportlab.lib.colors import tan, green
def handnote(xoffset=0, size=None, fillcolor=tan, strokecolor=green):
	getStory().append(examples.HandAnnotation(xoffset,size,fillcolor,strokecolor))


#make a singleton, created when requested rather
#than each time a chapter imports it.
_story = []
def getStory():
	return _story
