[Tex/LaTex] Changing geometry after loading the geometry package

geometrypackage-options

I've looked at some related questions and can't solve this.

I have a common LaTeX file where I load the geometry package among others.

This file (called pre.tex here) is a common, site wide file which loads many packages. These options work for almost all files on the site, But sometimes one file needs different geometry options.

Hence I want to change geometry setting after inputting this common LaTeX file.

I am following the answer give in Unload a LaTeX package but it is giving me an error. Here is a MWE:

\documentclass[11pt,titlepage]{article}%    
\makeatletter
\@namedef{geometry.sty}{}% a fake for a "loaded" package
\makeatother

\input{pre} %this has in it \usepackage{geometry} with some options

% I want to override common file and load geometry 
% with different options here
\usepackage[letterpaper,bindingoffset=0.2in,
            left=1.2in,right=1.2in,top=.8in,bottom=.8in,
            footskip=.25in]{geometry}

\begin{document}    
test    
\end{document}

Where pre.tex has just the line

\usepackage[letterpaper,bindingoffset=0.2in,left=2in,right=2in]{geometry}

The error from lualatex file.tex is

(/usr/local/texlive/2018/texmf-dist/tex/generic/ifxetex/ifxetex.sty))

! LaTeX Error: Option clash for package geometry.

See the LaTeX manual or LaTeX Companion for explanation.
Type  H <return>  for immediate help.
 ...

l.12

?

The strange thing is that this works and compiles

\documentclass[11pt,titlepage]{article}%
\makeatletter
\@namedef{geometry.sty}{}% a fake for a "loaded" package
\makeatother

\input{pre} %this has in it \usepackage{geometry} 

\usepackage{geometry}

\begin{document}
test
\end{document}

So it only fails when I want to load geometry again with options that are different from the original loading?

What is the correct way to remove geometry once loaded including any options it was loaded with, so that if specific file want to use different options they can reload geometry with different options?

Using TL 2018

Best Answer

It will probably always be a bad idea to trick LaTeX into allowing you to load a package twice. Even if you managed to to that by un-defining \ver@geometry.sty (which I will not show how -- pretty easy, though -- so people don't blame me for teaching this :), you would get a long list of other errors, such as

! LaTeX Error: Command \Gm@vrules@mpi already defined.
               Or name \end... illegal, see p.192 of the manual.

See the LaTeX manual or LaTeX Companion for explanation.
Type  H <return>  for immediate help.
 ...                                              

l.927   \llap{\Gm@vrule}\hfil\Gm@vrule}}
                                        %
?

because packages aren't supposed to be loaded twice, so they use \newcommand, which we all know doesn't work twice.

That said, for this geometry case, you can pass your options to the \geometry command:

\geometry{letterpaper,bindingoffset=0.2in,
          left=1.2in,right=1.2in,top=.8in,bottom=.8in,
          footskip=.25in}

From the manual:

\geometry{<options>} changes the page layout according to the options specified in the argument. This command, if any, should be placed only in the preamble (before \begin{document}).

The geometry package may be used as part of a class or another package you use in your document. The command \geometry can overwrite some of the settings in the preamble. Multiple use of \geometry is allowed and then processed with the options concatenated. If geometry is not yet loaded, you can use only \usepackage[<options>]{geometry} instead of \geometry.

For a more general case, I would suggest turning your pre.tex into a package pre.sty and adding some options to it. For example, here's what I did in a class I wrote to have predefined values but allow the user to override them:

\RequirePackage{filecontents}
\begin{filecontents}{pre.sty}

\ProvidesPackage{pre}[2018-09-17 Nasser's geometry package]

% I'm using kvoptions, but you can adapt to your favorite keyval package
\RequirePackage{kvoptions}

% First pass options -- Here only the "interface" option will be declared
\SetupKeyvalOptions{%
  family=nasser@first,%
  prefix=nasser@@%
}
% Create the interface option
\DeclareStringOption{interface}

% Temporarily redefine \KV@errx to allow "undefined" options
\let\nasser@KVerr\KV@errx
\let\KV@errx\@gobble
% Process the interface option. Every other option is left untouched
\ProcessLocalKeyvalOptions{nasser@first}
\let\KV@errx\nasser@KVerr% Restore \KV@errx

% Now we can set the main option list of the package
\SetupKeyvalOptions{%
  family=nasser@main,%
  prefix=nasser@% Different prefix than before!
}

% Create a dummy interface option just to satisfy the option parser -- won't be used anywhere
\DeclareStringOption{interface}

% By now we have \nasser@@interface, a comma-separated list of package names

% Then, for each <package>, we create an option <package> and another force<package>
\@for\pkg:=\nasser@@interface\do{% (notice that for interface, the prefix is nasser@@ -- two @s)
  \DeclareStringOption{\pkg}% Soft option
  \DeclareStringOption{force\pkg}% Hard option
}%

% Now the user has passed the options and we can
\ProcessKeyvalOptions{nasser@main}

% Now the fun part begins :)
% We have three option lists to pass for a package:
%  - The "soft" option from the interface;
%  - The package default and;
%  - The "hard" option from the interface
% and we are going to pass them in that order to respect their hierarchy

