Skip to main content

Ralsina.Me — Roberto Alsina's website

Posts about python (old posts, page 65)

PET: English Translation Issue 1 has a date

Be­cause it worked on­ce, let's do it again. I have just set a com­plete­ly ar­bi­trary, and prob­a­bly too ear­ly date for the re­lease of the first eng­lish Is­sue of "PET: Python En­tre To­dos" mag­a­zine.

The eng­lish ver­sion is called PET, which means "Python En­tre To­dos: Eng­lish Trans­la­tion".

It will have the same con­tents as the first span­ish Is­sue and... it will be the last Is­sue done like this.

From now on, both ver­sions will be pub­lished at the same time, if we can.

So, there will be a very short gap be­tween the eng­lish first Is­sue and the sec­ond one (less than a mon­th, we hope).

So, stay tuned

Things I learned publishing a magazine

To­day at 00:00:00 GMT-3 PET: Python en­tre to­dos was in­deed launched, in time (ar­bi­trary but forced) and in bud­get ($0).

So, what did I learn? I learned a lot!

  • The on­­ly thing you need to pub­­lish an e-­­mag is time and con­­ten­t.

  • Time can be con­vert­ed in­­­to con­­ten­t, but if you write ev­ery­thing your­­self it's a blog, not a mag­a­zine. Luck­­i­­ly, PET found great con­trib­u­­tors.

  • If you want util­i­­tar­i­an de­sign, rst2pdf can do the job

  • In fac­t, it can do it bet­ter than oth­­er tools in some ways

    • I can push a fixed ver­­­sion of the PDFs in 5 min­utes for all lay­out­s. How much would it take me us­ing Scribus or oth­­­er DT­P? In a mag­a­zine where cor­rec­t­­­ness mat­ter­s, that's a big deal.

    • TOCs are bet­ter than in most am­a­­­teur PDF mag­a­zines I've seen. The in­­­-­­­con­­ten­t-­­­TOC is click­­­able, and the PDF TOC is per­fec­t.

    • Page num­bers in the PDF TOC make sense (no, the cov­­­er is not page 1)

    • I am pro­­­duc­ing 6 PDF ver­­­sion­s: A4(b­w, colour), A5(b­w,­­­colour), Book­let(b­w, colour) and I could add any oth­­­er I want in a few min­utes.

  • I learned about PDF im­po­si­­tion!

Let's ex­plain the last one:

Sup­pose you want to print a small book­let, and you have 32 pages of con­tent. How do you do that?

The eas­i­est way is to print it 2-up dou­ble-sid­ed in A4 pa­per so that you can stack the pages, fold them down the mid­dle, sta­ple them, and get a nice A5 book­let.

The prob­lem is that the page or­der­ing is hard to get right. For ex­am­ple, for a 4-­page book­let, you need to print one A4 page with pages 4-1 on one side and 2-3 on the oth­er. For an 8 page book­let it's 8-1,2-7,3-6,4-5.

Lucklily there's a way to get this done au­to­mat­i­cal­ly:

1. In­stall pod­ofo 3. Get book­let-A4.­plan (see be­low) 2. Run this:

podofoimpose my-A5-pages.pdf my-booklet.pdf booklet-A4.plan lua

book­let-A4.­plan is this:

---Generic Booklet (A4)
---
---It is said generic as it will try to determine
---automatically how to fit the booklet onto A4
---paper sheets, scaling pages if necessary.
---it is well suited for office documents for
---which you do not care too much about resulting
---imposition artefacts since it manages to save
---paper!
---
-- print("Booklet")
-- We output an A4 booklet
PageWidth = 595.27559
PageHeight = 841.88976

print("PageCount",PageCount)

-- We assume that H > W
-- Argh, we now can do better since we have "if" ;-)
-- Scale = PageHeight / (2*SourceWidth)
if(SourceWidth <= SourceHeight)
then
--  If you A5 pages are not really A5, uncomment the next line
--  Scale = PageHeight / (2*SourceWidth)
    Scale = 1
    rot = 90
        xof = SourceHeight
        yofRA = 0
        yofRB = SourceWidth
        yofVA = 0
        yofVB = SourceWidth
else
--  If you A5 pages are not really A5, uncomment the next line
--  Scale = PageHeight / (2*SourceHeight)
    Scale = 1
    rot = 0
        xof = 0;
        yofRA = 0
        yofRB = SourceHeight
        yofVA = SourceHeight
        yofVB = 0
end

do
    rest = PageCount % 4
    totp = PageCount
    if rest ~= 0
        then
        totp = totp + ( 4 - rest)
        end
    inc = 0
    count = 0
    imax = totp/4
    while count < imax
        do
--          We assume that podofoimpose will discard invalid records
--          such as those with source page greater than PageCount
--          print(totp, inc, rot, xof,yofRA, yofRA, yofVA, yofVB)
-- Recto
        PushRecord(totp - inc , inc + 1 , rot, xof , yofRA)
        PushRecord(inc + 1 , inc + 1 , rot, xof , yofRB)
-- Verso
        PushRecord(inc + 2 , inc + 2 , rot, xof , yofVA)
        PushRecord(totp-(inc + 1) , inc + 2 , rot, xof, yofVB)

        count = count + 1
        inc = inc + 2
        end
