Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How do I add a rear page to my pdf? #379

Open
techauthoruk opened this issue Oct 27, 2022 · 25 comments
Open

How do I add a rear page to my pdf? #379

techauthoruk opened this issue Oct 27, 2022 · 25 comments

Comments

@techauthoruk
Copy link

Like the front cover page. I need a rear cover that I can style in the same way, so adding some text, a background image, etc.

Is there a mechanism that allows this in rinoh? Also, is it possible to position more than one image - similar to the logo on the title page, but probably 2 or 3 images positioned near the bottom right hand corner?

I can't seem to find anything that gives me any pointers (apologies if I haven't looked in the right place!)

@techauthoruk
Copy link
Author

I see in the showcase there is a pdf (odpisy.pdf) where they have added a custom back page, so it's obviously possible. The question is of course, how is it done!

@brechtm can you enlighten me?

@brechtm
Copy link
Owner

brechtm commented Nov 1, 2022

Like the front cover page. I need a rear cover that I can style in the same way, so adding some text, a background image, etc. Is there a mechanism that allows this in rinoh?

This requires defining a custom template. This may be a bit difficult if you aren't familiar with writing Python, however. The Subclassing a Template section explains what you need to do without going into any details. Basically, you need to add a part template, page template and page classes. You can have a look at the Book template source code for hints.

Also, is it possible to position more than one image - similar to the logo on the title page, but probably 2 or 3 images positioned near the bottom right hand corner?

Yes, that is possible. But if these are static, it may be easier to include them in the background image.

This is again something that isn't documented yet. In short, you can define one or more containers and add images to it/them:

self.title = DownExpandingContainer('title', CONTENT, self,
self.left_margin, self.top_margin,
self.body_width)
if 'logo' in metadata:
self.title << HorizontalRule(style='title page rule')
self.title << Image(get_metadata('logo'),
style='title page logo',
limit_width=100*PERCENT)

Adding multiple images (or other elements) to a container will place them below each-other (how all elements are normally flowed to a page).

@brechtm
Copy link
Owner

brechtm commented Nov 4, 2022

Below are more detailed instructions for creating a document template that extends the Book template with a rear cover page. For production use, I would recommend building a document template that is independent of the Book (or Article) templates, so that changes to the latter will not affect your template.

Create, alongside conf.py, a file mytemplate.py:

from pathlib import Path

from rinoh import register_template
from rinoh.attribute import OverrideDefault
from rinoh.dimension import CM, PT
from rinoh.image import Image
from rinoh.layout import CONTENT, DownExpandingContainer, UpExpandingContainer
from rinoh.paragraph import Paragraph
from rinoh.template import (DocumentTemplate, DocumentPartTemplate,
                            PageBase, PageTemplateBase)
from rinoh.text import StyledText


class RearCoverPageTemplate(PageTemplateBase):
    def page(self, document_part, page_number, chain):
        return RearCoverPage(document_part, self, page_number)


REAR_COVER_PAGE_TEXT = r"""
'Some '(style 1) 'Text\n'(style 2)
'Some '(style 1) 'Text'(style 2)
""".replace('\n', '') # work around rinohtype bug


class RearCoverPage(PageBase):
    configuration_class = RearCoverPageTemplate

    def __init__(self, document_part, template, page_number):
        super().__init__(document_part, template, page_number)
        margin = 2*CM
        split = 20*CM
        width = self.width - 2 * margin
        self.text = UpExpandingContainer('text', CONTENT, self, left=margin,
                                         bottom=split, width=width)
        self.text << Paragraph(StyledText.from_string(REAR_COVER_PAGE_TEXT),
                               style='rear cover page text')
        self.imgs = DownExpandingContainer('images', CONTENT, self,
                                           left=margin, top=split, width=width)
        self.imgs << Image(Path(__file__).parent / 'rear_page_1.png',
                           width=2*CM, align='right')
        self.imgs << Image(Path(__file__).parent / 'rear_page_2.png',
                           width=2*CM, align='right')


class RearCoverPartTemplate(DocumentPartTemplate):
    drop_if_empty = OverrideDefault(False)

    def _flowables(self, document):
        return iter([])     # content is static, placed in RearCoverPage


