%% %% This is file `bookshelf.cls', %% generated with the docstrip utility. %% %% The original source files were: %% %% bookshelf.dtx (with options: `class') %% %% This is a generated file. %% %% Copyright © 2020-2024 by latex@silmaril.ie %% %% This file was generated from an XML master source. %% Amendments and corrections should be notified to the %% maintainer for inclusion in future versions. %% \NeedsTeXFormat{LaTeX2e}[2017/04/15] \ProvidesClass{bookshelf}[2024/01/04 v0.8 Package code for the bookshelf class] %% %% Implementation %% %% \RequirePackage{fix-cm} \PassOptionsToPackage{svgnames}{xcolor} %% %% ****************************************************************** %% %% Options %% %% There are no package options. %% %% The paper size and orientation are the only two valid options, both of which are the same as the standard documentclass options, and will be passed to the underlying class automatically, but they need recording so that they can be used by the _geometry_ package. The default is for A0 paper, landscape. \def\SIL@paper{a0paper}% \DeclareOption{a0paper}{% \def\SIL@paper{a0paper}% \setlength\paperheight {1189mm}% \setlength\paperwidth {841mm}} \DeclareOption{a1paper}{% \def\SIL@paper{a1paper}% \setlength\paperheight {841mm}% \setlength\paperwidth {594mm}} \DeclareOption{a2paper}{% \def\SIL@paper{a2paper}% \setlength\paperheight {594mm}% \setlength\paperwidth {420mm}} \DeclareOption{a3paper}{% \def\SIL@paper{a3paper}% \setlength\paperheight {420mm}% \setlength\paperwidth {297mm}} \DeclareOption{a4paper}{% \def\SIL@paper{a4paper}% \setlength\paperheight {297mm}% \setlength\paperwidth {210mm}} \DeclareOption{a5paper}{% \def\SIL@paper{a5paper}% \setlength\paperheight {210mm}% \setlength\paperwidth {148mm}} \DeclareOption{b5paper}{% \def\SIL@paper{b5paper}% \setlength\paperheight {250mm}% \setlength\paperwidth {176mm}} \DeclareOption{letterpaper}{% \def\SIL@paper{letterpaper}% \setlength\paperheight {11in}% \setlength\paperwidth {8.5in}} \DeclareOption{legalpaper}{% \def\SIL@paper{legalpaper}% \setlength\paperheight {14in}% \setlength\paperwidth {8.5in}} \DeclareOption{executivepaper}{% \def\SIL@paper{executivepaper}% \setlength\paperheight {10.5in}% \setlength\paperwidth {7.25in}} \DeclareOption{ledgerpaper}{% \def\SIL@paper{ledgerpaper}% \setlength\paperheight {17in}% \setlength\paperwidth {11in}} \DeclareOption{tabloidpaper}{% \def\SIL@paper{tabloidpaper}% \setlength\paperheight {17in}% \setlength\paperwidth {11in}} \def\SIL@orient{landscape}% \DeclareOption{landscape}{% \def\SIL@orient{landscape}% \setlength\@tempdima {\paperheight}% \setlength\paperheight {\paperwidth}% \setlength\paperwidth {\@tempdima}} \DeclareOption{portrait}{% \def\SIL@orient{}} %% Now invoke the options. \ExecuteOptions{} \ProcessOptions\relax %% %% ****************************************************************** %% %% Load the document base class %% %% This class is based on the standard LaTeX _report_ class, with no special options except the extra sizes defined above. The default is A0 paper, landscape. \DeclareOption*{\ClassWarning{bookshelf}{% Unknown option `\CurrentOption', please RTFM}} \ProcessOptions\relax \LoadClass{report} %% %% Packages required for the class %% %% Sets the Google NoTo typeface as the default. \RequirePackage{noto}% %% Provides the \MF{} and \MP{} logos. \RequirePackage{mflogo}% %% Provide for running headers and footers. \RequirePackage{fancyhdr}% %% Creates paragraphs separated by white-space with no indentation. \RequirePackage{parskip}% %% Font specification setup for use with \XeLaTeX{}. \RequirePackage{fontspec}% %% Implements simple mathematics in counters and dimensions. \RequirePackage{calc}% %% Used for fixed-point calculations \RequirePackage{fp}% %% Provide for graphics (PNG, JPG, or PDF format (only) for pdflatex; EPS format (only) for standard \LaTeX{}); and for reflection and rotation features. \RequirePackage{graphicx}% %% Provides a more sophisticated casing function than the default. \RequirePackage{textcase}% %% Provide color. \RequirePackage{xcolor}% \@ifundefined{T}{% \newcommand{\T}[2]{{\fontencoding{T1}% \selectfont#2}}}{} %% Add picture commands (or backgrounds) to every page. \RequirePackage{eso-pic}% %% Package for establishing margins and text area. \RequirePackage[\SIL@paper,\SIL@orient,nohead, nofoot,margin=1cm]{geometry}% %% Use biblatex instead of \BibTeX{} \RequirePackage[backend=biber,style=authoryear]{biblatex}% \AtBeginDocument{% \setlength{\bibitemsep}{1ex}% \setlength{\bibnamesep}{1.5\itemsep}% \defbibheading{shortbib}[References]% {\section{#1}}} \@ifpackagewith{babel}{british}{% \DeclareLanguageMapping{british}% {british-apa}}{\relax} \providetoggle{blx@skipbiblist} %% %% ****************************************************************** %% %% Non-package resources %% %% There is one resource not available in packaged form, the module that lets LaTeX create random values. This is in `random.tex`, which on the author's system is hiding in a directory `texmf/tex/generic/genmisc/`, in the `texmf-dist` tree, and indexed by an `ls-R` database, so it should therefore be findable by any TeX system. \input{random.tex} %% %% ****************************************************************** %% %% The code %% %% This is beta software: the code is messy and covered in tracing output. %% %% Font selection %% %% This is set in the `\input` file `pickfont.tex`, which is created by the preparatory data script `prepdata.sh`. It is the number of working text fonts found on the system. \newcounter{SIL@maxfont} %% %% This is set to a random number between one and `\SIL@maxfont`, and used as the name of the file containing the font name. \newcounter{SIL@fontsel} %% %% This file is created by the preparatory data script `prepdata.sh` after it sets up the subdirectory list of valid text fonts. It sets the value of `\SIL@maxfont`. \input{pickfont.tex} %% %% Color selection %% %% This value is set at the end of the color choice file created by the script. This is the number of color names found by the routine in `prepdata.sh` which extracts the color names. \newcounter{SIL@maxcolno} %% %% The preparatory data script `prepdata.sh` retrieves the colors named in the **`svgnames`** option to the _xcolor_ package and instantiates them as a LaTeX case list in the file `svgnam.tex` as the command `\SIL@svgcolname`. \input{svgnam.tex} %% %% The random font selection is done in a loop because of the need to test the values. This counter counts the iterations… \newcounter{SIL@loopcount} %% %% …and this one the limit. \newcounter{SIL@maxloop} %% %% The colors are selected numerically. This value is the background color of the spine of a book. \newcounter{SIL@bgcolno} %% %% And this is the foreground color, used to typeset the title and author on the spine of a book. \newcounter{SIL@fgcolno} %% %% To make sure that `\SIL@bgcolno` and `\SIL@fgcolno` are distinct, we will need to pick one “dark” and one “light”, crudely distinguished by examining their “brightness” (monochrome intensity value) using the formula due to (Dobovizki, 2008). From inspection, the modal point of the SVG values occurs around 0.6, so use use this to determine if the randomly-selected color is “dark” or “light”. Because it's a decimal fraction, we express it as a dimension and strip off the “pt” later. \newlength{\SIL@splitpoint} \setlength{\SIL@splitpoint}{0.6pt} %% %% We establish defaults for the background color… \def\SIL@bgcol{White} %% %% …and the foreground color. \def\SIL@fgcol{Black} %% %% The values computed by the `prepdata.sh` script and stored in `svgnam.tex` are decimal fractions, to they need to be retrieved as lengths. This is the background value… \newlength{\SIL@bgval} %% %% …and the foreground value. \newlength{\SIL@fgval} %% %% The “dark” or “light” test discussed above also needs to test if the values are too close to the splitpoint. By examination, if the values have an absolute difference of 0.2 they should be visually distinct enough. The difference is calculated and stored in this length variable, as it's a decimal fraction. \newlength{\SIL@bgfgdiff} %% %% In the testing for colors, the nested conditionals set this switch true or false, so that it can be used to control the iteration through successive attempts to find suitable random values. \newif\ifSIL@notyetcols %% %% Page border setup %% %% The page background color is set to a pale brown roughly matching the pine veneer of IKEA bookcases, with the inner page (behind the books) in a dark shadow brown. The technique for imposing a colored margin is due to [Ulrike Fischer](https://tex.stackexchange.com/questions/7725/how-to-set-a-certain-color-other-than-white-to-margin-areas) and uses the commands from the _eso-pic_ package. \pagecolor{BurlyWood} \AddToShipoutPictureBG{% \AtTextLowerLeft{\color{SaddleBrown}% \rule[-\footskip]{\textwidth}{% \dimexpr\textheight+\footskip}}} %% %% Size and shape %% %% Each book is assigned a random height and width, within the bounds set by the maxima and minima. The final dimensions may then be modified by the choice of layout and font. \newlength{\SIL@bookheight} \newlength{\SIL@bookwidth} \newlength{\SIL@minbookwidth} \newlength{\SIL@maxbookwidth} \newlength{\SIL@minbookheight} \newlength{\SIL@maxbookheight} %% %% Title and author dimensions %% %% The title and author need to be measured, and decisions are made about what size they need to be. The two layouts (author separately at the top, and author inline to title) are distinguished with the `\SIL@topauthor` conditional. If the title (with or without the author can fit on one line (rather than multiple lines) this is signalled with the `\SIL@titleoneline` conditional. \newlength{\SIL@titlewidth} \newlength{\SIL@authorwidth} \newlength{\SIL@titleheight} \newlength{\SIL@authorheight} \newlength{\SIL@scaledtitle} \newlength{\SIL@heightfortitle} \newbox\SIL@titlebox \newif\ifSIL@topauthor \newif\ifSIL@titleoneline %% %% Handling the math %% %% To extract the integer part of a fixed-point value, we define a simple strip which uses the integer and throws away the rest. The integer ends up in this counter. \newcounter{SIL@scale} %% %% The integer macro returns the counter above. \def\SIL@scaleint#1.#2\sentinel{% \setcounter{SIL@scale}{#1}} %% %% Settings %% %% We set the space around a box and the thickness of the rule, and remove the page numbers. \fboxsep1em\fboxrule.1pt \pagestyle{empty} %% %% Making the book %% %% The `\makebook` macro is huge, and handles all the detail of making a book spine. It takes one mandatory argument: a BiBTeX entry label value from the declared BiBTeX `.bib` file in `\addbibresource`. %% %% Start by announcing the entry label and setting the values that need to be reset every time. \newcommand{\makebook}[1]{{% \typeout{^^J#1}% \setcounter{SIL@maxloop}{10}% \setcounter{SIL@loopcount}{0}% \setlength{\SIL@minbookwidth}{5mm}% \setlength{\SIL@maxbookwidth}{20mm}% \setlength{\SIL@minbookheight}{70mm}% \setlength{\SIL@maxbookheight}{110mm}% \setlength{\SIL@bookwidth}{0pt}% \setlength{\SIL@bookheight}{0pt}% \setlength{\SIL@heightfortitle}{0pt}% \SIL@topauthorfalse %% %% Start a loop which will pick two random integers, one for background and one for foreground colors. Look these up in the `\SIL@svgcolval` (in `svgnam.tex`) to get the brightness values, and calculate the absolute distance between them. \loop \addtocounter{SIL@loopcount}{1}% \typeout{Try \theSIL@loopcount}% \setrannum{\c@SIL@bgcolno}{1}{% \c@SIL@maxcolno}% \typeout{BG=\theSIL@bgcolno}% \setrannum{\c@SIL@fgcolno}{1}{% \c@SIL@maxcolno}% \typeout{FG=\theSIL@fgcolno}% \setlength{\SIL@bgval}{% \SIL@svgcolval{\theSIL@bgcolno}pt}% \typeout{BGval=\the\SIL@bgval}% \setlength{\SIL@fgval}{% \SIL@svgcolval{\theSIL@fgcolno}pt}% \typeout{FGval=\the\SIL@fgval}% \setlength{\SIL@bgfgdiff}{% \SIL@bgval - \SIL@fgval}% \typeout{Split gap is \the\SIL@bgfgdiff}% \ifdim\SIL@bgfgdiff<0pt \setlength{\SIL@bgfgdiff}{% \SIL@fgval - \SIL@bgval}% \typeout{Using absolute value \the\SIL@bgfgdiff}% \fi %% %% The colours need to be separated either side of the 0.6 splitpoint value of the calculated brightness, so make this the outer test, and make the inner test for the separation difference. This will return true if the colors are separated enough, and come from opposite sides of the split point. If the loop makes `\SIL@maxloop` iterations without finding a pair of values, use whatever was tested last. \ifdim\SIL@bgval<\SIL@splitpoint \ifdim\SIL@fgval>\SIL@splitpoint \ifdim\SIL@bgfgdiff>0.2pt \SIL@notyetcolsfalse \else \SIL@notyetcolstrue \fi \else \SIL@notyetcolstrue \fi \else \ifdim\SIL@fgval<\SIL@splitpoint \ifdim\SIL@bgfgdiff>0.2pt \SIL@notyetcolsfalse \else \SIL@notyetcolstrue \fi \else \SIL@notyetcolstrue \fi \fi \typeout{BG=\theSIL@bgcolno, FG=\theSIL@fgcolno}% \ifnum\c@SIL@loopcount>\c@SIL@maxloop \SIL@notyetcolsfalse \fi \ifSIL@notyetcols\repeat \def\SIL@bgcol{\SIL@svgcolname{% \theSIL@bgcolno}}% \def\SIL@fgcol{\SIL@svgcolname{% \theSIL@fgcolno}}% \typeout{BG=\SIL@bgcol, FG=\SIL@fgcol}% %% %% Now pick a random font: the files generated by `prepdata.sh` are named as integers with a `.tex` extension in the `fontsel` directory. These files load the font as `\SILmfont` (no @ sign, because this is occurring in user mode), and define `\SILmfontname` as the name (for the same reason). \setrannum{\c@SIL@fontsel}{1}{\c@SIL@maxfont}% \input{fontsel/\theSIL@fontsel.tex}% \typeout{Set in \SILmfontname}% %% %% Measure the author width and height at the default size (10pt). If the author fits in 90\% of the maximum width of the book, we put it at the top of the spine and shrink the book width to 1.1 times the set width, provided that is not less than the defined minimum width. The book width is therefore fixed at this point and won't change later. \settowidth{\SIL@authorwidth}{% \SILmfont\citefullauthor{#1}}% \typeout{Author width: \the\SIL@authorwidth}% \settoheight{\SIL@authorheight}{% \SILmfont\citefullauthor{#1}}% \typeout{Author height: \the\SIL@authorheight}% \ifdim\SIL@authorwidth<.9\SIL@maxbookwidth \typeout{Author width is less than 90\% of \the\SIL@maxbookwidth}% \setlength{\SIL@bookwidth}{% 1.1\SIL@authorwidth}% \typeout{Book width set to \the\SIL@bookwidth}% \ifdim\SIL@bookwidth<\SIL@minbookwidth \setlength{\SIL@bookwidth}{% \SIL@minbookwidth}% \typeout{Book width reset to min \the\SIL@minbookwidth}% was height 2020-06-26 \fi \SIL@topauthortrue \else \typeout{Author won't fit in .9 of \the\SIL@maxbookwidth}% \fi %% %% Now measure the title, with an em rule and the author if it hasn't been assigned to the top of the spine. \settowidth{\SIL@titlewidth}{% \SILmfont\citetitle{#1}}% \ifdim\SIL@titlewidth=0pt \typeout{WARNING title width for entry "#1" set in \SILmfontname=0pt!}% \typeout{Likely that the entry has faulty syntax or a bogus title field}% \typeout{or a BiBTeX management or crossref setting is being misinterpreted.}% \typeout{I can't go any further until you fix this, sorry}% \end{document}% \fi \ifSIL@topauthor \typeout{Title width: \the\SIL@titlewidth}% \else \addtolength{\SIL@titlewidth}{% \widthof{\SILmfont~~—~~}}% \addtolength{\SIL@titlewidth}{% \SIL@authorwidth}% \typeout{Title width with em rule and author: \the\SIL@titlewidth}% \fi %% %% We now have enough data to make a shot at the dimensions. Pick a random book height and set the height available for the title (set sideways) to 90\% of that, so that it fits comfortably. Then if the author was earlier assigned to the top of the spine, reduce this height available for the title by 1.2 times the height occupied by the author (again, to leave a little space). In this case, the width has already been set; otherwise, generate a random width now. \typeout{Limits: width=\the\SIL@minbookwidth –\the\SIL@maxbookwidth; height=\the\SIL@minbookheight –\the\SIL@maxbookheight}% \setrandim{\SIL@bookheight}% {\SIL@minbookheight}% {\SIL@maxbookheight}% \typeout{Height generated as \the\SIL@bookheight}% \setlength{\SIL@heightfortitle}% {.9\SIL@bookheight}% \typeout{Height available for title (90\%): \the\SIL@heightfortitle}% \ifSIL@topauthor \typeout{Width set because author fits: \the\SIL@bookwidth}% \addtolength{\SIL@heightfortitle}% {-1.2\SIL@authorheight}% \typeout{Height available for title reset to \the\SIL@heightfortitle}% \else \setrandim{\SIL@bookwidth}% {\SIL@minbookwidth}% {\SIL@maxbookwidth}% \typeout{Width generated as \the\SIL@bookwidth}% \fi %% %% See how the title will fit the space: if it's smaller, it will fit on one line, but we scale it up so it occupies more of the space available. To do this, we perform fixed-point arithmetic on the space it takes and the space available, and use the resulting decimal fraction later to scale the title. However, if the value resulting is greater than four, set it to four, otherwise the title will be too big to fit. The value four was obtained by inspection and trial and error. \ifdim\SIL@titlewidth<\SIL@heightfortitle \typeout{Titling fits in \the\SIL@heightfortitle}% \SIL@titleonelinetrue \edef\titleval{\strip@pt\SIL@titlewidth}% \edef\heightval{\strip@pt\SIL@heightfortitle}% \FPeval\SIL@scaledtitle{\heightval/\titleval}% \typeout{Scaling 1-line title by \SIL@scaledtitle}% \expandafter\SIL@scaleint \SIL@scaledtitle\sentinel \ifnum\c@SIL@scale>4 \gdef\SIL@scaledtitle{4}% \typeout{Resetting scale \theSIL@scale\ to \SIL@scaledtitle}% \fi %% %% Otherwise (too long) the title needs to be set in a box as a multi-line title. This gets complicated: 1. %% set the title in a `\vbox` and then add the height and depth it occupies to get the height of the set title 2. %% if that height is more than the width of the book, use the method above to divide the available height by the [over]used height and use that to reduce the point size by deducting it from 10 (points) 3. %% otherwise (the multiline title fits in the width of the book), do the reverse and increase the point size to take advantage of the extra space by adding it to 10pt. \else \typeout{Titling won't fit \the\SIL@heightfortitle}% \SIL@titleonelinefalse \setbox\SIL@titlebox=\vbox{% \hsize\SIL@heightfortitle \SILmfont\raggedright \vrule height1em width0pt \bfseries\citetitle{#1}% \vrule depth.2em width0pt }% \setlength{\SIL@titleheight}% {\ht\SIL@titlebox + \dp\SIL@titlebox}% \typeout{Multiline title takes \the\SIL@titleheight}% \ifdim\SIL@titleheight>\SIL@bookwidth \typeout{Height of title \the\SIL@titleheight\ is greater than \the\SIL@bookwidth}% \edef\titleval{\strip@pt\SIL@titleheight}% \edef\heightval{\strip@pt\SIL@bookwidth}% \FPeval\SIL@scaledtitle {10 - \heightval / \titleval}% \typeout{10 - \heightval\ ÷ \titleval\ = \SIL@scaledtitle}% \typeout{Using smaller font \SIL@scaledtitle pt for multiline title}% \else \typeout{Height of title \the\SIL@titleheight\ is less than \the\SIL@bookwidth}% \edef\titleval{\strip@pt\SIL@titleheight}% \edef\heightval{\strip@pt\SIL@bookwidth}% \FPeval\SIL@scaledtitle {10 + \heightval / \titleval}% \typeout{10 + \heightval\ ÷ \titleval\ = \SIL@scaledtitle}% \typeout{Using larger font \SIL@scaledtitle pt for multiline title}% \fi \fi %% %% Finally, set a `\vbox` to the defined width _less_ the space occupied by the `\fcolorbox` border and rule; then set the `\fcolorbox` with the chosen colors, with the author at the top if that's what was selected earlier, and the title below, either scaled using `\scalebox` if it was a single-line title, or with the amended font size if it was a multiline title. %% %% For a setting with the author inline to the title, just do the scaling of the title. \leavevmode\vbox{\hsize\SIL@bookwidth \advance\hsize by2\fboxsep \advance\hsize by2\fboxrule \fcolorbox{black}{\SIL@bgcol}{% \ifSIL@topauthor \typeout{Setting with top author}% \vbox to\SIL@bookheight{\hsize\SIL@bookwidth \typeout{Spine is a vbox to \the\SIL@bookheight, hsize=\the\SIL@bookwidth}% \centering \SILmfont\color{\SIL@fgcol}% \citefullauthor{#1}% \par\vfill \rotatebox{90}{\vbox to\SIL@bookwidth{% \hsize\SIL@heightfortitle \null\vfill \typeout{Title in a vbox to \the\SIL@bookwidth, hsize=\the\SIL@heightfortitle}% \raggedright\color{\SIL@fgcol}% \ifSIL@titleoneline \scalebox{\SIL@scaledtitle}% {\bfseries\citetitle{#1}}% \else \fontsize{\SIL@scaledtitle}% {\SIL@scaledtitle}\selectfont \bfseries\citetitle{#1}% \fi \par\vfill}% }% }% \else \typeout{Setting author inline to title}% \vbox to\SIL@bookheight{\hsize\SIL@bookwidth \typeout{Spine is a vbox to \the\SIL@bookheight, hsize=\the\SIL@bookwidth}% \centering \SILmfont\color{\SIL@fgcol}% \rotatebox{90}{\vbox to\SIL@bookwidth{% \hsize\SIL@heightfortitle \null\vfill \typeout{Title and author in a vbox to \the\SIL@bookwidth, hsize=\the\SIL@heightfortitle}% \raggedright\color{\SIL@fgcol}% \ifSIL@titleoneline \scalebox{\SIL@scaledtitle}% {{\bfseries\citetitle{#1}}\quad —\ \ \citefullauthor{#1}}% \else \fontsize{\SIL@scaledtitle}% {\SIL@scaledtitle}\selectfont {\bfseries\citetitle{#1}}\quad —\ \ \citefullauthor{#1}% \fi \par\vfill}% }% }% \fi }% %% %% At the bottom, add a colored bar to fake up the shelf the books stand on. The number is the number of the font that was selected, and is there for error-tracing purposes only. %% %% Despite best efforts at ending all non-control-sequence line-ends with a percent shield, space is creeping in somewhere, so the final negative kern removes it. \\\fboxsep0pt\fboxrule0pt \colorbox{BurlyWood}{\hbox to\hsize{% \hfil\vrule height3mm depth6mm width0pt \normalfont\scriptsize\theSIL@fontsel\hfil}}% }% \kern-2.2mm}}% %% %% Trying to ensure we have an author's full name, not just the surname. This came from [lockstep](https://tex.stackexchange.com/questions/24979/citing-authors-full-name-in-biblatex), modified at _biblatex_'s suggestion to use given-family instead of first-last, but it doesn't seem to have any effect. \DeclareCiteCommand{\citefullauthor} {\boolfalse{citetracker}% \boolfalse{pagetracker}% \DeclareNameAlias{labelname}{given-family}% \usebibmacro{prenote}} {\ifciteindex {\indexnames{labelname}} {}% \printnames{labelname}} {\multicitedelim} {\usebibmacro{postnote}} %% %% Similarly, in an attempt to get keep the author and title for books and monographs, but use the editors and volume/journal/book title for articles, incollection, and inbook entry types, these two definitions don't seem to have any effect. The remaining definitions are needed to cope with the expected commands found in sample bibliographies. %%\DeclareLabeltitle[article]{% %% \field{journaltitle} %%} %%\DeclareLabeltitle %% [inbook,incollection,inproceedings]{% %% \field{booktitle} %% \field{maintitle} %%} \DeclareFieldFormat*{citetitle}{#1} \let\citeA\textcite \let\titleref\emph \def\emdash{~--- } \endinput %% %% End of file `bookshelf.cls'.