end

That code is tak­en from here: http://www.oep-h.­com/im­pose/

And voilá, you get a scram­bled PDF with the pages in ex­act­ly the right or­der (and emp­ty pages added as need­ed).

Come see me in Bahía Blanca next weekend!

I will be speak­ing at the Jor­nadas del Sur in Bahía Blan­ca this week­end (Au­gust 14/15 and 16).

For a change, I will not be giv­ing my old tired talk­s, but a brand new one, called "The Am­a­teur", and prob­a­bly a light­ning talk or some­thing else.

The usu­al of­fer of free beer will not be pos­si­ble this year, so: If you men­tion this blog in the QA ses­sion, you get... free can­dy!

Af­ter that, I will be speak­ing at FM La Tribu on sat­ur­day Au­gust 21st in the Char­las Abier­tas 2010 where I will be speak­ing about a lot of things be­tween vir­tualenv and nose and tox and oth­er test­ing-re­lat­ed top­ic­s.

It's coming: PET - Python Entre Todos

Edit­ed

I re­moved the count­down be­cause it was wrong (time­zone prob­lem). Check http://re­vista.python.org.ar for one that works :-)

I have been dragged in­to YAP (Yet An­oth­er Pro­jec­t) and in this case it's the first-is­sue-ev­er of PET - Python En­tre To­dos a python on­line mag­a­zine in span­ish.

This is an emer­gent out­put of PyAr the on­line Python com­mu­ni­ty of Ar­genti­na, and it may nev­er have a sec­ond is­sue un­less we get a lot of help, so pay at­ten­tion to the first one :-)

I have been do­ing web­mas­ter­ing, edit­ing and PDF pro­duc­tion, Emil­iano Dal­la Verde Mar­cozzi has been help­ing ev­ery­where and get­ting ar­ti­cles.

The first is­sue is some 45 A5 pages (or 23 A4 ones) in small type, so it's not ex­act­ly huge, but it has half a dozen ar­ti­cles aimed at dif­fer­ent python lev­el­s.

It has been a lot of fun so far, I hope it's well re­ceived!

Extending rst2pdf: easy and powerful

I do al­most all my busi­ness writ­ing (and my book) us­ing re­struc­tured tex­t. And when I want to pro­duce print­-qual­i­ty out­put, I tend to use my own tool, rst2pdf.

It's pop­u­lar, sure­ly my most pop­u­lar pro­gram, but very few know it's al­so ex­treme­ly easy to ex­tend (hat tip to Patrick Maupin, who wrote this part!). And not on­ly that, but you can make it do some amaz­ing stuff with a lit­tle ef­fort.

To show that, let's cre­ate the most daz­zling sec­tion head­ings known to man ( Let's see you do what this ba­by can do in La­TeX ;-).

First: de­fine the prob­lem.

The ti­tles rst2pdf can pro­duce are bor­ing. If you pull ev­ery lever and push ev­ery but­ton, you may end with the ti­tle tex­t, in a Com­ic San­s, right-aligned, in pink let­ter­ing, with av­o­cado-­green back­ground and a red bor­der.

And that's as far as the cus­tomiza­tion ca­pa­bil­i­ties go us­ing stylesheet­s. That's usu­al­ly enough, be­cause rst2pdf is not meant for brochures or some­thing like that (but I have done it).

The re­al prob­lem is that when you get all graph­ic-de­sign­er on rst2pdf, you lose doc­u­ment struc­ture, be­cause you are not be­ing se­man­tic.

Sec­ond: de­fine the goal.

So, imag­ine you want to make a head­ing that looks like this:

fancytitles1

The im­age is tak­en from the li­brary of con­gress with some light (and bad) gimp­ing by me to leave that emp­ty space at the left, and the ti­tle was added us­ing Inkscape.

Can you do that with rst2pdf? Hell no you can't. Not with­out cod­ing. So let's code an ex­ten­sion that lets you cre­ate any head­ing you like with­in the lim­its of Inkscape!