class MyDocumentTemplate(Book):
    parts = OverrideDefault(['title', 'front_matter', 'contents', 'back_matter',
                             'rear_cover'])
    rear_cover = RearCoverPartTemplate()
    rear_cover_page = RearCoverPageTemplate(base='page')

register_template('my_template', MyDocumentTemplate)

Create mytemplate.rtt:

[TEMPLATE_CONFIGURATION]
name=my template configuration
template=my_template
stylesheet=mystylesheet.rts

[rear_cover_page]
background = 'rear_page_bg.png', scale=fill

In conf.py:

sys.path.insert(0, os.path.abspath('.'))

import mytemplate

...

rinoh_documents = [dict(
    ...
    template='mytemplate.rtt',
)]

rinohtype will look for the images (rear_page_1.png, rear_page_2.png and rear_page_bg.png) in the same directory as where conf.py sits.

I haven't tested this exact code (yet), but I did change a test case that checks whether relative paths in conf.py, template configuration and style sheet files work properly [1]: 4a044d5

[1] That explains the unnecessary scattering of files across directories

@techauthoruk
Copy link
Author

I created a new (small) project to test this, and I ran into some issues. I followed the instructions above exactly, and this is what I ran into

Configuration error:
There is a programmable error in your configuration file:

Traceback (most recent call last):
  File "/home/mark/.venv/lib/python3.10/site-packages/sphinx/config.py", line 350, in eval_config_file
    exec(code, namespace)
  File "/home/mark/SEaB/pdf-dev/source/conf.py", line 31, in <module>
    sys.path.insert(0, os.path.abspath('.'))
NameError: name 'sys' is not defined

make: *** [Makefile:20: rinoh] Error 2

so added import sys before the rinoh section in conf.py, and then got:

Configuration error:
There is a programmable error in your configuration file:

Traceback (most recent call last):
  File "/home/mark/.venv/lib/python3.10/site-packages/sphinx/config.py", line 350, in eval_config_file
    exec(code, namespace)
  File "/home/mark/SEaB/pdf-dev/source/conf.py", line 32, in <module>
    sys.path.insert(0, os.path.abspath('.'))
NameError: name 'os' is not defined

make: *** [Makefile:20: rinoh] Error 2

so added import os as well. I then got this error:

Configuration error:
There is a programmable error in your configuration file:

Traceback (most recent call last):
  File "/home/mark/.venv/lib/python3.10/site-packages/sphinx/config.py", line 350, in eval_config_file
    exec(code, namespace)
  File "/home/mark/SEaB/pdf-dev/source/conf.py", line 35, in <module>
    import mytemplate
  File "/home/mark/SEaB/pdf-dev/source/mytemplate.py", line 52, in <module>
    class MyDocumentTemplate(Book):
NameError: name 'Book' is not defined

make: *** [Makefile:20: rinoh] Error 2

I assume that this is a reference to the rinoh book template? I have no idea where to go next, so will wait for @brechtm to get back from vacation.

@techauthoruk
Copy link
Author

OK...one step forwards, three back!

Sorted the NameError by adding:

from rinoh.templates import Book

to mytemplate.py. The build starts, but then throws this error:

