Files
l3-ocr-slides/packages/slides-partners.sty
2026-11-09 11:32:54 +00:00

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