First, we cre­ate a SVG tem­plate for the head­ings (it's a bit big be­cause it has the bit­map em­bed­ded).

Three: the im­age-­head­ing flow­able

Suppose you have an image of the heading just like the one above. How would you draw that in a PDF? In reportlab, you do that using flowables which are elements that compose the story that is your document. These flowables are arranged in pages, and that's your PDF.

If you are do­ing a head­ing, there's a bit more, in that you need to add a book­mark, so it ap­pears on the PDF ta­ble of con­tents.

So, here's a flowable that does just that. It's cobbled from pieces inside rst2pdf, and is basically an unholy mix of Heading and MyImage:

class FancyHeading(MyImage):
  '''This is a cross between the Heading flowable, that adds outline
  entries so you have a PDF TOC, and MyImage, that draws images'''

  def __init__(self, *args, **kwargs):
      # The inicialization is taken from rst2pdf.flowables.Heading
      self.stext = kwargs.pop('text')
      # Cleanup title text
      self.stext = re.sub(r'<[^>]*?>', '', unescape(self.stext))
      self.stext = self.stext.strip()

      # Stuff needed for the outline entry
      self.snum = kwargs.pop('snum')
      self.level = kwargs.pop('level')
      self.parent_id= kwargs.pop('parent_id')


      MyImage.__init__(self, *args, **kwargs)

  def drawOn(self,canv,x,y,_sW):

      ## These two lines are magic.
      #if isinstance(self.parent_id, tuple):
          #self.parent_id=self.parent_id[0]

      # Add outline entry. This is copied from rst2pdf.flowables.heading
      canv.bookmarkHorizontal(self.parent_id,0,0+self.image.height)

      if canv.firstSect:
          canv.sectName = self.stext
          canv.firstSect=False
          if self.snum is not None:
              canv.sectNum = self.snum
          else:
              canv.sectNum = ""

      canv.addOutlineEntry(self.stext.encode('utf-8','replace'),
                                self.parent_id.encode('utf-8','replace'),
                                int(self.level), False)

      # And let MyImage do all the drawing
      MyImage.drawOn(self,canv,x,y,_sW)

And how do we tell rst2df to use that instead of a regular Heading? by overriding the TitleHandler class. Here's where the extension magic kicks in.

If you de­fine, in an ex­ten­sion, a class like this:

class FancyTitleHandler(genelements.HandleParagraph, docutils.nodes.title):

Then that class will handle all docutils nodes of class docutils.nodes.title. Here, I just took rst2pdf.genelements.HandleTitle and changed how it works for level-1 headings, making it generate a FancyHeading instead of a Heading... and that's all there is to it.

class FancyTitleHandler(genelements.HandleParagraph, docutils.nodes.title):
  '''
  This class will handle title nodes.

  It takes a "titletemplate.svg", replaces TITLEGOESHERE with
  the actual title text, and draws that using the FancyHeading flowable
  (see below).

  Since this class is defined in an extension, it
  effectively replaces rst2pdf.genelements.HandleTitle.
  '''

  def gather_elements(self, client, node, style):
      # This method is copied from the HandleTitle class
      # in rst2pdf.genelements.

      # Special cases: (Not sure this is right ;-)
      if isinstance(node.parent, docutils.nodes.document):
          #node.elements = [Paragraph(client.gen_pdftext(node),
                                      #client.styles['title'])]
          # The visible output is now done by the cover template
          node.elements = []
          client.doc_title = node.rawsource
          client.doc_title_clean = node.astext().strip()
      elif isinstance(node.parent, docutils.nodes.topic):
          node.elements = [Paragraph(client.gen_pdftext(node),
                                      client.styles['topic-title'])]
      elif isinstance(node.parent, docutils.nodes.Admonition):
          node.elements = [Paragraph(client.gen_pdftext(node),
                                      client.styles['admonition-title'])]
      elif isinstance(node.parent, docutils.nodes.table):
          node.elements = [Paragraph(client.gen_pdftext(node),
                                      client.styles['table-title'])]
      elif isinstance(node.parent, docutils.nodes.sidebar):
          node.elements = [Paragraph(client.gen_pdftext(node),
                                      client.styles['sidebar-title'])]
      else:
          # Section/Subsection/etc.
          text = client.gen_pdftext(node)
          fch = node.children[0]
          if isinstance(fch, docutils.nodes.generated) and \
              fch['classes'] == ['sectnum']:
              snum = fch.astext()
          else:
              snum = None
          key = node.get('refid')
          maxdepth=4
          if reportlab.Version > '2.1':
              maxdepth=6

          # The parent ID is the refid + an ID to make it unique for Sphinx
          parent_id=(node.parent.get('ids', [None]) or [None])[0]+u'-'+unicode(id(node))
          if client.depth > 1:
              node.elements = [ Heading(text,
                      client.styles['heading%d'%min(client.depth, maxdepth)],
                      level=client.depth-1,
                      parent_id=parent_id,
                      node=node,
                      )]
          else: # This is an important title, do our magic ;-)
              # Hack the title template SVG
              tfile = open('titletemplate.svg')
              tdata = tfile.read()
              tfile.close()
              tfile = tempfile.NamedTemporaryFile(dir='.', delete=False, suffix='.svg')
              tfname = tfile.name
              tfile.write(tdata.replace('TITLEGOESHERE', text))
              tfile.close()

              # Now tfname contains a SVG with the right title.
              # Make rst2pdf delete it later.
              client.to_unlink.append(tfname)

              e = FancyHeading(tfname, width=700, height=100,
                  client=client, snum=snum, parent_id=parent_id,
                  text=text, level=client.depth-1)

              node.elements = [e]

          if client.depth <= client.breaklevel:
              node.elements.insert(0, MyPageBreak(breakTo=client.breakside))
      return node.elements

The full ex­ten­sion is in SVN and you can try it this way:

[fancytitles]$ rst2pdf -e fancytitles -e inkscape demo.txt -b1

You need to en­able the Inkscape ex­ten­sion so the SVG will look nice. And here's the out­put:

fancytitles2

You can over­ride how any el­e­ment is han­dled. That's be­ing ex­ten­si­ble :-)


Contents © 2000-2020 Roberto Alsina