Exception occurred:
  File "/home/mark/.venv/lib/python3.10/site-packages/rinoh/template.py", line 777, in __init__
    raise ValueError("The '{}' document template has no part named"
ValueError: The 'MyDocumentTemplate' document template has no part named 'title'

No idea where to go next - @brechtm can you assist?

@brechtm
Copy link
Owner

brechtm commented Nov 25, 2022

Exception occurred:
  File "/home/mark/.venv/lib/python3.10/site-packages/rinoh/template.py", line 777, in __init__
    raise ValueError("The '{}' document template has no part named"
ValueError: The 'MyDocumentTemplate' document template has no part named 'title'

I'm afraid you hit another bug! 😊 This isn't triggered for a full custom template that doesn't inherit from Book/Article.
I'll look into it next week.

@brechtm
Copy link
Owner

brechtm commented Dec 3, 2022

That bug is fixed in 24973f6. Please try again with the current development version.

@techauthoruk
Copy link
Author

Thanks @brechtm - I will try this tomorrow and let you know the result

@techauthoruk
Copy link
Author

@brechtm - still throws the same no part named 'title' error. I downloaded the latest version of rinoh using:

pip install https://github.com/brechtm/rinohtype/archive/refs/heads/master.zip

which I assume is correct?. Here is the full error log if that helps:

# Sphinx version: 5.3.0
# Python version: 3.10.6 (CPython)
# Docutils version: 0.19 
# Jinja2 version: 3.1.2
# Last messages:
#   pickling environment...
#   done
#   checking consistency...
#   done
#   processing manual...
#   index
#   legal
#   hands
#   glossary
#   resolving references...
# Loaded extensions:
#   sphinx.ext.mathjax (5.3.0) from /home/mark/.venv/lib/python3.10/site-packages/sphinx/ext/mathjax.py
#   sphinxcontrib.applehelp (1.0.2) from /home/mark/.venv/lib/python3.10/site-packages/sphinxcontrib/applehelp/__init__.py
#   sphinxcontrib.devhelp (1.0.2) from /home/mark/.venv/lib/python3.10/site-packages/sphinxcontrib/devhelp/__init__.py
#   sphinxcontrib.htmlhelp (2.0.0) from /home/mark/.venv/lib/python3.10/site-packages/sphinxcontrib/htmlhelp/__init__.py
#   sphinxcontrib.serializinghtml (1.1.5) from /home/mark/.venv/lib/python3.10/site-packages/sphinxcontrib/serializinghtml/__init__.py
#   sphinxcontrib.qthelp (1.0.3) from /home/mark/.venv/lib/python3.10/site-packages/sphinxcontrib/qthelp/__init__.py
#   alabaster (0.7.12) from /home/mark/.venv/lib/python3.10/site-packages/alabaster/__init__.py
#   rinoh.frontend.sphinx (0.5.5) from /home/mark/.venv/lib/python3.10/site-packages/rinoh/frontend/sphinx/__init__.py
Traceback (most recent call last):
  File "/home/mark/.venv/lib/python3.10/site-packages/rinoh/template.py", line 774, in __init__
    self.part_templates = [next(self._find_templates(name))
  File "/home/mark/.venv/lib/python3.10/site-packages/rinoh/template.py", line 774, in <listcomp>
    self.part_templates = [next(self._find_templates(name))
  File "/home/mark/.venv/lib/python3.10/site-packages/rinoh/template.py", line 586, in get_entries
    yield from self.base.get_entries(name, document)
  File "/home/mark/.venv/lib/python3.10/site-packages/rinoh/template.py", line 587, in get_entries
    raise KeyError(name)
KeyError: 'title'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/mark/.venv/lib/python3.10/site-packages/sphinx/cmd/build.py", line 281, in build_main
    app.build(args.force_all, args.filenames)
  File "/home/mark/.venv/lib/python3.10/site-packages/sphinx/application.py", line 347, in build
    self.builder.build_update()
  File "/home/mark/.venv/lib/python3.10/site-packages/sphinx/builders/__init__.py", line 307, in build_update
    self.build(['__all__'], to_build)
  File "/home/mark/.venv/lib/python3.10/site-packages/sphinx/builders/__init__.py", line 376, in build
    self.write(docnames, list(updated_docnames), method)
  File "/home/mark/.venv/lib/python3.10/site-packages/rinoh/frontend/sphinx/__init__.py", line 257, in write
    self.write_document(entry)
  File "/home/mark/.venv/lib/python3.10/site-packages/rinoh/frontend/sphinx/__init__.py", line 263, in write_document
    rinoh_document = self.construct_rinohtype_document(data)
  File "/home/mark/.venv/lib/python3.10/site-packages/rinoh/frontend/sphinx/__init__.py", line 281, in construct_rinohtype_document
    rinoh_document = rinoh_template.document(rinoh_tree)
  File "/home/mark/.venv/lib/python3.10/site-packages/rinoh/template.py", line 611, in document
    return self.template(document_tree, configuration=self,
  File "/home/mark/.venv/lib/python3.10/site-packages/rinoh/template.py", line 777, in __init__
    raise ValueError("The '{}' document template has no part named"
ValueError: The 'MyDocumentTemplate' document template has no part named 'title'

@techauthoruk
Copy link
Author

Ah! It does work...I had some issues with my WSL2 Linux implementation, so had to reinstall and set up again, and I can now create with a rear page. Thank you @brechtm !

I will close this as complete

@brechtm
Copy link
Owner

brechtm commented Dec 7, 2022

I was going to look into this again today, so it's great to see that you've gotten things working!

BTW Don't bother with closing issues as I sometimes I want to keep them open. I'm reopening this one as a reminder to add documentation for this.

@brechtm brechtm reopened this Dec 7, 2022
@techauthoruk
Copy link
Author

OK, so I was a bit too keen there!

There is one issue that I can't see how to resolve now. The rear page is added (great!), but the page headers and footers aren't as they should be. When I create a pdf using the Book template, my header/footer look like this:

image
image

Using the revised code/config to add the rear page, the headers and footers look like this:

image
image

What do I need to change to get the new pdf with rear page to look like the pdf without rear page?

@Gregor14
Copy link

Gregor14 commented Dec 7, 2022

Hi, I look for some solution to append fixed pdf at begin of pdf made by Rinoh (but for now, without success). In this way I found this thread. If there is no easier way, I decide to do this "rear page project" and later just to adapt to "front page project" :)
I pass all way made by @techauthoruk and stop at his last problem "template has no part named 'title'". @techauthoruk solve this by some reinstallation WSL2, but I'm using pure Win10. Could you point me, where could be error? Or (if exist) recommend me how to add fixed pdf (cover page) at begin of pdf from Rinoh? Thanks.

@brechtm
Copy link
Owner

brechtm commented Dec 7, 2022

There is one issue that I can't see how to resolve now. The rear page is added (great!), but the page headers and footers aren't as they should be.
What do I need to change to get the new pdf with rear page to look like the pdf without rear page?

Again, this is difficult to say without knowing the contents of your files (the template configuration file in this case).

In your template configuration file, you set header_text = '\t\t{DOCUMENT_TITLE}', so I'm guessing "PDF Test" is simply the document title for the project you're testing the rear cover page with?

I'm not sure where "8/14" would come from, since the page footer is not configured like that in the Book template or the sample code I provided above. "8/14" seems to come from the default footer text defined by BodyPageTemplateBase. But why isn't this being overridden by the footer text defined for the Book template? 🤔 Could be another bug...

The strange positioning of "8/14" hints at a problem with the tab stops defined for the footer text. I think this is because you overrode the header_footer style definition:

[header_footer:Paragraph]
tab_stops=20% CENTER, 100% RIGHT

@brechtm
Copy link
Owner

brechtm commented Dec 7, 2022

@Gregor14 The fix for the ValueError exception is not available in a rinohtype release. You can install the current development release like this:

pip install https://github.com/brechtm/rinohtype/archive/refs/heads/master.zip

@Gregor14
Copy link

Gregor14 commented Dec 7, 2022

@brechtm It was my mistake, sorry. I did update to dev release, but to wrong Python release (I must keep 3.8 and latest). When I correct it, I was able to generate pdf with added picture at the end.
However, there are some issues (maybe it will be useful for you for future)

  1. Dissapear completly "Table of Contents"
  2. Two last part of pdf ("Python Module Index" and "Index") has new page enumeration. So, main document end at 21/21 and then start "Python Module Index" from 1/5.

@techauthoruk
Copy link
Author

In your template configuration file, you set header_text = '\t\t{DOCUMENT_TITLE}', so I'm guessing "PDF Test" is simply the document title for the project you're testing the rear cover page with?

Yes, PDF test is the name I'm using for the development. My mistake about the header - it's fine.

I'm not sure where "8/14" would come from, since the page footer is not configured like that in the Book template or the sample code I provided above. "8/14" seems to come from the default footer text defined by BodyPageTemplateBase. But why isn't this being overridden by the footer text defined for the Book template? 🤔 Could be another bug...

The strange positioning of "8/14" hints at a problem with the tab stops defined for the footer text. I think this is because you overrode the header_footer style definition:

I changed the tab settings because in the original Book template the subsection name is included in the odd-page footer, and some of them were wrapping:

image

...but this doesn't have any impact on the page number position when I generate from the Book template. In this new template, apart from the unusual page number formatting, the section name is missing from the even pages and the subsection name from the odd pages.

I'm using essentially the same template and stylesheet information with the new 'mytemplate' file as I did with my other instance (you have both of them in the zip file I shared with you for the index 'XX' bug - SEaB.rtt and SEaB.rts). So I'm somewhat bemused as to why this odd page number formatting is happening.

The other differences I note is that the index is in a single column (in the Book template it is two columns). Also, as @Gregor14 commented, the ToC is missing - I don't include any modules in my source, so cannot say whether there is an issue there.

Back to you @brechtm !

@techauthoruk
Copy link
Author

Ah, just noticed that the page numbering is reset in the index, so it restarts at 1 of x:

image

One other question @brechtm (last one, honest!). How do you change the size of the text on the rear cover? Is this done in the 'mytemplate.py` file:

REAR_COVER_PAGE_TEXT = r"""
'Some '(style 1) 'Text\n'(style 2)
'Some '(style 1) 'Text'(style 2)
""".replace('\n', '') # work around rinohtype bug

or can it be styled in the template configuration or stylesheet?

@techauthoruk
Copy link
Author

@brechtm - is there a resolution for this imminent? I have a document that needs the rear page, and ideally I would like to get it done before Christmas. If not its not a problem, as I will add a rear page manually in Acrobat once I have generated the pdf file.

@brechtm
Copy link
Owner

brechtm commented Dec 11, 2022

Sorry, I won't be able to do any rinohtype work next week. I may be able to the week of the 19th, but no promises!

@brechtm
Copy link
Owner

brechtm commented Jan 6, 2023

One other question @brechtm (last one, honest!). How do you change the size of the text on the rear cover? Is this done in the 'mytemplate.py` file:

REAR_COVER_PAGE_TEXT = r"""
'Some '(style 1) 'Text\n'(style 2)
'Some '(style 1) 'Text'(style 2)
""".replace('\n', '') # work around rinohtype bug

or can it be styled in the template configuration or stylesheet?

The (style 1) annotation sets the style for the text preceding it so that you can style it in the style sheet:

[text with style 1 : StyledText('style 1')]
font_size=14pt

[text with style 2 : StyledText('style 2')]
font_size=18pt

You'll want to use more descriptive style names than 'style 1' and 'style 2', of course.

@brechtm
Copy link
Owner

brechtm commented Jan 6, 2023

@techauthoruk It looks like we're running into several bugs here. I want to extend the customtemplate test case to reproduce all of the issues discussed here. Can you list the issues you see with the current test case files? I don't have time now, but I already noticed that the table of contents is indeed missing.

I don't know whether you are familiar with using git, but perhaps you could fork the rinohtype repository and create a pull request with changes to the test case files that trigger other issues?

@techauthoruk
Copy link
Author

@brechtm Apologies for taking so long to get back to you, but I have moved on from my previous role, so haven't been doing much Sphinx/rinoh stuff.

I would happily fork the repo, but I'm not sure how (nor how to create the pull request tied to the test case files, etc). Happy to do this if you can advise how!

@techauthoruk
Copy link
Author

@brechtm OK - I have forked the repo and created a pull request against the rear page branch (hope that's right). Do I now detail the issues in the pull request or do I need to do something else? All new to me...

@brechtm
Copy link
Owner

brechtm commented Jan 19, 2023

Nope, that's not quite what I meant. But I know git, forking and pull requests can be very confusing when you're new to it! 😄

Perhaps you're not familiar with git (and perhaps version control) in general? If so, and if you're often working with plain-text documentation (such as a Sphinx project), I would highly recommend it. It will help you track changes and keep a clear overview different versions (branches) that need to be maintained in parallel. Git has a notoriously complex command line interface, but a basic GUI like GitHub Desktop shields you from that. If you're interested, you can find plenty of introductory tutorials on git (you should probably look for one specifically using GitHub Desktop).

The idea behind the pull request is that you

  1. fork the rinohtype repository (as you have done)
  2. check it out on your computer (using GitHub Desktop)
  3. edit the customtemplate test case file (at tests_regression/sphinx/test-customtemplate in your checkout)
  4. commit (one or more commits) your changes locally (using GitHub Desktop)
  5. push these commits to GitHub (again using GitHub Desktop)
  6. create a pull request against my master branch (I think GitHub Desktop can do that as well, otherwise from the GitHub website)

Please don't feel obliged to go through this for my sake. Only if you're interested! 😃

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants