def oneColumnPage(cls, theme, doc, page=None, parent=None, pageNumbers=None, **kwargs): """ >>> from pagebotnano.document import Document >>> from pagebotnano.themes import BackToTheCity >>> template = OneColumnTemplate() >>> theme = BackToTheCity() >>> doc = Document() >>> page = template.oneColumnPage(theme, doc, pageNumbers=NONE) >>> page.compose(doc) >>> page.find(MAIN) <TextBox name=mainText w=535 h=782> >>> page = doc.newPage(template=oneColumnPage) """ page = cls._initialize(theme, doc, page, parent) # Add text element with the main text column of this page e = TextBox('', name=MAIN, x=page.pl, y=page.pb, w=page.pw, h=page.ph) page.addElement(e) if pageNumbers is None: pageNumbers = [LEFT, RIGHT] if LEFT in pageNumbers and page.pn % 2 == 0: # Even page number?: bs = BabelString(str(page.pn), style) e = Text(bs, name=PN_LEFT, x=page.pl, y=page.pb/2) page.addElement(e) if CENTER in pageNumbers: e = Text('', name=PN_CENTER, x=page.pl+page.pw, y=page.pb/2) page.addElement(e) if RIGHT in pageNumbers: e = Text('', name=PN_RIGHT, x=page.pl+page.pw, y=page.pb/2) page.addElement(e) return page
def compose(self, doc, page, parent=None): """Compose the cell as background color, with recipes text block on top. """ label = self._getLabel() bs = BabelString(label, self.style) tw, th = bs.textSize if self.layout == SPOTSAMPLE: # Mark abbreviated color recipes by parenthesis. # They are not an exact match, but closest known value for this color. # Used padding-bottom (self.pb) also as gutter between color rectangle and labels e = Rect(x=0, y=th+self.pb, w=self.w, h=self.h-th-self.pb, fill=self.c) self.addElement(e) e = Text(bs, x=self.w/2, y=th-self.style.get('lineHeight', 0) + self.pb, w=self.w, h=self.h) self.addElement(e) else: # Default layout is OVERLAY e = Rect(x=0, y=0, w=self.w, h=self.h, fill=self.c) self.addElement(e) e = Text(bs, x=self.w/2, y=th-self.style.get('lineHeight', 0) + self.pb, w=self.w, h=self.h) self.addElement(e)
def page(self, doc): """ >>> from pagebotnano.document import Document >>> from pagebotnano.themes import BackToTheCity >>> templates = OneColumnTemplates() >>> templates <OneColumnTemplates> >>> theme = BackToTheCity() >>> doc = Document(theme=theme, templates=templates) >>> page = templates.cover(doc) # The "page" template always must be there. >>> page.compose(doc) """ page = self._initialize(doc, True) # Add text element with the main text column of this page e = TextBox('', name=MAIN, x=page.pl, y=page.pb, w=page.pw, h=page.ph) page.addElement(e) leftPageNumberStyle = doc.theme.getStyle('leftPageNumber') centerPageNumberStyle = doc.theme.getStyle('centerPageNumber') rightPageNumberStyle = doc.theme.getStyle('rightPageNumber') if leftPageNumberStyle is not None and page.pn % 2 == 0: # Even page number?: bs = BabelString(str(page.pn), style) e = Text(bs, name=PN_LEFT, x=page.pl, y=page.pb / 2) page.addElement(e) if centerPageNumberStyle is not None: e = Text('', name=PN_CENTER, x=page.pl + page.pw, y=page.pb / 2) page.addElement(e) if rightPageNumberStyle is not None and page.pn % 2 != 0: # Odd page number?: e = Text('', name=PN_RIGHT, x=page.pl + page.pw, y=page.pb / 2) page.addElement(e) return page
def addPageNumberXXX(self, page, pad, leftStyle, rightStyle): # Add text element with page number if page.pn % 2 == 0: # Even page number? style = leftStyle x = pad else: # Odd page number style = rightStyle x = page.w - pad pn = BabelString(str(page.pn), style) # Center the page number. #e = Text(pn, page.w/2, pad/2) e = Text(pn, x=x, y=pad * 3 / 4, w=page.w - 2 * pad, fill=0.9) page.addElement(e)
def compose(self, doc, parent=None): """Compose the cell as background color, with recipes text block on top. """ label = self._getLabel() if self.layout == SPOTSAMPLE: # Mark approximated color recipes by parenthesis. # They are not an exact match, but closest known value for this color. bs = BabelString(label, self.style) tw, th = bs.textSize # Used padding-bottom (self.pb) also as gutter between color rectangle and labels e = Rect(x=self.pl, y=th+self.pb, w=self.pw, h=self.h-th-self.pb, fill=self.c) self.addElement(e) e = Text(bs, x=self.w/2, y=th-self.style.get('fontSize') + self.pb, w=self.w, h=self.h) self.addElement(e) else: # Default layout is OVERLAY. # Check the text color to be enough contrast with the background. # Otherwise flip between black and white. style = copy(self.style) # Copy as we are going to alter it if self.c.gray < 0.33: # Dark color? labelText = color(1) # White label text else: labelText = color(0) # Black label text style['fill'] = labelText bs = BabelString(label, style) tw, th = bs.textSize e = Rect(x=self.pl, y=self.pb, w=self.pw, h=self.ph, fill=self.c) self.addElement(e) e = Text(bs, x=self.w/2, y=th-self.style.get('fontSize')*2/3 + self.pb, w=self.w, h=self.h) self.addElement(e)
def compose(self, doc, page, parent=None): """Compose the cell as background color, with recipes text block on top. """ e = Rect(x=0, y=0, w=self.w, h=self.h, fill=self.c) self.addElement(e) # Mark approximated color recipes by parenthesis. # They are not an exact match, but closest known value for this color. Cmyk, cMyk, cmYk, cmyK = self.c.cmyk s = '#%s\n(%s)\n(cmyk %d %d %d %d)\n(Spot %s)\n(RAL %s)' % \ (self.c.hex, self.c.name.capitalize(), Cmyk*100, cMyk*100, cmYk*100, cmyK*100, self.c.spot, self.c.ral) if self.themePosition is not None: s += '\nColor[%d][%d]' % (base, shade) bs = BabelString(s, self.style) tw, th = bs.textSize e = Text(bs, x=self.w/2, y=th-self.style.get('lineHeight', 0)/2, w=self.w, h=self.h) self.addElement(e)
def drawContent(self, ox, oy, doc, page, parent): """Draw the content of this single glyph/string fitting, with line indicators of vertical metrics. TODO: Show more font metrics and glyph metrics here. Add labels of values and names. """ labelStyle = dict(font=self.font, fontSize=7, textFill=0, lineHeight=8, aligh=LEFT) style = dict(font=self.font, fontSize=self.fontSizes[0], textFill=0, align=LEFT) bs = BabelString('', style) tw, th = bs.textSize for fontSize in self.fontSizes: labelLine = BabelString(' %d pt' % fontSize, labelStyle) ltw, lth = labelLine.textSize style['fontSize'] = fontSize style['lineHeight'] = fontSize * self.leading sample = self.sample textLine = BabelString(sample, style) stw, sth = textLine.textSize while sample and self.w and stw + ltw > self.w: # If not fitting, shorten the string until it does sample = sample[:-1] textLine = BabelString(sample, style) stw, sth = textLine.textSize if self.h and th + sth > self.h: break # No vertical space left, skip the rest of the fontSizes. bs += BabelString( '\n' + sample, style ) + labelLine # There still is vertical space, add the textLine tw, th = bs.textSize e = Text(bs, x=ox, y=oy + self.h) page.h = page.pb + page.pt + th page.addElement(e)
def frenchPage(cls, theme, doc, page=None, parent=None, **kwargs): """Compose the template page with the position of the “French” (or “Voordehandse”) text. Empty page, with only the title of the book centere on the page width. BabelString alignment should be CENTER. >>> from pagebotnano.document import Document >>> from pagebotnano.themes import BackToTheCity >>> template = OneColumnTemplate() >>> theme = BackToTheCity() >>> doc = Document() >>> page = template.frenchPage(theme, doc) >>> page.compose(doc) >>> page.find(MAIN) <Text name=mainText w=None h=None> """ page = _initialize(doc, page, parent) e = Text('', name=MAIN, x=page.pl + page.pw / 2, y=page.h * 4 / 5) page.addElement(e) return page
def drawContent(self, ox, oy, doc, page, parent): """Draw the content of this single glyph/string fitting, with line indicators of vertical metrics. TODO: Show more font metrics and glyph metrics here. Add labels of values and names. TODO: For large sizes, compensate for the side beatings of straight stems. """ y = self.h for word in self.words: if self.fontChoice: fontName = choice(self.fontChoice) else: fontName = self.font if self.theme is None: textColor = Color(0) else: textColor = self.theme.randomTextColor style = dict(font=fontName, textFill=textColor, align=LEFT) if self.capsOnly: word = word.upper() style[ 'fontSize'] = fontSize = 100 # Start with large guess of fontSize textLine = BabelString(word, style) tlw, tlh = textLine.textSize style['fontSize'] = fontSize = fontSize * self.w / tlw textLine = BabelString(word, style) # Get a new scaled version tlw, tlh = textLine.textSize if tlh > y: # Not fitting this word vertical anymore, try other. continue if self.capsOnly: y -= doc.context.fontCapHeight(self.font, fontSize) else: y -= doc.context.fontAscender(self.font, fontSize) e = Text(textLine, x=ox, y=oy + y) page.addElement(e) if not self.capsOnly: y += doc.context.fontDescender(self.font, fontSize) y -= fontSize * (self.leading - 1) + self.gh
x=page.pl + cw + GUTTER_X, y=page.pt, w=cw, h=page.ph, gh=GUTTER_Y, capsOnly=capsOnly, fill=0.9) page.addElement(e) footNoteStyle = dict(font=labelFontName, tracking=0.5, fontSize=labelSize, fill=labelColor, align=LEFT) bs = BabelString('TYPETR ' + fontName1, footNoteStyle) e = Text(bs, x=page.pl, y=page.pb / 2, w=page.pw) page.addElement(e) footNoteStyle = dict(font=labelFontName, tracking=0.5, fontSize=labelSize, fill=labelColor, align=LEFT) bs = BabelString('TYPETR ' + fontName1, footNoteStyle) e = Text(bs, x=page.pl, y=page.pb / 2, w=page.pw) page.addElement(e) footNoteStyle['align'] = RIGHT bs = BabelString('Generated by PageBotNano', footNoteStyle) e = Text(bs, x=page.pl + page.pw, y=page.pb / 2, w=page.pw) page.addElement(e)
def compose(self, doc, parent=None): """Compose the self.elements with ColorCell instances, as now we know the actual size of self. Draw the cells of the theme in the given element size. """ if self.theme is None: self.theme = doc.theme or DefaultTheme() if self.labelStyle is None: self.labelStyle = dict(font=FONT_NAME, fontSize=self.FONT_SIZE, align=CENTER, lineHeight=self.FONT_SIZE*self.LEADING) if self.labels is None: self.labels = (HEX,) fontName = self.labelStyle.get('fontName', self.FONT_NAME) fontSize = self.labelStyle.get('fontSize', self.FONT_SIZE) textFill = self.theme.colors[0][4] backgroundFill = self.theme.getColor(0,2) if self.titleStyle and not isinstance(self.titleStyle, dict): self.titleStyle = dict(font=fontName, fontSize=fontSize*2, fill=textFill, lineHeight=fontSize*2*self.LEADING) if self.captionStyle and not isinstance(self.captionStyle, dict): self.captionStyle = dict(font=fontName, fontSize=fontSize, fill=textFill, lineHeight=self.LEADING) cols = len(self.theme.colors[0]) rows = len(self.theme.colors) cw = self.pw/cols # Column width ch = self.ph/rows # Column height for shade in range(cols): for base in range(rows): # Get the color from the theme color matrix and add as rectangle # This is the extended example, instead of using the ColorCell element. c = self.theme.colors[base][shade] # If textColor not defined, then get it from the theme, based on the # darkness of the current color. tc = self.theme.textColor(base, shade) # The ColorCell element takes care of showing the color as rectangle # and the lines of various recipes on top. e = ColorCell(c, x=self.pl+shade*cw, y=self.pb+base*ch, w=cw, h=ch, themePosition=(shade, base), layout=self.layout, labels=self.labels, style=self.labelStyle, padding=self.cellPadding) e.pb = self.labelStyle['lineHeight'] self.addElement(e) e.compose(doc, parent=self) # Do recursive compose. if self.titleStyle: # Add background rectangle on top with theme name and mood. getColor(shade, base) bs = BabelString('%s – %s' % (self.theme.name, self.theme.mood), self.titleStyle) tw, th = bs.textSize e = Text(bs, x=self.pl+fontSize/2, y=self.h-self.pt*2/3, w=self.w) self.addElement(e) if self.captionStyle: bs = BabelString('Colors with (parenthesis) are approximated to the closest recipe.', self.captionStyle) tw, th = bs.textSize e = Text(bs, x=self.pl, y=self.pb-th, w=self.pw) self.addElement(e) captionStyle2 = copy(self.captionStyle) captionStyle2['align'] = RIGHT bs = BabelString('Generated by PageBotNano', captionStyle2) tw, th = bs.textSize e = Text(bs, x=self.pl+self.pw, y=self.pb-th, w=self.pw) self.addElement(e)
def compose(self): """This is the core of a publication, composing the specific content of the document. The compose method gets called before building and exporting the self.doc document. """ fontSize = 11 headSize = fontSize*1.5 titleSize = 36 subTitleSize = titleSize * 0.5 pad = 48 self.theme.styles['h1'] = dict(font='Georgia-Bold', lineHeight=titleSize*1.1, fontSize=titleSize, align=CENTER, fill=1, # White title on dark cover background language=EN, hyphenation=False, ) self.theme.styles['h2'] = dict(font='Georgia-Italic', paragraphTopSpacing=subTitleSize/2, lineHeight=subTitleSize*1.2, fontSize=subTitleSize, align=CENTER, fill=1, # White title on dark cover background language=EN, hyphenation=False, ) headStyle = dict(font='Georgia', lineHeight=headSize*1.3, fontSize=headSize, fill=0, # Black text language=EN, hyphenation=False, ) subHeadStyle = dict(font='Georgia-Italic', lineHeight=headSize*0.8*1.4, fontSize=headSize*0.8, fill=0, # Black text language=EN, hyphenation=False, ) bodyStyle = dict(font='Georgia', lineHeight=fontSize*1.4, fontSize=fontSize, fill=0, # Black text language=EN, hyphenation=True, ) pageNumberLeftStyle = dict( font='Georgia', fontSize=9, fill=0, # Black text align=LEFT, ) pageNumberRightStyle = copy(pageNumberLeftStyle) pageNumberRightStyle['align'] = RIGHT # Make the cover page. page = coverPage(self.theme, self.doc) # Make “French” “Voordehandse” page. page = self.doc.newPage() # No page number here. # CENTER text alignment overwrites the value in headStyle. # fontSize overwrites the value in headStyle bs = BabelString('AAAA'+'\n', headStyle, fontSize=fontSize, align=CENTER) e = Text(bs, x=page.w/2, y=page.h*4/5) page.addElement(e) # Make Title page. page = titlePage(self.theme, self.doc) page.compose(self.doc, page) bs = BabelString('VVVVV'+'\n', headStyle, align=CENTER) bs.append(BabelString('AUTHOR', subHeadStyle, align=CENTER)) page.find(MAIN).bs = bs # For all the elements that are collected in the galley, assume that # the TextBoxes are chapters, creating a new page for them. # If the TextBox does not fit on the page, keep adding new pages # until all of the BabelString overfill is processed. for ge in self.galley.elements: if isinstance(ge, TextBox): bs = ge.bs # Get the BabelString from the galley box. for n in range(self.MAX_PAGES): page = self.doc.newPage() # Add text element with page number self.addPageNumber(page, pad, pageNumberLeftStyle, pageNumberRightStyle) # Add text element with the main text column of this page e = TextBox(bs, x=pad, y=pad, w=page.w-2*pad, h=page.h-2*pad) page.addElement(e) # If there is overflow on this page, continue looping creating # as many pages as needed to fill all the text in self.content. # Otherwise break the loop, as we are done placing content. bs = e.getOverflow(bs, doc=self.doc) # Test on this “incomplete” BabelString, as it only has a cached FS if not bs.fs: break elif isinstance(ge, Image): # Images not supported yet page = self.doc.newPage() self.addPageNumber(page, pad, pageNumberLeftStyle, pageNumberRightStyle) page.addElement(ge) ge.w = page.w - pad iw, ih = ge.getSize(self.doc) ge.x = pad/2 ge.y = page.h - pad - ih