flynx / photobook

LaTeX class for making photo books.
BSD 3-Clause "New" or "Revised" License
22 stars 3 forks source link

Creating a multi-picture layout #3

Open francofusco opened 2 months ago

francofusco commented 2 months ago

Hello! Thanks for the package, it looks very interesting!

I would like to try creating travel photo-books with it and I would like to define some multi-image layouts to be used in selected pages. What I mean with it is that, for example, I would like a page to contain four images arranged in a grid, possibly with different sizes. I have started developing the simple command \Collage{outer-margin}{image-spacing}{image-1}{image-2}{image-3}{image-4}, which can produce something like this:

collage

The white rectangle represents the page produced by LaTeX, while the grey frame is just needed to see the borders of the page.

I managed to build the command, but I am a bit uncertain of the way I did it. I had to create a page and insert 4 cells using "absolute positioning". The width of each image should be (page_width - bleed - 2* outer_margin - image_spacing) / 2, while the the height (page_height - 2*bleed - 2* outer_margin - image_spacing) / 2. The origin of the top-left image should be (outer_margin, bleed+outer_margin), and for the others I just need to shift one or both origins by image_spacing+image_width or image_spacing+image_height.

My doubts are:

Here is a minimal (more or less) working example. To compile it, you just need to have an image named test in the same folder. I used Lenna's picture, it seemed appropriate!

\documentclass[%
    layoutmode=block,
    blockwidth=20cm,
    blockheight=20cm,
    imageblockwidth=1.0,
    imageblockheight=1.0,
    bleed=10mm,
    final
]{photobook}

% Remove page numbering
\usepackage{nopageno}

% A font I like
\usepackage{palatino}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% The following should eventually be moved into a package

% Dependencies
\usepackage{tikz}
\usepackage{pgfmath}
\usepackage[strict]{changepage}
\usepackage{ifdraft}

% Helper command to convert a value from points to the given unit.
\makeatletter
\def\convertto#1#2{\strip@pt\dimexpr #2*65536/\number\dimexpr 1#1}
\makeatother

