204 lines
8.4 KiB
TeX
204 lines
8.4 KiB
TeX
\NeedsTeXFormat{LaTeX2e}
|
|
\ProvidesPackage{packages/slides-partners}[2026/10/11 Automatic title-page partner logos]
|
|
\RequirePackage{graphicx}
|
|
\RequirePackage{expl3}
|
|
\RequirePackage{xparse}
|
|
|
|
\ExplSyntaxOn
|
|
\seq_new:N \g__slides_partners_seq
|
|
\prop_new:N \g__slides_partner_files_prop
|
|
\seq_new:N \l__slides_partner_files_seq
|
|
\seq_new:N \l__slides_partner_ratios_seq
|
|
\box_new:N \l__slides_partner_box
|
|
\tl_new:N \l__slides_partner_file_tl
|
|
\tl_new:N \l__slides_partner_resolved_tl
|
|
\int_new:N \l__slides_partner_count_int
|
|
\int_new:N \l__slides_partner_columns_int
|
|
\int_new:N \l__slides_partner_rows_int
|
|
\fp_new:N \l__slides_partner_minratio_fp
|
|
\fp_new:N \l__slides_partner_maxratio_fp
|
|
\fp_new:N \l__slides_partner_candidate_fp
|
|
\fp_new:N \l__slides_partner_scale_fp
|
|
\dim_new:N \l__slides_partner_width_dim
|
|
\dim_new:N \l__slides_partner_maxheight_dim
|
|
\dim_new:N \l__slides_partner_xgap_dim
|
|
\dim_new:N \l__slides_partner_ygap_dim
|
|
\dim_new:N \l__slides_partner_cellwidth_dim
|
|
\dim_new:N \l__slides_partner_cellheight_dim
|
|
|
|
% Selection is ordered and idempotent. File lookup is delayed until rendering,
|
|
% so a declaration can appear before or after its corresponding selection.
|
|
\NewDocumentCommand{\partner}{m}
|
|
{
|
|
\seq_if_in:NnF \g__slides_partners_seq {#1}
|
|
{ \seq_gput_right:Nn \g__slides_partners_seq {#1} }
|
|
}
|
|
\NewDocumentCommand{\declarepartner}{mm}
|
|
{ \prop_gput:Nnn \g__slides_partner_files_prop {#1} {#2} }
|
|
|
|
\cs_new_protected:Npn \__slides_partners_collect:
|
|
{
|
|
\seq_clear:N \l__slides_partner_files_seq
|
|
\seq_clear:N \l__slides_partner_ratios_seq
|
|
\seq_map_inline:Nn \g__slides_partners_seq
|
|
{
|
|
\prop_get:NnNF \g__slides_partner_files_prop {##1}
|
|
\l__slides_partner_file_tl
|
|
{ \tl_set:Nn \l__slides_partner_file_tl {packages/partners/##1} }
|
|
\tl_clear:N \l__slides_partner_resolved_tl
|
|
\file_if_exist:VTF \l__slides_partner_file_tl
|
|
{ \tl_set_eq:NN \l__slides_partner_resolved_tl \l__slides_partner_file_tl }
|
|
{
|
|
\clist_map_inline:nn {png,pdf,jpg,jpeg}
|
|
{
|
|
\file_if_exist:nT {\l__slides_partner_file_tl .####1}
|
|
{
|
|
\tl_set:Nx \l__slides_partner_resolved_tl
|
|
{\l__slides_partner_file_tl .####1}
|
|
\clist_map_break:
|
|
}
|
|
}
|
|
}
|
|
\tl_if_empty:NTF \l__slides_partner_resolved_tl
|
|
{
|
|
\PackageWarning{slides-partners}
|
|
{Logo~for~partner~`##1'~not~found~at~
|
|
`\tl_use:N \l__slides_partner_file_tl'.~Skipping~it}
|
|
}
|
|
{
|
|
\seq_put_right:NV \l__slides_partner_files_seq
|
|
\l__slides_partner_resolved_tl
|
|
\hbox_set:Nn \l__slides_partner_box
|
|
{ \includegraphics{\tl_use:N \l__slides_partner_resolved_tl} }
|
|
\seq_put_right:Nx \l__slides_partner_ratios_seq
|
|
{
|
|
\fp_eval:n
|
|
{ \dim_to_fp:n {\box_wd:N \l__slides_partner_box} /
|
|
\dim_to_fp:n {\box_ht:N \l__slides_partner_box +
|
|
\box_dp:N \l__slides_partner_box} }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
% Equal-area logos in equal-width cells give seals and wide wordmarks comparable
|
|
% visual weight. Try every column count, maximizing the common area scale while
|
|
% fitting the tallest and widest logos into the available title-page rectangle.
|
|
\cs_new_protected:Npn \__slides_partners_layout:
|
|
{
|
|
\int_set:Nn \l__slides_partner_count_int
|
|
{ \seq_count:N \l__slides_partner_files_seq }
|
|
% Keep a margin on the left so a full grid stays clear of the affiliations.
|
|
\dim_set:Nn \l__slides_partner_width_dim {.82\linewidth}
|
|
\dim_set:Nn \l__slides_partner_maxheight_dim {.24\paperheight}
|
|
\dim_set:Nn \l__slides_partner_xgap_dim
|
|
{ \fp_to_dim:n {min(.04 * \dim_to_fp:n {\linewidth},
|
|
.25 * \dim_to_fp:n {\linewidth} / max(1,\l__slides_partner_count_int-1))} }
|
|
\dim_set:Nn \l__slides_partner_ygap_dim
|
|
{ \fp_to_dim:n {min(.015 * \dim_to_fp:n {\paperheight},
|
|
.25 * \dim_to_fp:n {\l__slides_partner_maxheight_dim} /
|
|
max(1,\l__slides_partner_count_int-1))} }
|
|
\fp_set:Nn \l__slides_partner_minratio_fp {inf}
|
|
\fp_zero:N \l__slides_partner_maxratio_fp
|
|
\seq_map_inline:Nn \l__slides_partner_ratios_seq
|
|
{
|
|
\fp_set:Nn \l__slides_partner_minratio_fp
|
|
{min(\l__slides_partner_minratio_fp,##1)}
|
|
\fp_set:Nn \l__slides_partner_maxratio_fp
|
|
{max(\l__slides_partner_maxratio_fp,##1)}
|
|
}
|
|
\fp_zero:N \l__slides_partner_scale_fp
|
|
\int_set:Nn \l__slides_partner_columns_int {1}
|
|
\int_step_inline:nnnn {\l__slides_partner_count_int} {-1} {1}
|
|
{
|
|
\int_set:Nn \l__slides_partner_rows_int
|
|
{ \fp_eval:n {ceil(\l__slides_partner_count_int / ##1)} }
|
|
\fp_set:Nn \l__slides_partner_candidate_fp
|
|
{ min(.14 * \dim_to_fp:n {\paperheight},
|
|
(\dim_to_fp:n {\l__slides_partner_maxheight_dim} -
|
|
(\l__slides_partner_rows_int-1) *
|
|
\dim_to_fp:n {\l__slides_partner_ygap_dim}) /
|
|
\l__slides_partner_rows_int * sqrt(\l__slides_partner_minratio_fp),
|
|
(\dim_to_fp:n {\l__slides_partner_width_dim} -
|
|
(##1-1) * \dim_to_fp:n {\l__slides_partner_xgap_dim}) /
|
|
##1 / sqrt(\l__slides_partner_maxratio_fp)) }
|
|
% Break equal-size ties by minimizing unused cells in the final row.
|
|
\fp_compare:nT
|
|
{ \l__slides_partner_candidate_fp > \l__slides_partner_scale_fp + .0001 ||
|
|
(abs(\l__slides_partner_candidate_fp - \l__slides_partner_scale_fp) <= .0001 &&
|
|
##1 * \l__slides_partner_rows_int <
|
|
\l__slides_partner_columns_int *
|
|
ceil(\l__slides_partner_count_int / \l__slides_partner_columns_int)) }
|
|
{
|
|
\fp_set_eq:NN \l__slides_partner_scale_fp \l__slides_partner_candidate_fp
|
|
\int_set:Nn \l__slides_partner_columns_int {##1}
|
|
}
|
|
}
|
|
% Size cells to the widest rendered logo, not the entire available column.
|
|
% The enclosing row still right-aligns the compact grid in the title footer.
|
|
\dim_set:Nn \l__slides_partner_cellwidth_dim
|
|
{ \fp_to_dim:n {\l__slides_partner_scale_fp * sqrt(\l__slides_partner_maxratio_fp)} }
|
|
\dim_set:Nn \l__slides_partner_cellheight_dim
|
|
{ \fp_to_dim:n {\l__slides_partner_scale_fp / sqrt(\l__slides_partner_minratio_fp)} }
|
|
}
|
|
|
|
\cs_new_protected:Npn \__slides_partners_typeset:
|
|
{
|
|
\vbox
|
|
{
|
|
\offinterlineskip
|
|
\int_step_inline:nn {\l__slides_partner_count_int}
|
|
{
|
|
\int_compare:nNnT
|
|
{\int_mod:nn {##1-1} {\l__slides_partner_columns_int}} = {0}
|
|
{
|
|
\int_compare:nNnT {##1} > {1}
|
|
{ \vskip \l__slides_partner_ygap_dim }
|
|
\hbox_to_wd:nn {\linewidth}
|
|
{
|
|
\hfil
|
|
\int_step_inline:nnn {##1}
|
|
{\int_min:nn {##1+\l__slides_partner_columns_int-1}
|
|
{\l__slides_partner_count_int}}
|
|
{
|
|
\int_compare:nNnT {####1} > {##1}
|
|
{ \hskip \l__slides_partner_xgap_dim }
|
|
\vbox_to_ht:nn {\l__slides_partner_cellheight_dim}
|
|
{
|
|
\vfil
|
|
\hbox_to_wd:nn {\l__slides_partner_cellwidth_dim}
|
|
{
|
|
\hfil
|
|
\includegraphics[height=\fp_to_dim:n
|
|
{\l__slides_partner_scale_fp / sqrt(
|
|
\seq_item:Nn \l__slides_partner_ratios_seq {####1})}]
|
|
{\seq_item:Nn \l__slides_partner_files_seq {####1}}
|
|
\hfil
|
|
}
|
|
\vfil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
\NewDocumentCommand{\insertpartnerlogos}{}
|
|
{
|
|
\group_begin:
|
|
\seq_if_empty:NTF \g__slides_partners_seq
|
|
{ \inserttitlegraphic } % Backwards compatibility with existing slides.
|
|
{
|
|
\__slides_partners_collect:
|
|
\seq_if_empty:NF \l__slides_partner_files_seq
|
|
{
|
|
\__slides_partners_layout:
|
|
\__slides_partners_typeset:
|
|
}
|
|
}
|
|
\group_end:
|
|
}
|
|
\ExplSyntaxOff
|
|
\endinput
|