% First, the soft options which, if in conflict with the package defaults, are overridden
\@for\pkg:=\nasser@@interface\do{% Loop through every interface'd package
  \expandafter\ifx\csname nasser@\pkg\endcsname\relax% If the option wasn't used do nothing
  \else% else
    \expandafter\PassOptionsToPackage\expandafter{% \PassOptionsToPackage
      \csname nasser@\pkg\endcsname}% {soft,option,list}
      {\pkg}% {package}
  \fi
}

% Now we insert the package defaults
\PassOptionsToPackage{letterpaper,bindingoffset=0.2in,left=2in,right=2in}{geometry}

% And finally, the hard options which can override package defaults
\@for\pkg:=\nasser@@interface\do{% Loop through every interface'd package
  \expandafter\ifx\csname nasser@force\pkg\endcsname\relax% If the option wasn't used do nothing
  \else% else
    \expandafter\PassOptionsToPackage\expandafter{% \PassOptionsToPackage
      \csname nasser@force\pkg\endcsname}% {hard,option,list}
      {\pkg}% {package}
  \fi
}

% And finally load the package
\RequirePackage{geometry}
\endinput

\end{filecontents}
\documentclass[11pt,titlepage]{article}
\usepackage[%
  interface=geometry,%
  forcegeometry={%
    letterpaper,bindingoffset=0.2in,%
    left=1.2in,right=1.2in,top=.8in,bottom=.8in,%
    footskip=.25in%
    }%
  ]{pre}
% With \usepackage{pre}
% * h-part:(L,W,R)=(144.54pt, 310.76125pt, 144.54pt)
% * v-part:(T,H,B)=(95.39737pt, 556.47656pt, 143.09605pt)

% With \geometry
% * h-part:(L,W,R)=(86.72377pt, 426.39369pt, 86.72377pt)
% * v-part:(T,H,B)=(57.81621pt, 679.33757pt, 57.81621pt)

% With geometry option
% * h-part:(L,W,R)=(144.54pt, 310.76125pt, 144.54pt)
% * v-part:(T,H,B)=(57.81621pt, 679.33757pt, 57.81621pt)

% With forcegeometry option
% * h-part:(L,W,R)=(86.72377pt, 426.39369pt, 86.72377pt)
% * v-part:(T,H,B)=(57.81621pt, 679.33757pt, 57.81621pt)

\geometry{verbose}% Just to show settings in the log
\begin{document}
test
\end{document}

Basically, this sets up the package loading so that the package has a list of default options for each package, the user is allowed to pass additional options to that package that respect the package's defaults and is also allowed to override those defaults, if they explicitly ask for that.

The code is is quite long because it allows interfacing with any number of packages and allows this three-level hierarchy. But for your case, with only one package and no hierarchy levels (that is, any option overrides the package's defaults) it could be reduced to:

\RequirePackage{filecontents}
\begin{filecontents}{pre.sty}

\ProvidesPackage{pre}[2018-09-17 Nasser's geometry package]

% I'm using kvoptions, but you can adapt to your favorite keyval package
\RequirePackage{kvoptions}

% Define the family and prefix for the options
\SetupKeyvalOptions{%
  family=nasser,%
  prefix=nasser@%
}%

% Decalre a "geometry" option
\DeclareStringOption{geometry}

% Process input option list
\ProcessKeyvalOptions*

% Now we insert the package defaults
\PassOptionsToPackage{letterpaper,bindingoffset=0.2in,left=2in,right=2in}{geometry}

% Then pass the package options to geometry
\PassOptionsToPackage{\nasser@geometry}{geometry}

% And finally load the package
\RequirePackage{geometry}
\endinput

\end{filecontents}
\documentclass[11pt,titlepage]{article}
\usepackage[%
  geometry={%
    letterpaper,bindingoffset=0.2in,%
    left=1.2in,right=1.2in,top=.8in,bottom=.8in,%
    footskip=.25in%
    }%
  ]{pre}

% With \usepackage{pre}
% * h-part:(L,W,R)=(144.54pt, 310.76125pt, 144.54pt)
% * v-part:(T,H,B)=(95.39737pt, 556.47656pt, 143.09605pt)

% With \geometry
% * h-part:(L,W,R)=(86.72377pt, 426.39369pt, 86.72377pt)
% * v-part:(T,H,B)=(57.81621pt, 679.33757pt, 57.81621pt)

% With geometry option
% * h-part:(L,W,R)=(86.72377pt, 426.39369pt, 86.72377pt)
% * v-part:(T,H,B)=(57.81621pt, 679.33757pt, 57.81621pt)

% \geometry{letterpaper,bindingoffset=0.2in,
%           left=1.2in,right=1.2in,top=.8in,bottom=.8in,
%           footskip=.25in}

\geometry{verbose}% Just to show settings in the log
\begin{document}
test
\end{document}