% Print the given length, in cm.
\newcommand{\printcm}[1]{\convertto{cm}{\the#1}cm}

% Create a white box that shows the size (in cm) of the cell it box belongs to.
% This is meant for debugging purposes.
\newcommand{\cellsizebox}{\begin{tikzpicture}\node[draw,minimum width=\cellwidth,minimum height=\cellheight] at (0,0) {\shortstack{W: \printcm{\cellwidth}\\ H: \printcm{\cellheight}}};\end{tikzpicture}}

% Some lengths that I need inside the main macro.
\newlength{\CollageOffset}
\setlength{\CollageOffset}{\bleed}
\newlength{\CollageOuterMargin}
\newlength{\CollageImageSpacing}
\newlength{\CollageFirstWidth}
\newlength{\CollageSecondWidth}
\newlength{\CollageFirstHeight}
\newlength{\CollageSecondHeight}

% Main command: create a collage with four pictures of variable size.
% Usage:
%   \CollageTwo{outer_margin}{outer_margin}{width_ratio}{height_ratio}{img1}{img2}{img3}{img4}
% 
% Creates a collage with four pictures like this one:
%
%   =-----------------------------------+
%   = . . . . . . . . . . . . . . . . . |
%   =                                 . |
%   =                                 . |
%   =    +------+   +------------+    . |
%   =    | img1 |   |    img2    |    . |
%   =    +------+   +------------+    . |
%   =                                 . |
%   =    +------+   +------------+    . |
%   =    |      |   |            |    . |
%   =    | img3 |   |    img4    |    . |
%   =    |      |   |            |    . |
%   =    +------+   +------------+    . |
%   =                                 . |
%   =                                 . |
%   = . . . . . . . . . . . . . . . . . |
%   =-----------------------------------+
%
% In the image above, the outer frame represents the visible page in the
% produced pdf. The dotted frame is obtained by "shrinking" the page size by
% "\bleed" from all sides, except the gutter. The dotted frame is then shurk,
% this time in all four directions, by a margin equal to "outer_margin". The
% distance between all images is determined by outer_margin. Finally, the size
% of each image is calculated as follows. The remaining horizontal and vertical
% space for the images  are respectively:
%   W = page_width - bleed - 2*outer_margin - image_spacing
%   H = page_width - 2*bleed - 2*outer_margin - image_spacing
% The sizes of the images are then calculated as:
%   img1:     width_ratio*W ;     height_ratio*H
%   img2: (1-width_ratio)*W ; (1-height_ratio)*H
%   img3:     width_ratio*W ;     height_ratio*H
%   img4: (1-width_ratio)*W ; (1-height_ratio)*H
%
% TODO: allow to set the image fill-type (fit, fill, etc?)
% TODO: key-value pairs with defaults (at least for the ratios!)
% TODO: change name!
\newcommand{\CollageTwo}[8]{%
    \begin{page}%
        % Give a name to the first two parameters of the macro.
        \setlength{\CollageOuterMargin}{#1}%
        \setlength{\CollageImageSpacing}{#2}%
        %
        % Calculate the size of each image using pgfmath.
        \pgfmathparse{(\blockwidth - \bleed - 2\CollageOuterMargin - \CollageImageSpacing) * #3}%
        \setlength{\CollageFirstWidth}{\pgfmathresult pt}%
        \pgfmathparse{(\blockwidth - \bleed - 2\CollageOuterMargin - \CollageImageSpacing) * (1-#3)}%
        \setlength{\CollageSecondWidth}{\pgfmathresult pt}%
        \pgfmathparse{(\blockheight - 2\bleed - 2\CollageOuterMargin - \CollageImageSpacing) * (#4)}%
        \setlength{\CollageFirstHeight}{\pgfmathresult pt}%
        \pgfmathparse{(\blockheight - 2\bleed - 2\CollageOuterMargin - \CollageImageSpacing) * (1-#4)}%
        \setlength{\CollageSecondHeight}{\pgfmathresult pt}%
        %
        % Calculate the coordinates of the origins. Make sure to properly handle
        % even and odd pages.
        \checkoddpage%
        \def\collageX{\ifoddpage\else\bleed+\fi \CollageOffset+\CollageOuterMargin}%
        \def\collageY{\CollageOffset+\bleed+\CollageOuterMargin}%
        \def\collageXX{\collageX+\CollageFirstWidth+\CollageImageSpacing}%
        \def\collageYY{\collageY+\CollageFirstHeight+\CollageImageSpacing}%
        %
        % Place the images if in final mode, or a box with the content size in
        % cm, if in draft mode.
        \begin{cell}{\collageX,\collageY}{\CollageFirstWidth}{\CollageFirstHeight}%
            \ifdraft{\cellsizebox}{\imagecell[fill]{}{#5}}%
        \end{cell}%
        %
        \begin{cell}{\collageXX, \collageY}{\CollageSecondWidth}{\CollageFirstHeight}%
            \ifdraft{\cellsizebox}{\imagecell[fill]{}{#6}}%
        \end{cell}%
        %
        \begin{cell}{\collageX, \collageYY}{\CollageFirstWidth}{\CollageSecondHeight}%
            \ifdraft{\cellsizebox}{\imagecell[fill]{}{#7}}%
        \end{cell}%
        %
        \begin{cell}{\collageXX, \collageYY}{\CollageSecondWidth}{\CollageSecondHeight}%
            \ifdraft{\cellsizebox}{\imagecell[fill]{}{#8}}%
        \end{cell}%
    \end{page}%
}

% TODO: once the "CollageTwo" macro accepts the defaults for the ratio, remove this macro!
\newcommand{\Collage}[6]{\CollageTwo{#1}{#2}{0.5}{0.5}{#3}{#4}{#5}{#6}}

% Book information
\def\BookTitle{Foobar}
\def\BookAuthors{Franco Fusco}
\def\ThanksTo{Flynx}
\def\Edition{N/A}

\begin{document}

    % Author and copyright information.
    \BookInfoPage

    % "Square" aspect ratio
    \Collage{0cm}{0cm}{test}{test}{test}{test}
    \Collage{0cm}{0.5cm}{test}{test}{test}{test}
    \Collage{1cm}{0cm}{test}{test}{test}{test}
    \Collage{1cm}{0.5cm}{test}{test}{test}{test}

    % Non-square aspect ratio
    \CollageTwo{0cm}{0cm}{0.6}{0.6}{test}{test}{test}{test}
    \CollageTwo{0cm}{0.5cm}{0.6}{0.6}{test}{test}{test}{test}
    \CollageTwo{1cm}{0cm}{0.6}{0.6}{test}{test}{test}{test}
    \CollageTwo{1cm}{0.5cm}{0.6}{0.6}{test}{test}{test}{test}

    % Full bleed
    \CollageTwo{-\bleed}{0cm}{0.6}{0.6}{test}{test}{test}{test}
    \CollageTwo{-\bleed}{0.5cm}{0.6}{0.6}{test}{test}{test}{test}
\end{document}
flynx commented 2 months ago

Hi,

Seems that you have already figured out most of it.

I'll try and fill in the blanks:

Looking at how you organized your code, if you are aiming for a generic command, usable in different projects, I'd suggest a less absolute approach if possible, here's a basic example: https://github.com/flynx/photobook/blob/main/examples/two-image-page.tex

Also, do not forget that \bindingoffset is used instead of \bleed in the gutter so you'll have to also make your command page-position-aware if you are positioning things absolutely.

And in the general case you should also account for \gutteroffset to make things more compatible with photobook macros/commands/envs -- it is used to shift page content relative to the page gutter to account for different binding types.

IMHO, it would be far simpler to actually position your content relative to the containing cell, this way you can use any of the existing cells as containers to place your "element" in any way you want (including recursively), be it full bleed or with the same geometry as a text block, for example... and implement your page templates on top of that. This way photobook will take care of all the details like bleeds and page offsets so you can forget they ever existed (see the available container cells defined by photobook) ...I was planning on implementing a grid cell but was procrastinating, putting it off till the next book project, but I was thinking of a more generic approach, though that would be quite a bit more tedious than what you are doing =)

Cheers)

francofusco commented 2 months ago

Ok, I think that now I understand my... misunderstanding! Basically, the bleed is invisible in the pdf viewer, but it is there. The red rectangle in the picture I attached is actually the visible page already. And the margin I have added in the internal part should be "manually" offset by the gutter and binding offset as needed - since I am using "absolute positioning".

So to make it clear (at least to me) the snippet

\begin{page}
  \begin{cell}{0pt, 0pt}{\paperwidth}{\paperheight}
    \imagecell{}{img.jpg}
  \end{cell}
\end{page}

creates a "full page image" that is "good" for printing and cutting. If I used just \begin{cell}{\bleed, \bleed}{\blockwidth}{\blockheight} the pdf would have been ok but printing is troublesome - because I am not bleeding.

francofusco commented 2 months ago

That said, I still have a couple of questions. I'll post them in separate comments to make them more digestible.

First of all, you suggested to use inline cells. However, I am having issues. I tried to write the following:

\documentclass[%
  layoutmode=block,
  blockwidth=22cm,
  blockheight=20cm,
  imageblockwidth=1.0,
  imageblockheight=1.0,
  bindingoffset=1cm,
  gutteroffset=1cm,
  bleed=5mm,
  final
]{photobook}

\begin{page}
  \begin{pagecell}
    \begin{inlinecell}{10cm}{10cm}
       \imagecell{}{square}
    \end{inlinecell}
    \begin{inlinecell}{10cm}{10cm}
       \imagecell{}{square}
    \end{inlinecell}
    \begin{inlinecell}{10cm}{10cm}
       \imagecell{}{square}
    \end{inlinecell}
    \begin{inlinecell}{10cm}{10cm}
       \imagecell{}{square}
    \end{inlinecell}
  \end{inlinecell}
\end{page}

I would have expected to see four squares "nicely arranged" next to each other. However, this is the result:

squares

Do you have an idea why the four images are not stacked nicely?

francofusco commented 2 months ago

Second issue. I was trying to understand how to make my code cleaner, and I read in the manual that cell environments "register" their offset - allowing to "locate" them so to speak. For this, here you use \setlength\celloffsettop{\photobook@cell@offset[1]}. Shouldn't it be \celloffsetleft that is updated here? I assume that the offset in \photobook@cell@offset[1] is (x, y), maybe that's my mistake.

Also, I am trying to use the \celloffsetleft parameter to position cells relative to each other, but still using cell instead of inlinecell. I tried the following:

\begin{page}
    \begin{cell}{\bleed,\bleed}{\paperwidth}{\paperheight}
        \begin{cell}{\celloffsetleft+\bleed,\celloffsettop+\bleed}{10cm}{10cm}
        \imagecell{}{square}
            \begin{cell}{\celloffsetleft+\bleed,\celloffsettop+\bleed}{10cm}{10cm}
                \imagecell{}{square}
                \begin{cell}{\celloffsetleft+\bleed,\celloffsettop+\bleed}{10cm}{10cm}
                    \imagecell{}{square}
                \end{cell}
            \end{cell}
        \end{cell}
    \end{cell}
\end{page}

Which, to my understanding, should be able to repeat the picture while shifting it towards the bottom right corner. However, this is not the case. What happens is that the picture is shifted only once towards the right. Any idea why this may be happening?

francofusco commented 2 months ago

Finally, I encountered a problem while working with "real" images - instead of the test one, that was just 512x512. When using "large" images I encounter an Arithmetic overflow error. Here is an example to reproduce it with this picture:

\documentclass[%
  layoutmode=block,
  blockwidth=20cm,
  blockheight=20cm,
  imageblockwidth=1.0,
  imageblockheight=1.0,
  bleed=2mm,
  final
]{photobook}

\begin{document}

  % IMAGE CREDITS
  % Photo by Mohamed Almari from Pexels: https://www.pexels.com/photo/golden-gate-bridge-san-francisco-1591382/

  \begin{page}
    \begin{cell}{2cm, 1cm}{10cm}{15cm}
      \imagecell[fill]{}{image.jpg}
    \end{cell}
  \end{page}

\end{document}
flynx commented 2 months ago

@francofusco

\documentclass[%
  layoutmode=block,
  blockwidth=22cm,
  blockheight=20cm,
  imageblockwidth=1.0,
  imageblockheight=1.0,
  bindingoffset=1cm,
  gutteroffset=1cm,
  bleed=5mm,
  final
]{photobook}

\begin{page}
  \begin{pagecell}
    \begin{inlinecell}{10cm}{10cm}
       \imagecell{}{square}
    \end{inlinecell}
    \begin{inlinecell}{10cm}{10cm}
       \imagecell{}{square}
    \end{inlinecell}
    \begin{inlinecell}{10cm}{10cm}
       \imagecell{}{square}
    \end{inlinecell}
    \begin{inlinecell}{10cm}{10cm}
       \imagecell{}{square}
    \end{inlinecell}
  \end{inlinecell}
\end{page}

Don't know how you managed to build this without a document but...

Here's a working version ;)

\documentclass[%
  layoutmode=block,
  blockwidth=22cm,
  blockheight=20cm,
  imageblockwidth=1.0,
  imageblockheight=1.0,
  bindingoffset=1cm,
  gutteroffset=1cm,
  bleed=5mm,
  final
]{photobook}

\begin{document}%
%
\begin{page}%
  \begin{pagecell}%
    \begin{inlinecell}{10cm}{10cm}%
       \imagecell{}{square}%
    \end{inlinecell}%
    \begin{inlinecell}{10cm}{10cm}%
       \imagecell{}{square}%
    \end{inlinecell}%
    \newline%
    \begin{inlinecell}{10cm}{10cm}%
       \imagecell{}{square}%
    \end{inlinecell}%
    \begin{inlinecell}{10cm}{10cm}%
       \imagecell{}{square}%
    \end{inlinecell}%
  \end{pagecell}%
\end{page}%
%
\end{document}
flynx commented 2 months ago

@francofusco

Ok, I think that now I understand my... misunderstanding! Basically, the bleed is invisible in the pdf viewer, but it is there. The red rectangle in the picture I attached is actually the visible page already. And the margin I have added in the internal part should be "manually" offset by the gutter and binding offset as needed - since I am using "absolute positioning".

So to make it clear (at least to me) the snippet

\begin{page}
  \begin{cell}{0pt, 0pt}{\paperwidth}{\paperheight}
    \imagecell{}{img.jpg}
  \end{cell}
\end{page}

creates a "full page image" that is "good" for printing and cutting. If I used just \begin{cell}{\bleed, \bleed}{\blockwidth}{\blockheight} the pdf would have been ok but printing is troublesome - because I am not bleeding.

Yep =)

flynx commented 2 months ago

@francofusco

\begin{page}
    \begin{cell}{\bleed,\bleed}{\paperwidth}{\paperheight}
        \begin{cell}{\celloffsetleft+\bleed,\celloffsettop+\bleed}{10cm}{10cm}
        \imagecell{}{square}
            \begin{cell}{\celloffsetleft+\bleed,\celloffsettop+\bleed}{10cm}{10cm}
                \imagecell{}{square}
                \begin{cell}{\celloffsetleft+\bleed,\celloffsettop+\bleed}{10cm}{10cm}
                    \imagecell{}{square}
                \end{cell}
            \end{cell}
        \end{cell}
    \end{cell}
\end{page}

This I'll have to investigate a bit deeper, might be a bug... (I've not touched the code here for some time)

flynx commented 2 months ago

@francofusco

Finally, I encountered a problem while working with "real" images - instead of the test one, that was just 512x512. When using "large" images I encounter an Arithmetic overflow error. Here is an example to reproduce it with this picture:

The link you included returns an empty JSON object, at least for me...

How large was the image? (pixel size, dpi, color depth)

francofusco commented 2 months ago

Don't know how you managed to build this without a document but...

Because I'm the god of LaTeX of course! 😃 Jokes aside, I think I messed something while re-formatting it here in the issue, since the original script that contains the page is a bit longer. I confirm that the snippet as you posted it works as intended, so it must be my tinkering that has messed up something. I'll try to understand the root of the issue and report back if needed!

francofusco commented 2 months ago

@francofusco

Finally, I encountered a problem while working with "real" images - instead of the test one, that was just 512x512. When using "large" images I encounter an Arithmetic overflow error. Here is an example to reproduce it with this picture:

The link you included returns an empty JSON object, at least for me...

How large was the image? (pixel size, dpi, color depth)

Ah that's weird. For me it redirects to a page where you can see the image below:

IMP009

The resolution is 5304x6630‬. I just tested it to make sure the issue is not (again) caused by my tinkering - and indeed, I think I'm innocent this time!

\documentclass[%
  layoutmode=block,
  blockwidth=20cm,
  blockheight=20cm,
  imageblockwidth=1.0,
  imageblockheight=1.0,
  bleed=2mm,
  final
]{photobook}

\begin{document}
    \ImagePageClear{}{images/goldenbridge} % This is ok!
    \ImagePageFill{}{images/goldenbridge} % This fails :(
\end{document}
flynx commented 2 months ago

Finally, I encountered a problem while working with "real" images - instead of the test one, that was just 512x512. When using "large" images I encounter an Arithmetic overflow error. Here is an example to reproduce it with this picture:

OK, seems that I can reproduce this with image of height over 3989px and/or width over 4999px.... really odd....

UPDATE: do not see anything obvious, will have to dig in deeper)

francofusco commented 2 months ago

I don't want to say something stupid, but if I remember correctly, LaTeX uses (I want to say) 16 bits for numbers, which is not a lot (2^16 is about 65k). Maybe in some calculations the limit is reached, that would make sense given the type of error.

flynx commented 2 months ago

2^16

Yes, an overflow somewhere is the primary hypothesis here, but what's confusing (or rather it's fun) is both the different thresholds for height and width and the distance between 216 and these values, especially considering the math involved =)

UPDATE: found the culprit \ratio{3395.67801pt}{5018.73784pt} though not yet sure why it's breaking, need to get some sleep first =)

UPDATE: Published a workaround: 1508055cf3170cec299d3fb210829b0c0561eab8 (v0.1.31) -- see Issues section in docs.