sile-typesetter / sile

The SILE Typesetter — Simon’s Improved Layout Engine
https://sile-typesetter.org
MIT License
1.63k stars 96 forks source link

Set binding side and default view for pdf #1314

Open mnjames opened 2 years ago

mnjames commented 2 years ago

Feature request

PDFs have the ability have a binding direction set, with the result that when the pdf is viewed on a computer in "book" view, the right and left pages are in the order you would see them in a book. In particular, for RTL languages the even page would be on the right and the odd page on the left.

Similarly, pdfs can be set to have a default view when they're opened. With choices (in Adobe Acrobat Pro) like the following image

Could these setting be set from within SILE, and the resultant pdf then behave in the desired manner?

Omikhleia commented 2 years ago

FWIW, according to the PDF specifications, the "direction" (entry /Direction set to L2R or R2L) may be set in the (optional) ViewerPreferences dict. The page layout (which more or less corresponds to the screenshot you provided) may be set in the PageLayout dict.

image

Interestingly, I tried to hack both, but my default viewer (not Adobe Acrobat Pro) seems to ignore them. Just mentioning. since anyway most PDF viewers seem to have the equivalent options in some menus.

mnjames commented 2 years ago

The viewer I use (SumatraPDF) does respect the choices. PageLayout is often/usually able to be set by viewers, but ViewerPreferences often can't. Hence in SumatraPDF and also in other viewers, I can manually set the view to look like two pages of a book. But there's no way for me to change the binding direction.

I know almost nothing about the PDF spec, but I did open the pdfs created by SILE in a text editor and can't find where I could manually edit these settings into the file. Does SILE create some sort of compressed pdf which doesn't have the pdf spec in human-readable form?

I'd be willing to create a script to add plain text into the pdf after it's been created rather than have it directly created by SILE. What I'm hoping to avoid is moving my pdf to my other machine and using Acrobat Pro to change those small settings.

alerque commented 2 years ago

This has actually crossed my mind before but I never got around to looking into it. I'm sure it's possible: we have this data in SILE (or at least could if we add meta data to the masters setup, assuming master names of 'left' and 'right' seems like a bad idea) and it can be encoded in the PDF, we just need to figure out exactly how and where.

Omikhleia commented 2 years ago

@alerque

we have this data in SILE (or at least could if we add meta data to the masters setup, assuming master names of 'left' and 'right' seems like a bad idea) and it can be encoded in the PDF, we just need to figure out exactly how and where.

As each frame may have its own masters and its own direction(s), this could be tricky...

What we may have, however, is a "global" direction=... option passed to the class when setting up the document (e.g. \begin[direction=TTB-LTR]{document}, end of §8.2 in the manual). Unless mistaken, it is handled here - i.e. currently just applied to the default frame set. We could possibly do more around those lines, but we have to remember that the backend is not always a PDF backend (e.g. libtexpdf), so it could be wrong actually trying to always do it there...

Another (nicer?) solution would perhaps be to add a command to the pdf package, in the same vein as the pdf:metadata, and let the user be in control of what he does there... I am not sure everyone would want their document to open in two page mode if honored by the PDF viewer, so it should likely be an author decision ? Otherwise, why not the full page mode, etc. too?

E.g. adding the ViewerPreferences and the PageLayout entries in the Catalog dictionary (if I read the PDF specifications correctly) would be along those lines (untested as not honored by my viewer):

SILE.outputters.libtexpdf._init()
local catalog = pdf.get_dictionary("Catalog")
local viewprefs = pdf.lookup_dictionary(catalog, "ViewerPreferences")
if not viewprefs then
 -- Create an empty ViewerPreferences dictionary if absent...
 pdf.add_dict(catalog, pdf.parse("/ViewerPreferences"), pdf.parse("<<>>"))
end
-- Update ViewerPreferences Direction
viewprefs = pdf.lookup_dictionary(catalog, "ViewerPreferences")
pdf.add_dict(viewprefs, pdf.parse("/Direction"), pdf.parse("/R2L")) -- or /L2R
-- Setting PageLayout
pdf.add_dict(catalog, pdf.parse("/PageLayout"), pdf.parse("/TwoColumnRight")) -- or /TwoColumnLeft, etc.

@mnjames

I know almost nothing about the PDF spec, but I did open the pdfs created by SILE in a text editor and can't find where I could manually edit these settings into the file. Does SILE create some sort of compressed pdf which doesn't have the pdf spec in human-readable form?

SILE generates a compressed PDF indeed (and editing a binary file in a text editor is certainly not recommended)...

To inspect a PDF structure, one could e.g. use exiftool -v some.pdf (pretty raw and verbose)... ... or better mutool (from muPDF)

For instance, inspecting the Root (Catalog) on a SILE-generated PDF:

$ mutool show test.pdf trailer/Root
1 0 obj
<<
  /Names 363 0 R
  /Outlines 364 0 R
  /Pages 435 0 R
  /Type /Catalog
>>
endobj

And on a document where I used my above piece of code... in a command derived from pdf:metadata (here.)

$ mutool show experimental.pdf trailer/Root
1 0 obj
<<
  /ViewerPreferences <<
    /Direction /R2L
  >>
  /PageLayout /TwoColumnRight
  /Pages 16 0 R
  /Type /Catalog
>>
endobj

For the record, on the PDF Reference 1.4 3rd edition (I had that at hand):

$ mutool show ~/Downloads/PDFReference.pdf trailer/Root
1 0 obj
<<
  /Type /Catalog
  /Pages 2 0 R
  /Metadata 3 0 R
  /Outlines 4 0 R
  /PageLabels 5 0 R
  /OpenAction 6 0 R
  /PageMode /UseOutlines
  /ViewerPreferences <<
    /DisplayDocTitle true
  >>
>>
endobj

So it seems one has all the tools to tweak and experiment these things ;)

mnjames commented 2 years ago

@Omikhleia, thank you very much. Being pointed to mutool and the other information you listed gave me all the information I needed to write script to set the desired direction. So my immediate need is lessened.

I still think it would be nice if this were built into SILE. While I agree that it might be nice if the binding direction were set automatically by the choice of main text direction, I also agree that we shouldn't assume a "default" setting like that for page layout.

My original thought, and I would still be happy with it, would be for the author to have to explicitly state both options (and for the default if it's not stated to be L2R and single page).

Omikhleia commented 2 years ago

For the record, LibreOffice's PDF export only has the following options for the page layout:

And according to its code, it sets the ViewerPreferences Direction to R2L when Continuous facing is selected and the first page is a left page (not sure why it doesn't do it always, regardless of the selected page layout).