I don't know whether the following satisfies your needs. I don't remember the syntax and behaviour of pdfpages too well (I used it only once) and was too lazy to look it up.
The following defines the new macro \multpages. It takes an optional argument and a mandatory one. The mandatory one is the file from which the pages should be taken. The optional has the following options:
pages: a list of pages to include, it should be a comma-separated list which can contain ranges
nup: the arrangement of the images. It can either be a single number, which then sets the number of pages displayed next to each other horizontally, with a line break following those, or something matching the pattern AxB. In this case A images are displayed horizontally next to each other and B vertically. The images have the maximum width of hsize divided by the number of horizontally arranged images (padding respected), and the maximum height of vsize divided by the number of vertically arranged images (padding respected). However there is no box forced around the images, they might line or page break unexpectedly. The product of AxB doesn't have to be greater or equal to the number of included pages.
hpad: the padding in horizontal direction between the images. No padding is applied on the outermost sides.
vpad: the padding in vertical direction between the images. No padding is applied on the outermost sides.
hsize: the horizontally available space. If you don't specify this one the current \textwidth will be used.
vsize: the vertically available space. If you don't specify this one the current \textheight will be used. Has no effect if nup doesn't contain a vertical part.
Every unknown key is forwarded to the underlying \includegraphics commands.
Changes by edit:
hpad and vpad are now skips not dim, so they accept glue as argument.
pages with pattern -B are now ranges beginning by 1, A- are now ranges until the last page of the pdf to be included.
Changes by edit2:
hcor and vcor keys, allowing to make small adjustments to the calculation of height and width (introduced instead of the fixed value of .1pt, initially set to 0pt)
- less vertical space after the image array, resulting in the same caption placement as under a single
\includegraphics
- consistent vertical space with and without
\centering
- removed duplicate code and more modularized by defining more functions and reusing functions instead of code blocks
Changes by edit3:
hboxed key: is a choice between true meaning a horizontal box is put around each row of figures, fixed meaning the row is put into a box with the width fixed to the hsize, and false meaning it is not boxed
vboxed key: also a choice between true, fixed, and false, with ruffly the same meaning but the vertical box is put around the whole array
\noindent is forced prior to the array, you can suppress this using the nonoindent key
Changes by edit4:
pages with undelimited range on the right side now works with LuaTeX and XeTeX, too.
Known issues:
- the vertical height doesn't match the specified
vsize, it tends to be bigger for more than one row and to be smaller for one row (as far as I have encountered so far). As a result vboxed=fixed results in the lowest row overlapping the \vbox downwards. As of now, I don't know what causes this.
Known issues (if hboxed=false)
- for some combinations of options in a
figure environment the width seems to be a bit too wide, the figures are line broken prematurely
- line breaks can change depending on
\centering being present or not
I won't use \centering if I don't use a vertical nup argument and no hsize, as in this case the result always takes the full text width and there is no need for centring the output, but it could lead to unexpected line breaks.
Perhaps it would be better to box up single rows in a \hbox, but I don't know whether I will invest the time to change the output loop to test this.
Code:
\documentclass[]{article}
\usepackage{showframe}% for debugging
\usepackage{graphicx}
\usepackage{xparse}
\ExplSyntaxOn
\clist_new:N \g_DenisBitouze_pages_clist
\clist_new:N \l_DenisBitouze_tmp_clist
\dim_new:N \l_DenisBitouze_hsize_dim
\dim_new:N \l_DenisBitouze_vsize_dim
\skip_new:N \l_DenisBitouze_tmp_skip
\int_new:N \l_DenisBitouze_hnup_int
\int_new:N \l_DenisBitouze_vnup_int
\int_new:N \l_DenisBitouze_tmp_int
\tl_new:N \l_DenisBitouze_tmp_tl
\tl_new:N \l_DenisBitouze_graphicx_opt_tl
\cs_new_protected:Nn \DenisBitouze_not_hboxed:n { #1 }
\keys_define:nn { DenisBitouze }%>>>
{
,pages .tl_set:N = \l_DenisBitouze_pages_tl
,nup .tl_set:N = \l_DenisBitouze_nup_tl
,hpad .skip_set:N = \l_DenisBitouze_hpad_skip
,hpad .initial:n = 1em
,vpad .skip_set:N = \l_DenisBitouze_vpad_skip
,vpad .initial:n = 1em
,hsize .dim_set:N = \l_DenisBitouze_hsize_available_dim
,hsize .default:n = \textwidth
,vsize .dim_set:N = \l_DenisBitouze_vsize_available_dim
,vsize .default:n = \textheight
,hcorrection .dim_set:N = \l_DenisBitouze_hcor_dim
,hcorrection .initial:n = 0pt
,hcor .meta:n = { hcorrection = #1 }
,vcorrection .dim_set:N = \l_DenisBitouze_vcor_dim
,vcorrection .initial:n = 0pt
,vcor .meta:n = { vcorrection = #1 }
,vboxed .choice:
,vboxed / true .code:n =
\cs_set_protected:Nn \DenisBitouze_vbox:n { \vbox:n { ##1 } }
,vboxed / false .code:n =
\cs_set_protected:Nn \DenisBitouze_vbox:n { ##1 }
,vboxed / fixed .code:n =
\cs_set_protected:Nn \DenisBitouze_vbox:n
{ \vbox_to_ht:nn { \l_DenisBitouze_vsize_available_dim } { ##1 } }
,vboxed .default:n = true
,vboxed .initial:n = true
,hboxed .choice:
,hboxed / true .code:n =
\cs_set_eq:NN \DenisBitouze_hbox:n \hbox:n
,hboxed / false .code:n =
\cs_set_eq:NN \DenisBitouze_hbox:n \DenisBitouze_not_hboxed:n
,hboxed / fixed .code:n =
\cs_set_protected:Nn \DenisBitouze_hbox:n
{ \hbox_to_wd:nn { \l_DenisBitouze_hsize_available_dim } { ##1 } }
,hboxed .default:n = true
,hboxed .initial:n = true
,nonoindent .bool_set:N = \l_DenisBitouze_no_noindent_bool
,nonoindent .default:n = true
,unknown .code:n =
{
\tl_put_right:NV \l_DenisBitouze_graphicx_opt_tl \l_keys_key_tl
\tl_if_empty:nF { #1 }
{ \tl_put_right:Nn \l_DenisBitouze_graphicx_opt_tl { =#1 } }
\tl_put_right:Nn \l_DenisBitouze_graphicx_opt_tl { , }
}
}%<<<
\cs_new_protected:Nn \DenisBitouze_store_pages_count:n%>>>
{
\group_end:
\cs_set:Nn \DenisBitouze_pages_count: { #1 }
}%<<<
\cs_generate_variant:Nn \DenisBitouze_store_pages_count:n { x }
\cs_new_protected:Nn \DenisBitouze_define_count_pages:nn%>>>
{
\cs_set_protected:Nn \DenisBitouze_count_pages:n
{
\group_begin:
#1
\DenisBitouze_store_pages_count:x { #2 }
}
}%<<<
\sys_if_engine_pdftex:T%>>>
{
\DenisBitouze_define_count_pages:nn
{ \pdfximage { #1 } }
{ \the\pdflastximagepages }
}%<<<
\sys_if_engine_luatex:T%>>>
{
\DenisBitouze_define_count_pages:nn
{ \saveimageresource { #1 } }
{ \the\lastsavedimageresourcepages }
}%<<<
\sys_if_engine_xetex:T%>>>
{
\DenisBitouze_define_count_pages:nn
{}
{ \the\XeTeXpdfpagecount #1 \relax }
}%<<<
\cs_new:Nn \DenisBitouze_recall_parse:nnn%>>>
{
\DenisBitouze_parse_range:w #1 - #2 \q_stop { #3 }
}%<<<
\cs_generate_variant:Nn \DenisBitouze_recall_parse:nnn { nxn }
\cs_new_protected:Npn \DenisBitouze_parse_range:w #1-#2\q_stop#3%>>>
{
\tl_if_blank:nTF { #1 }
{
\DenisBitouze_parse_range:w 1-#2\q_stop { #3 }
}
{
\tl_if_blank:nTF { #2 }
{
\tl_if_in:nnTF { #3 } { .pdf }
{ \DenisBitouze_count_pages:n { #3 } }
{ \DenisBitouze_count_pages:n { #3.pdf } }
\DenisBitouze_recall_parse:nxn
{ #1 } { \DenisBitouze_pages_count: } { #3 }
}
{
\int_step_inline:nnn { #1 } { #2 }
{
\clist_gput_right:Nn \g_DenisBitouze_pages_clist { ##1 }
}
}
}
}%<<<
\cs_new_protected:Nn \DenisBitouze_parse_pages:n%>>>
{
\clist_gclear:N \g_DenisBitouze_pages_clist
\clist_set:NV \l_DenisBitouze_tmp_clist \l_DenisBitouze_pages_tl
\clist_map_inline:Nn \l_DenisBitouze_tmp_clist
{
\tl_if_in:nnTF { ##1 } { - }
{
\DenisBitouze_parse_range:w ##1 \q_stop { #1 }
}
{
\clist_gput_right:Nn \g_DenisBitouze_pages_clist { ##1 }
}
}
}%<<<
\cs_new_protected:Npn \DenisBitouze_parse_nup_x:w #1x#2\q_stop%>>>
{
\int_set:Nn \l_DenisBitouze_hnup_int { #1 }
\int_set:Nn \l_DenisBitouze_vnup_int { #2 }
}%<<<
\cs_new_protected:Nn \DenisBitouze_parse_nup:n%>>>
{
\tl_if_empty:nTF { #1 }
{
\int_set:Nn \l_DenisBitouze_hnup_int { \c_one }
\int_set:Nn \l_DenisBitouze_vnup_int { \c_zero }
}
{
\tl_if_in:nnTF { #1 } { x }
{
\DenisBitouze_parse_nup_x:w #1 \q_stop
}
{
\int_set:Nn \l_DenisBitouze_hnup_int { #1 }
\int_set:Nn \l_DenisBitouze_vnup_int { \c_zero }
}
}
}%<<<
\cs_generate_variant:Nn \DenisBitouze_parse_nup:n { V }
\cs_new_protected:Nn \DenisBitouze_output_single:nnn%>>>
{
\includegraphics [ #1, #2 ] { #3 }
}%<<<
\cs_generate_variant:Nn \DenisBitouze_output_single:nnn { nVn }
\cs_new_protected:Nn \DenisBitouze_output_single_no_vnup:nn%>>>
{
\DenisBitouze_output_single:nVn
{ width=\l_DenisBitouze_hsize_dim, page=#1 }
\l_DenisBitouze_graphicx_opt_tl
{ #2 }
}%<<<
\cs_new_protected:Nn \DenisBitouze_output_single_with_vnup:nn%>>>
{
\DenisBitouze_output_single:nVn
{
width=\l_DenisBitouze_hsize_dim,
height=\l_DenisBitouze_vsize_dim,
keepaspectratio=true,
page = #1
}
\l_DenisBitouze_graphicx_opt_tl
{ #2 }
}%<<<
\cs_generate_variant:Nn \DenisBitouze_output_single_no_vnup:nn { V }
\cs_generate_variant:Nn \DenisBitouze_output_single_with_vnup:nn { V }
\cs_new_protected:Nn \DenisBitouze_calc_hsize:%>>>
{
\dim_set:Nn \l_DenisBitouze_hsize_dim
{
(
\l_DenisBitouze_hsize_available_dim
+ \l_DenisBitouze_hpad_skip
)
/ \l_DenisBitouze_hnup_int
- \l_DenisBitouze_hpad_skip
% rounding could lead to too much horizontal space taken without
% this in a figure environment
- \l_DenisBitouze_hcor_dim
}
}%<<<
\cs_new_protected:Nn \DenisBitouze_calc_vsize:%>>>
{
\dim_set:Nn \l_DenisBitouze_vsize_dim
{
(
\l_DenisBitouze_vsize_available_dim
+ \l_DenisBitouze_vpad_skip
)
/ \l_DenisBitouze_vnup_int
- \l_DenisBitouze_vpad_skip
- \l_DenisBitouze_vcor_dim
}
}%<<<
\cs_new_protected:Nn \DenisBitouze_output_line:Nn%>>>
{
\int_step_inline:nn { \l_DenisBitouze_hnup_int }
{
\clist_if_empty:NF \g_DenisBitouze_pages_clist
{
\clist_gpop:NN \g_DenisBitouze_pages_clist \l_DenisBitouze_tmp_tl
#1 \l_DenisBitouze_tmp_tl { #2 }
\skip_horizontal:N \l_DenisBitouze_hpad_skip
}
}
\skip_horizontal:n { -\l_DenisBitouze_hpad_skip }
}%<<<
\cs_new_protected:Nn \DenisBitouze_output_loop:Nn%>>>
{
\bool_do_while:nn { !\clist_if_empty_p:N \g_DenisBitouze_pages_clist }
{
\bool_if:NF \l_DenisBitouze_no_noindent_bool { \noindent }
\mbox{}
\DenisBitouze_hbox:n { \DenisBitouze_output_line:Nn #1 { #2 } }
\mbox{}
\skip_vertical:N \l_DenisBitouze_vpad_skip
}
}%<<<
\cs_new_protected:Nn \DenisBitouze_after_loop:%>>>
{
\skip_vertical:n { - \l_DenisBitouze_vpad_skip }
}%<<<
\cs_new_protected:Nn \DenisBitouze_output_no_vnup:n%>>>
{
\DenisBitouze_output_loop:Nn
\DenisBitouze_output_single_no_vnup:Vn { #1 }
}%<<<
\cs_new_protected:Nn \DenisBitouze_output_with_vnup:n%>>>
{
\DenisBitouze_calc_vsize:
\DenisBitouze_output_loop:Nn
\DenisBitouze_output_single_with_vnup:Vn { #1 }
}%<<<
\NewDocumentCommand \multpages { O{} m }%>>>
{
\group_begin:
\keys_set:nn { DenisBitouze } { hsize, vsize, #1 }
\DenisBitouze_parse_pages:n { #2 }
\clist_if_empty:NT \g_DenisBitouze_pages_clist
{
\clist_gset:Nn \g_DenisBitouze_pages_clist { \c_one }
}
\DenisBitouze_parse_nup:V \l_DenisBitouze_nup_tl
\DenisBitouze_calc_hsize:
\DenisBitouze_vbox:n
{
\int_compare:nNnTF { \l_DenisBitouze_vnup_int } = { \c_zero }
{ \DenisBitouze_output_no_vnup:n { #2 } }
{ \DenisBitouze_output_with_vnup:n { #2 } }
\DenisBitouze_after_loop:
}
\group_end:
}%<<<
\ExplSyntaxOff
\begin{document}
\begin{figure}[t]% >>>
\centering
\multpages[pages=1-5,nup=3]{example-image-duck.pdf}
\caption
{%
A happy duck family%
\label{fig:ducks}%
}%
\end{figure}% <<<
\end{document}


\includepdfinside a float? To the float that thing will have no size as everything is added into the background of the page. – daleif Jun 18 '18 at 14:59\includepdfinside a float is, in the real use, we want to include multiple logical pages of an external document at once and this takes some vertical space (floats then welcome) plus a caption is needed. But you probably pointed out the trouble: "everything is added into the background of the page": strange and unfortunate for this use case. – Denis Bitouzé Jun 18 '18 at 15:09\includegraphicscalls plus thepage=Xoption. – daleif Jun 18 '18 at 15:09\includepdfis specifically made for including full page PDFs as full pages in the output (statement with modifications of course), it is not meant to be used inside floats. – daleif Jun 18 '18 at 15:10\includegraphicscannot include multiple pages at the same time. Okay, as said by daleif, we could use several\includegraphicscalls plus thepage=Xoption. That's less convenient thanpages={X-Y},nup=AxBfrompdfpagesbut, if there isn't any better solution, we will use this. – Denis Bitouzé Jun 18 '18 at 15:13pdfpagesdoesn't mention\includepdfis specifically made for including full page PDFs as full pages in the output nor it is not meant to be used inside floats. – Denis Bitouzé Jun 18 '18 at 15:18pdfpagesmanual, the description of thenupkey: "Puts multiple logical pages onto each sheet of paper." Also the first and second paragraphs of the introduction don't explicitly mention that it is only meant to include on single pages in the output PDF, but to me this is implicitly clear inside the text. – Skillmon Jun 19 '18 at 23:11