\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