% easyfloats.dtx
% Copyright 2020 E. Zöllner
%
% This work may be distributed and/or modified under the
% conditions of the LaTeX Project Public License, either version 1.3
% of this license or (at your option) any later version.
% The latest version of this license is in
%   http://www.latex-project.org/lppl.txt
% and version 1.3 or later is part of all distributions of LaTeX
% version 2005/12/01 or later.
%
% This work has the LPPL maintenance status `maintained'.
%
% The Current Maintainer of this work is E. Zöllner.
%
% This work consists of the files listed in easyfloats-file-list.txt.
% The official git repository is https://gitlab.com/erzo/latex-easyfloats


% dtx files (documented tex files) allow "to put the user documentation,
% code documentation and code itself in one place."
% https://www.texdev.net/2009/10/05/the-dtx-format/
% see also
% https://www.texdev.net/2009/10/06/a-model-dtx-file/
% https://ctan.org/pkg/dtxtut
%
% This file is a lot simpler, it contains nothing but the package code with comments.
% Technically it could be used instead of the sty file.
% easyfloats.ins reads this file, strips comments and indentation using docstrip
% and writes the result to the sty file in order to make it faster.


\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{easyfloats}[2020/12/20 v1.0.0]


% ========== options ==========

% load either graphicx or graphbox
\newif\ifesf@loadgraphics
% load graphbox
\newif\ifesf@loadgraphbox
% load longtable
\newif\ifesf@loadlongtable
% load array
\newif\ifesf@loadarray
% load booktabs
\newif\ifesf@loadbooktabs
% print a warning if figure or table is used directly
\newif\ifesf@warnstandardfloats
\esf@warnstandardfloatstrue

\DeclareOption{graphicx}{%
	\esf@loadgraphicstrue
	\esf@loadgraphboxfalse
}
\DeclareOption{graphbox}{%
	\esf@loadgraphicstrue
	\esf@loadgraphboxtrue
}
\DeclareOption{nographic}{%
	\esf@loadgraphicsfalse
	\esf@loadgraphboxfalse
}

\DeclareOption{longtable}{%
	\esf@loadlongtabletrue
}
\DeclareOption{nolongtable}{%
	\esf@loadlongtablefalse
}

\DeclareOption{booktabs}{%
	\esf@loadbooktabstrue
}
\DeclareOption{nobooktabs}{%
	\esf@loadbooktabsfalse
}

\DeclareOption{array}{%
	\esf@loadarraytrue
}
\DeclareOption{noarray}{%
	\esf@loadarrayfalse
}

% If I spell this with spaces LaTeX complains:
% > Unknown option `allowstandardfloats'
% It seems spaces are ignored by \usepackage
% but not by \DeclareOption.
% clsguide says:
% > Note that the name of an option should contain
% > only those characters allowed in a ‘LaTeX name’
% So maybe it's better to not rely on that behaviour.
\DeclareOption{allowstandardfloats}{%
	\esf@warnstandardfloatsfalse
}

%\DeclareOption*{\objectset{\CurrentOption}}


\ExecuteOptions{graphicx, array, booktabs}

% \relax is in definition order, starred is in specification order
\ProcessOptions\relax


% ========== required packages ==========

\RequirePackage{etoolbox}
\RequirePackage{pgfkeys}

% The float package defines the placement option H so that I don't need to
% provide a separate inline/floating option and deal with spacing and captions
% of inline objects. I can just use the usual environments like always.
% It also defines float styles which move the caption to the top or bottom of
% the object. It makes things easier for me because I can just insert the
% caption somewhere independent of the object type.
% I am *not* making use of it's capability to define new float types.
% Please note that \floatplacement is overridden if the placement option is non-empty.
\RequirePackage{float}

% By default there is no space between a table and it's caption (at least with
% the standard document classes). The caption package fixes this.
% The caption package defines the \phantomcaption command (see object environment).
% The caption package is required by the subcaption package.
% It also gives the possibility to customize the layout of captions. But I am
% leaving it to the user to do this if desired.
\RequirePackage{caption}

% subobjects
\RequirePackage{environ}

% "Die Pakete subfigure.sty und subfig.sty werden nicht mehr weiterentwickelt
%  und sind inkompatibel zu einigen aktuelleren Paketen wie hyperref.sty.
%  Als Ersatz bei Kompatibilitätsproblemen empfiehlt sich subcaption.sty."
% (l2tabu, Version 2.4 vom 3. Februar 2016, Seite 16)
\RequirePackage{subcaption}

% \includegraphics
\ifesf@loadgraphics
	\ifesf@loadgraphbox
		% extension for graphicx
		\RequirePackage{graphbox}
	\else
		\RequirePackage{graphicx}
	\fi
\fi

% Set long tables which are not floating
% but can span across page breaks.
% Requires several runs.
\ifesf@loadlongtable
	\RequirePackage{longtable}
\fi

% Additional features in column specification like <, >, !.
% Changes also the behaviour of rules but that is irrelevant if you use booktabs.
\ifesf@loadarray
	\RequirePackage{array}
\fi

% Macros for formating tables.
\ifesf@loadbooktabs
	\RequirePackage{booktabs}
\fi


% ========== init ==========

\newif\ifobject@directkeys
\object@directkeystrue

\newif\if@inobject
\newif\if@firstsubobject
\newif\if@containssubobjects

\floatstyle{plaintop}
\restylefloat{table}
\floatstyle{plain}
\restylefloat{figure}


% ========== hooks ==========

\newif\ifobject@isgraphic

\newcommand{\object@hook}{}
\newcommand{\subobject@hook}{}
\newcommand{\object@graphic@hook}{}

\newrobustcmd{\AtBeginObject}[1]{\appto\object@hook{#1}}
\newrobustcmd{\AtBeginSubobject}[1]{\appto\subobject@hook{#1}}
\newrobustcmd{\AtBeginGraphicObject}[1]{\appto\object@graphic@hook{#1}}


% ========== key management ==========

\newcommand{\esf@ifpgfkeyhasvalue}[1]{% #1: key, #2: executed if #1 is set, #3: executed if #1 has not been set
	% WARNING: Trying to get the value defines the key to \relax if it
	% was undefined (because of \csname). Setting the key to \relax is
	% discouraged because it causes inconsistent behaviour [TikZ & PGF
	% Manual v3.1.5b page 976 \pgfkeysifdefined].
	% With eTeX: \pgfkeysifdefined will report the key as *defined*.
	% Without eTeX: \pgfkeysifdefined will report the key as *un*defined.
	% Therefore don't use this macro with undefined keys. If you do use
	% it with an undefined key it will be treated as if it has been set [!].
	\pgfkeysgetvalue{#1}{\esf@tmp@value}%
	\ifx \pgfkeysnovalue@text \esf@tmp@value
		\expandafter \@secondoftwo
	\else
		\expandafter \@firstoftwo
	\fi
}

\newcommand{\esf@ifpgfkeyexists}[3]{%
	% I cannot use \@firstoftwo and \@secondoftwo because \pgfkeysifdefined
	% does not close the if statement before expanding the 2nd and 3rd argument
	\pgfkeysifdefined{#1}
		{#2}
		{\pgfkeysifdefined{#1/.@cmd}{#2}{#3}}%
}
\newcommand{\esf@ifpgfkeyrequiresvalue}[3]{% #1: key, #2: then block, #3: else block
	\pgfkeysifdefined{#1/.@def}{%
		\pgfkeysgetvalue{#1/.@def}{\esf@tmp@value}%
		\ifx \esf@tmp@value \pgfkeysvaluerequired
			#2%
		\else
			#3%
		\fi
	}{%
		#3%
	}%
}

\newcommand{\esf@ifkeyvalkeyexists}[4]{% #1: family, #2: key, #3: then block, #4: else block
	% \ifcsdef would treat \relax as defined, \ifcsundef does not
	\ifcsundef{KV@#1@#2}{#4}{#3}%
}
\newcommand{\esf@ifkeyvalkeyrequiresvalue}[2]{% #1: family, #2: key, #3: then block, #4: else block
	% \ifcsdef would treat \relax as defined, \ifcsundef does not
	\ifcsundef{KV@#1@#2@default}%
}

\newcommand{\esf@pgfkeyscopyvalue}[2]{% #1: key to be set, #2 key to take the value from
	% WARNING: the order of arguments is opposite of the shell command cp
	% because all pgf macros take the key they operate on as first argument.
	\pgfkeysgetvalue{#2}{\esf@tmp@value}%
	\pgfkeyslet{#1}{\esf@tmp@value}%
}
\newcommand{\esf@pgfkeysvalueappend}[2]{% #1: key to be set, #2 value to be appended
	\pgfkeysgetvalue{#1}{\esf@tmp@value}%
	\appto\esf@tmp@value{#2}%
	\pgfkeyslet{#1}{\esf@tmp@value}%
}

\pgfqkeys{/handlers}{%
	% ------- recommended -------
	.esf@recommended/.code={%
		\esf@ifpgfkeyhasvalue{\pgfkeyscurrentpath}{%
			% do nothing
		}{%
			\edef\esf@tmp@path{\pgfkeyscurrentpath}%
			\PackageWarning{easyfloats}{Recommended key '\strippath\esf@tmp@path' not given. #1\ifx\pgfkeysnovalue#1\@empty\else. \fi This warning occured}%
		}%
	},
	%
	% ------- mandatory -------
	.esf@mandatory/.code={%
		\esf@ifpgfkeyhasvalue{\pgfkeyscurrentpath}{%
			% do nothing
		}{%
			\edef\esf@tmp@path{\pgfkeyscurrentpath}%
			\PackageError{easyfloats}{Mandatory key '\strippath\esf@tmp@path' not given\ifx\pgfkeysnovalue#1\@empty\else. \fi #1}{}%
		}%
	},
	%
	%
	% ------- save unknown -------
	% I can't use /pgf/keyfilters/defined with /pgf/key filter handlers/append filtered to
	% because that appends full keys which are incompatible with keyval.
	.esf@exec on unknown/.code={% #1: control sequence taking two args: key and value
		\pgfkeysalso{\pgfkeyscurrentpath/.unknown/.code=% ##1: value
					#1{\pgfkeyscurrentname}{##1}%
		}%
	},
	%
	% ------- also from -------
	.esf@also from/.code={% #1: expandable control sequence containing options
		\pgfkeysalsofrom{#1}%
	},
	%
	% ------- debugging -------
	.show boolean/.code={%
		\begingroup
			\renewcommand\pgfkeys@handle@boolean[2]{% ##1: if name, ##2: value
				\expandafter \show \csname if##1\endcsname
			}%
			\edef\esf@tmp@path{\pgfkeyscurrentpath}%
			\pgfkeysalso{\esf@tmp@path}%
		\endgroup
	},
	.show boolean/.value forbidden,
}

\newcommand{\AppendToOptionsList}[3]{% #1: macro to append to, #2: key, #3: value
	\ifdefvoid#1%
		{}%
		{\appto#1{, }}%
	\eappto#1{#2}%
	\ifx \pgfkeysnovalue #3\relax
		% do nothing
	\else
		\appto#1{={#3}}%
	\fi
}

\newcommand{\strippath}[1]{%
	\expandafter\strippath@do#1/\pgfeov
}
\def\strippath@do#1/#2\pgfeov{%
	\if\relax\detokenize{#2}\relax
		#1%
	\else
		\strippath@do#2\pgfeov
	\fi
}


% ========== other helper ==========

\newcommand{\IfEnvironmentExistsOrIsEmpty}[1]{% #1: environment name, #2: then block, #3: else block
	\ifstrempty{#1}{%
		\let\IfEnvironmentExistsOrIsEmpty@do=\@firstoftwo
	}{\IfEnvironmentExists{#1}{%
		\let\IfEnvironmentExistsOrIsEmpty@do=\@firstoftwo
	}{%
		\let\IfEnvironmentExistsOrIsEmpty@do=\@secondoftwo
	}}%
	\IfEnvironmentExistsOrIsEmpty@do
}

\newcommand{\IfEnvironmentExists}[3]{% #1: environment name, #2: then block, #3: else block
	\ifcsmacro{#1}{%
		% I am not checking the existence of end#1 because for tabularx that is \relax.
		#2%
	}{%
		#3%
	}%
}


% \patchcmd replaces the first occurence only.
% therefore I am recursing as long as patching was successful
% to make sure that each occurrence was replaced.
% it is important to replace with \textunderscore instead of \_
% because otherwise it would replace \_ by \\_ and loop infinitely.
\newcommand{\PatchUnderscore}[1]{% #1: macro
	\patchcmd{#1}
		{_}{\textunderscore}
		{\PatchUnderscore#1}{}%
}


% ========== object ==========

\newif\ifobject@warnotherenv
\newif\ifobject@warnnolabel
\newif\ifobject@warnnocaption
\newif\ifobject@showenv

% ---------- set object options ----------

\newrobustcmd{\objectset}{% #1: options
	\@ifnextchar [%]
		\objectset@appendtostyles
		\objectset@direct
}

\newcommand{\objectset@direct}{% #1: options
	\pgfqkeys{/object}%
}

\newcommand{\GobbleLeadingSpaceIn}[1]{\edef#1{\expandafter\@firstofone#1}}

\def\objectset@appendtostyles[#1]#2{% #1: style #2: options
	\pgfkeyssetvalue{/object.check/env}{}%
	\pgfqkeys{/object.check}{%
		.esf@exec on unknown=\AppendOptionToObjectStyleGroups{#1},
		#2%
	}%
}

% ---------- check object options ----------

\newcommand{\CheckObjectOption}[2]{% #1: key, #2: value, #3: executed if valid
	% if #3 is *not* executed an error message is printed
	%
	% It is important to reset it at the beginning
	% because the \Check* macros do not expand any
	% of the two arguments if they throw an error.
	\let\CheckObjectOption@handleArg=\@gobble
	\esf@ifpgfkeyexists{/object/#1}{%
		\esf@ifpgfkeyrequiresvalue{/object/#1}{%
			\ifx \pgfkeysnovalue #2\relax
				\PackageError{easyfloats}{The key '#1' requires a value. I am going to ignore this key}{}%
			\else
				\let\CheckObjectOption@handleArg=\@firstofone
			\fi
		}{%
			\let\CheckObjectOption@handleArg=\@firstofone
		}%
	}{\CheckObjectGraphicOption{#1}{#2}{%
		\let\CheckObjectOption@handleArg=\@firstofone
	}{\CheckObjectEnvArgs{#1}{#2}{%
		\let\CheckObjectOption@handleArg=\@firstofone
	}{%
		\PackageError{easyfloats}{I do not know the key '#1' and I am going to ignore it. Perhaps you misspelled it}{}%
	}}}%
	\CheckObjectOption@handleArg
}


% ---------- available object options ----------

\pgfqkeys{/object}{%
	%
	% ------- mandatory -------
	%
	% The environment to use (figure|table)
	% An empty value is allowed for longtable
	% but that is intentionally left undocumented.
	type/.initial,
	type/.value required,
	type/.code=\@ifundefined{fps@#1}
		{\PackageError{easyfloats}{Invalid value for key 'type': '#1'. I am going to ignore this option}{}}
		{\pgfkeyssetvalue{/object/type}{#1}},
	%
	% Where to place the caption, see float package (plain|plaintop|ruled|boxed)
	% If empty do *not* perform a restyle.
	% This is ignored if type is empty.
	float style/.initial=,
	float style/.value required,
	%
	%
	% ------- recommended -------
	%
	% The caption displayed above or below the content (depending on the float style).
	% There is *no* dot added automatically behind it.
	caption/.initial,
	caption/.value required,
	caption/.prefix code={%
		\esf@ifpgfkeyhasvalue{/object/caption}{%
			\PackageWarning{easyfloats}{Overwriting existing value for caption with "#1"}%
		}{%
			% ok
		}%
	},
	%
	% A label which can be referenced by e.g. \ref{label} or \refpage{label}
	label/.initial,
	label/.value required,
	label/.prefix code={%
		\esf@ifpgfkeyhasvalue{/object/label}{%
			\PackageWarning{easyfloats}{Overwriting existing value for label with "#1"}%
		}{%
			% ok
		}%
	},
	%
	%
	% ------- optional -------
	%
	% Where to put the float on the page ([htbp]+ | H | <empty>)
	% (empty means to *not* pass the optional placement argument to the foating environment)
	% This is more powerful than \floatplacement because it allows H.
	placement/.initial=,
	placement/.value required,
	%
	% The caption used in the list of figures/list of tables
	list caption/.initial,
	list caption/.value required,
	%
	% An addition to the caption (at the object only, not in a list)
	details/.initial=,
	details/.value required,
	%
	details sep/.initial=.\space,
	details sep/.value required,
	%
	% An additional label which can be used synonymously to label.
	% If this key is given several times, only the last one will have an effect.
	add label/.initial,
	add label/.value required,
	%
	% TeX code which is inserted at the beginning of the type environment
	align/.initial=\centering,
	align/.value required,
	%
	% TeX code which is inserted at the beginning of the type environment (before align)
	exec/.initial=,
	exec/.value required,
	exec +/.code=\esf@pgfkeysvalueappend{/object/exec}{#1},
	exec +/.value required,
	exec+/.forward to=/object/exec +,
	exec+/.value required,
	%
	% An additional inner environment to wrap the content in like tabular, tabularx, tikzpicture
	env/.code=\IfEnvironmentExistsOrIsEmpty{#1}
		{\pgfkeyssetvalue{/object/env}{#1}}%
		{\PackageError{easyfloats}{Environment #1 which you passed to the key 'env' does not exist}{}},
	env/.value required,
	env=,
	%
	% Specify that this object contains subobjects.
	% This means that if env has a non-empty value
	% this value is used for subobject env
	% and env is set to an empty value.
	sub/.forward to=/object/contains subobjects,
	contains subobjects/.is if = @containssubobjects,
	%
	%
	% ------- tables -------
	% first head and last foot are optional but if they are given they have priority.
	% head and foot always need to have a value even if it's just empty.
	%
	first head/.initial,
	first head/.value required,
	%
	head/.initial=,
	head/.value required,
	%
	foot/.initial=,
	foot/.value required,
	%
	last foot/.initial,
	last foot/.value required,
	%
	table head style/.code=\pgfkeysdef{/object/table head}{\pgfqkeys{/object}{#1}},
	table head style/.value required,
	table head style={%
		first head =
			\toprule
			#1 \\
			\midrule,
		head =
			#1 \\
			\midrule,
		foot =
			\midrule
			\ifx\object@tableBreakText\@empty
			\else
				\multicolumn{\the\LT@cols}{r@\relax}{\object@tableBreakText}%
			\fi,
		last foot =
			\bottomrule,
	},
	%
	% text displayed in the non-last foot of a table
	table break text/.code=\def\object@tableBreakText{#1},
	table break text/.value required,
	table break text = (to be continued),
	%
	%
	% ------- warnings -------
	%
	warn no caption/.is if=object@warnnocaption,
	warn no caption=true,
	%
	warn no label/.is if=object@warnnolabel,
	warn no label=true,
	%
	warn other env/.is if=object@warnotherenv,
	warn other env=false,
	%
	%
	% ------- debugging -------
	%
	show env args/.is if=object@showenv,
	show env args=false,
	%
	%
	% ------- subobject -------
	% .value required is code duplication but
	% it is necessary for \esf@ifpgfkeyrequiresvalue.
	% It also changes the error message to report
	% this key instead of the linked key.
	%
	subobject linewidth/.forward to=/subobject/linewidth,
	subobject linewidth/.value required,
	subobject env/.forward to=/subobject/env,
	subobject env/.value required,
	%
	subcaptionbox/.forward to=/subobject/subcaptionbox,
	subcaptionbox/.value forbidden,
	subpage/.forward to=/subobject/subpage,
	subpage/.value forbidden,
	%
	subcaptionbox inner pos/.forward to=/subobject/subcaptionbox inner pos,
	subcaptionbox inner pos/.value required,
	%
	subpage inner pos/.forward to=/subobject/subpage inner pos,
	subpage inner pos/.value required,
	subpage height/.forward to=/subobject/subpage height,
	subpage height/.value required,
	subpage outer pos/.forward to=/subobject/subpage outer pos,
	subpage outer pos/.value required,
	subpage align/.forward to=/subobject/subpage align,
	subpage align/.value required,
	%
	subobject sep/.forward to=/subobject/sep,
	subobject sep/.value required,
	%
	subobject hor/.forward to=/subobject/hor,
	%subobject hor/ value is allowed but not necessary
	subobject hor sep/.forward to=/subobject/hor sep,
	subobject hor sep/.value required,
	subobject hor sep+/.forward to=/subobject/hor sep+,
	subobject hor sep+/.value required,
	subobject hor sep +/.forward to=/subobject/hor sep +,
	subobject hor sep +/.value required,
	%
	subobject ver/.forward to=/subobject/ver,
	%subobject ver/ value is allowed but not necessary
	subobject ver sep/.forward to=/subobject/ver sep,
	subobject ver sep/.value required,
	subobject ver sep+/.forward to=/subobject/ver sep+,
	subobject ver sep+/.value required,
	subobject ver sep +/.forward to=/subobject/ver sep +,
	subobject ver sep +/.value required,
	%
	subobject exec/.forward to=/subobject/exec,
	subobject exec/.value required,
	subobject exec+/.forward to=/subobject/exec+,
	subobject exec+/.value required,
	subobject exec +/.forward to=/subobject/exec +,
	subobject exec +/.value required,
	%
	subobject warn no caption/.forward to=/subobject/warn no caption,
	%value is allowed but not necessary
	subobject warn no label/.forward to=/subobject/warn no label,
	%value is allowed but not necessary
	%
	%
	% ------- unknown handler / key patterns -------
	%
	% Two key patterns are defined to automatically pass arguments to the environments specified by the env option if the environment name matches.
	% `<envname> arg` is for a single mandatory (undelimited) argument. It is wrapped in braces.
	% `<envname> args` is not wrapped in braces and can be used for several arguments or an optional argument.
	% It cannot be used for a single mandatory argument because \pgfkeys strips several levels of braces.
	.unknown/.code={%
		\ObjectProcessKeyPattern{\pgfkeyscurrentname}{#1}{%
			\pgfkeysgetvalue{/object/.really unknown/.@cmd}{\esf@tmp@err}%
			\esf@tmp@err#1\pgfeov
		}%
	},
	% Setting an unknown key handler shall not override env args but set an additional unknown handler.
	.unknown/.code/.code={%
		\pgfkeysdef{/object/.really unknown}{#1}%
	},
	.really unknown/.code={%
		\edef\do{\noexpand\pgfkeys{/errors/unknown key={\pgfkeyscurrentkey}{\unexpanded{#1}}}}%
		\do
	},
}

\pgfqkeys{/object.check}{%
	env/.initial=,
	env/.code={%
		\ifx \pgfkeysnovalue #1\relax
			\PackageError{easyfloats}{The key 'env' requires a value. I am going to ignore this key}{}%
		\else
			\IfEnvironmentExistsOrIsEmpty{#1}{%
				% set env so that I can use the value to check args
				\pgfkeyssetvalue{/object.check/env}{#1}%
				% call the unknown handler which appends it to the style
				% unlike '/errors/unknown key' '.unknown' does not take the key as argument
				% but it may require \pgfkeyscurrentpath and \pgfkeyscurrentname to be set
				\pgfkeys@split@path
				\pgfkeysvalueof{\pgfkeyscurrentpath/.unknown/.@cmd}#1\pgfeov
			}{%
				\PackageError{easyfloats}{Environment #1 which you passed to the key 'env' does not exist}{}%
			}%
		\fi
	},
	type/.code={%
		% set type so that I can use the value in \NewObjectStyleGroup
		\pgfkeyssetvalue{/object.check/type}{#1}%
		% call the unknown handler which appends it to the style
		% unlike '/errors/unknown key' '.unknown' does not take the key as argument
		% but it may require \pgfkeyscurrentpath and \pgfkeyscurrentname to be set
		\pgfkeys@split@path
		% Without the braces I got "Undefined control sequence. [...] \pgfeov"
		% \pgfkeys strips several levels of braces around an argument.
		% (I have already experienced that while experimenting with <env> args.
		%  That is the reason why I defined two separate keys arg and args.)
		% I am assuming that because of that the \pgfeov ended the argument of some
		% pgfkeys internal macro too early causing undefined behavior.
		% I am preventing this from happening by hiding \pgfeov inside of braces
		% which are inside of the argument so they won't get stripped.
		% I put \@firstofone in front of them so that they do not function as a group.
		\@firstofone{\pgfkeysvalueof{\pgfkeyscurrentpath/.unknown/.@cmd}#1\pgfeov}%
	},
}

% ---------- process key patterns ----------

\newcommand{\ObjectProcessKeyPattern}[2]{% #1: key, #2: value, #3: else
	\expandafter\ObjectProcessKeyPattern@expandedkey\expandafter{#1}{#2}%
}

\newcommand{\ObjectProcessKeyPattern@expandedkey}[2]{% #1: key, #2: value, #3: else
	\ObjectProcessGraphicOption@expandedkey{#1}{#2}{%
		\@gobble
	}{%
		\ObjectProcessArgs@expandedkey{#1}{#2}\relax
	}%
}

% ---------- process graphic options ----------

\ifesf@loadgraphics

% do not confuse this with \CheckGraphicobjectOption
\newcommand{\CheckObjectGraphicOption}[2]{% #1: key, #2: value
	% #3: executed if valid, #4: executed if *not* a graphic option
	% Please note that if the option looks like a graphic option
	% but is not valid neither #3 nor #4 is executed.
	% Instead an appropriate error message is printed.
	\begingroup
		\let\ObjectCheckGraphicOption@aftergroup=\@gobbletwo
		\renewcommand{\AppendToOptionsList}[3]{%
			\let\ObjectCheckGraphicOption@aftergroup=\@firstoftwo
		}%
		\ObjectProcessGraphicOption{#1}{#2}{%
			\let\ObjectCheckGraphicOption@aftergroup=\@secondoftwo
		}%
	\expandafter
	\endgroup
	\ObjectCheckGraphicOption@aftergroup
}

\newcommand{\ObjectProcessGraphicOption}[2]{% #1: key, #2: value
	% #3: executed if *not* a graphic option
	\expandafter\ObjectProcessGraphicOption@expandedkey\expandafter{#1}{#2}\relax
}
\newcommand{\ObjectProcessGraphicOption@expandedkey}[2]{% #1: key, #2: value
	% #3: executed if graphic option, #4: executed if *not* a graphic option
	\ObjectProcessGraphicOption@checkoption#1graphic \pgfeov{#2}%
}
\long\def\ObjectProcessGraphicOption@checkoption#1graphic #2\pgfeov#3{% #1: empty if graphic option, #2: key, #3: value
	% #4: executed if graphic option, #5: executed if *not* a graphic option
	\def\esf@tmp@arg{#1}%
	\ifx \esf@tmp@arg \@empty
		\edef\esf@opgoco@key{\StripGraphicSpace#2\pgfeov}%
		\CheckGraphicobjectOption{\esf@opgoco@key}{#3}{%
			\AppendGraphicobjectOption{\esf@opgoco@key}{#3}%
		}%
		\expandafter \@firstoftwo
	\else
		\expandafter \@secondoftwo
	\fi
}

\def\StripGraphicSpace#1graphic \pgfeov{#1}

\else %\ifesf@loadgraphics

\newcommand{\CheckObjectGraphicOption}[2]{% #1: key, #2: value
	% #3: executed if valid, #4: executed if *not* a graphic option
	\@secondoftwo
}

\newcommand{\ObjectProcessGraphicOption@expandedkey}[2]{% #1: key, #2: value
	% #3: executed if graphic option, #4: executed if *not* a graphic option
	\@secondoftwo
}

\fi %\ifesf@loadgraphics

% ---------- process env args ----------

\newcommand{\@object@envkey}{/object/env}
\newcommand{\CheckObjectEnvArgs}[2]{% #1: key, #2: value
	% #3: executed if valid, #4: executed if *not* an <env> arg
	% Please note that if the option looks like an <env> arg
	% but is not valid neither #3 nor #4 is executed.
	% Instead an appropriate error message is printed.
	\begingroup
		\edef\ObjectCheckArgs@key{#1}%
		\let\ObjectCheckArgs@aftergroup=\@gobbletwo
		\pgfkeysgetvalue{/object.check/env}{\esf@tmp@env}%
		\pgfkeys{\@object@envkey/.expand once=\esf@tmp@env}%
		\renewcommand{\ObjectDefineEnvargs}[2]{% ##1: env name, ##2: arguments
			\let\ObjectCheckArgs@aftergroup=\@firstoftwo
		}%
		\ObjectProcessArgs{\ObjectCheckArgs@key}{#2}{%
			\let\ObjectCheckArgs@aftergroup=\@secondoftwo
		}%
	\expandafter
	\endgroup
	\ObjectCheckArgs@aftergroup
}


\newcommand{\ObjectProcessArgs}[2]{% #1: key, #2: value, #3: else
	\expandafter\ObjectProcessArgs@expandedkey\expandafter{#1}{#2}\relax
}

\newcommand{\ObjectProcessArgs@expandedkey}[2]{% #1: key, #2: value, #3: then, #4: else
	\IfEndsOnSpacePlus{#1}{%
		\let\ObjectProcessArgs@csdef=\csappto
		\expandafter\ObjectProcessArgs@checkArgs\expandafter{\StripSpacePlus#1\pgfeov}{#2}%
	}{\IfEndsOnPlus{#1}{%
		\let\ObjectProcessArgs@csdef=\csappto
		\expandafter\ObjectProcessArgs@checkArgs\expandafter{\StripPlus#1\pgfeov}{#2}%
	}{%
		\let\ObjectProcessArgs@csdef=\csdef
		\ObjectProcessArgs@checkArgs{#1}{#2}%
	}}%
}

\newrobustcmd{\IfEndsOn}[2]{% #1: end character, #2: text
	\def\IfEndsOn@do##1#1##2\pgfeov{%
		\ifstrequal{##2}{}{%
			\let\do=\@secondoftwo
		}{\ifstrequal{##2}{#1}{%
			\let\do=\@firstoftwo
		}{%
			\def\do{\IfEndsOn@do##2\pgfeov}%
		}}%
		\do
	}%
	\IfEndsOn@do#2#1\pgfeov
}
\newcommand{\IfEndsOnPlus}{\IfEndsOn+}
\newcommand{\IfEndsOnSpacePlus}{\IfEndsOn{ +}}

\def\StripSpacePlus#1 +\pgfeov{#1}
\def\StripPlus#1+\pgfeov{#1}


\newcommand{\ObjectProcessArgs@checkArgs}[2]{% #1: key, #2: value, #3: then, #4: else
	\ObjectProcessArgs@do#1 args\pgfeov{#2}%
}
\long\def\ObjectProcessArgs@do#1 arg#2\pgfeov#3{% #1: env name, #2: test value, #3: arguments, #4: then, #5: else
	\let\ObjectProcessArgs@do@after=\@firstoftwo
	\ifstrequal{#2}{ args}{%
		\ObjectDefineEnvargsCheckName{#1}{{#3}}%
	}{\ifstrequal{#2}{s args}{%
		\ObjectDefineEnvargsCheckName{#1}{#3}%
	}{\ifstrequal{#2}{s}{%
		\ifstrequal{#1}{arg}{%
			\ObjectDefineEnvargsAuto{{#3}}%
		}{\ifstrequal{#1}{args}{%
			\ObjectDefineEnvargsAuto{#3}%
		}{%
			\let\ObjectProcessArgs@do@after=\@secondoftwo
		}}%
	}{%
		\let\ObjectProcessArgs@do@after=\@secondoftwo
	}}}%
	\ObjectProcessArgs@do@after
}
\newcommand{\ObjectDefineEnvargsCheckName}[2]{% #1: env name, #2: arguments
	\ifobject@directkeys
		\pgfkeysgetvalue{\@object@envkey}{\esf@tmp@env}%
		\ifdefvoid{\esf@tmp@env}{%
		}{\ifdefstring{\esf@tmp@env}{#1}{%
		}{%
			\ifobject@warnotherenv
				\PackageWarning{easyfloats}{Defining 'arg(s)' for #1 but 'env=\esf@tmp@env'}%
			\fi
		}}%
	\fi
	\IfEnvironmentExists{#1}
		{\ObjectDefineEnvargs{#1}{#2}}
		{\PackageError{easyfloats}{Environment #1 for which you set 'arg(s)' does not exist}{}}%
}
\newcommand{\ObjectDefineEnvargsAuto}[1]{% #1: arguments
	\pgfkeysgetvalue{\@object@envkey}{\esf@tmp@env}%
	\ifx \esf@tmp@env \@empty
		\PackageError{easyfloats}{'env' for 'arg(s)' is not defined}{}%
	\else
		\ObjectDefineEnvargs{\esf@tmp@env}{#1}%
	\fi
}
\newcommand{\ObjectDefineEnvargs}[2]{% #1: env name, #2: arguments
	\ObjectProcessArgs@csdef{object@#1 env@args}{#2}%
}


\newcommand{\ObjectAppendEnvargs}[1]{% #1: macro
	\ifcsname object@\pgfkeysvalueof{\@object@envkey} env@args\endcsname
		\eappto#1{\csexpandonce{object@\pgfkeysvalueof{\@object@envkey} env@args}}%
	\fi
}


% ---------- object environment ----------

\newcommand{\object@head}{}
\newcommand{\object@foot}{}
\newcommand{\object@restorestandardfloats}{}

\newcommand{\object@label}[1]{%
	\pgfkeys{/object/label={#1}}%
}%
\newcommand{\object@caption}[2][\pgfkeysnovalue]{%
	\pgfkeys{/object/caption={#2}}%
	\ifx \pgfkeysnovalue #1\relax
		\pgfkeys{/object/list caption={#2}}%
	\else
		\pgfkeys{/object/list caption={#1}}%
	\fi
}
\newcommand{\object@longtable@label}[1]{%
	\ObjectDefineEnvargs{longtable}{\label{#1}}%
}%
\newcommand{\object@longtable@caption}[2][\pgfkeysnovalue]{%
	\ifx \pgfkeysnovalue #1\relax
		\ObjectDefineEnvargs{longtable}{\caption{#2}}%
	\else
		\ObjectDefineEnvargs{longtable}{\caption[#1]{#2}}%
	\fi
	\def\object@longtable@caption@sep{\ObjectDefineEnvargs{longtable}{\\}}%
}

\newenvironment{object}[1]{% #1: options
	\if@inobject
		\PackageError{easyfloats}{object environment may not be nested}{Did you mean to use the subobject environment instead?}%
	\fi
	\@inobjecttrue
	%
	\object@restorestandardfloats
	%
	\object@hook
	\ifobject@isgraphic
		\object@graphic@hook
		\global\object@isgraphicfalse
	\fi
	%
	% ---------- options ---------
	%
	\pgfqkeys{/object}{%
		#1,
		%
		% ------- mandatory -------
		type/.esf@mandatory=It should be the name of a floating environment like figure or table,
		float style/.esf@mandatory={It should be one of plain, plaintop, ruled or boxed. See float package},
		env/.esf@mandatory=It should be the name of an environment like tabular or empty,
	}%
		%
		% ------- contains subobjects -------
	\if@containssubobjects
		\pgfkeysgetvalue{/object/env}{\esf@tmp@env}%
		\ifx \esf@tmp@env \@empty
		\else
			\pgfkeys{/subobject/env/.expand once=\esf@tmp@env}%
			\pgfkeyssetvalue{/object/env}{}%
		\fi
	\fi
	%
	% ---------- prepare ---------
	%
	\pgfkeysgetvalue{/object/env}{\esf@tmp@env}%
	\ifdefstring\esf@tmp@env{longtable}{%
		\object@longtable@prepare
	}{%
		\esf@ifpgfkeyhasvalue{/object/first head}{%
			\pgfkeysgetvalue{/object/first head}{\object@head}%
		}{%
			\pgfkeysgetvalue{/object/head}{\object@head}%
		}%
		\esf@ifpgfkeyhasvalue{/object/last foot}{%
			\pgfkeysgetvalue{/object/last foot}{\object@foot}%
		}{%
			\pgfkeysgetvalue{/object/foot}{\object@foot}%
		}%
	}%
	%
	\pgfkeysgetvalue{/object/type}{\esf@tmp@type}%
	\ifx\esf@tmp@type\@empty
		\let\object@floatenv@begin=\relax
		\let\object@floatenv@end=\relax
	\else
		\pgfkeysgetvalue{/object/float style}{\esf@tmp@floatstyle}%
		\ifx \esf@tmp@floatstyle \@empty
		\else
			\floatstyle{\esf@tmp@floatstyle}%
			\restylefloat{\esf@tmp@type}%
		\fi
		\edef\object@floatenv@begin{\expandonce{\csname\esf@tmp@type\endcsname}}%
		\edef\object@floatenv@end{\expandonce{\csname end\esf@tmp@type\endcsname}}%
		\pgfkeysgetvalue{/object/placement}{\esf@tmp@placement}%
		\ifx \esf@tmp@placement \@empty
			% do nothing
		\else
			\eappto\object@floatenv@begin{[\esf@tmp@placement]}%
		\fi
	\fi
	%
	\pgfkeysgetvalue{/object/details}{\object@details}%
	\ifx \object@details \@empty
		% do nothing
	\else
		\preto\object@details{\pgfkeysvalueof{/object/details sep}}%
	\fi
	%
	\pgfkeysgetvalue{/object/env}{\esf@tmp@env}%
	\ifx \esf@tmp@env \@empty
		% if you change this remember to update \object@lookahead@beforesubobject
		\def\object@innerenv@begin{\object@head\ignorespaces}%
		\def\object@innerenv@end{\unskip\object@foot}%
	\else
		\edef\object@innerenv@begin{\expandonce{\csname\esf@tmp@env\endcsname}}%
		\edef\object@innerenv@end{\expandonce{\csname end\esf@tmp@env\endcsname}}%
		\ObjectAppendEnvargs{\object@innerenv@begin}%
		\eappto\object@innerenv@begin{\expandonce{\object@head}}%
		\epreto\object@innerenv@end{\expandonce{\object@foot}}%
		\let\object@lookahead@begin=\object@innerenv@begin
		\ifobject@showenv
			\show\object@innerenv@begin
		\fi
		\let\object@innerenv@begin=\object@lookahead
	\fi
	%
	\global \@firstsubobjecttrue
	%
	% ---------- execute ---------
	%
	\object@floatenv@begin
		\let\esf@original@label=\label
		\let\esf@original@caption=\caption
		\let\label=\object@label
		\let\caption=\object@caption
		\pgfkeysvalueof{/object/exec}%
		\pgfkeysvalueof{/object/align}%
		\object@innerenv@begin
}{%
		\object@innerenv@end
		\if@containssubobjects
			\if@firstsubobject
				\PackageWarning{easyfloats}{'contains subobjects' is true but the object does not contain any subobjects.}%
			\fi
		\fi
		\let\label=\esf@original@label
		\let\caption=\esf@original@caption
		\object@processCaptionAndLabel
	\object@floatenv@end
}

\newcommand{\object@processCaptionAndLabel}{%
	% position of caption (above/below) is handled by float package (see float style)
	\esf@ifpgfkeyhasvalue{/object/caption}{%
		\esf@ifpgfkeyhasvalue{/object/list caption}%
			{\pgfkeysgetvalue{/object/list caption}{\esf@tmp@caption@short}}%
			{\pgfkeysgetvalue{/object/caption}{\esf@tmp@caption@short}}%
		\caption[\esf@tmp@caption@short]{\pgfkeysvalueof{/object/caption}\object@details}%
	}{%
		\ifobject@warnnocaption
			\PackageWarning{easyfloats}{%
				No caption given.
				This object will not show up in the list of figures/tables/whatsoever.
				If captions for subfigures are given they will be placed incorrectly in the list of figures.
				If you are using hyperref links to this figure may turn out wrong.
				This warning occured%
			}%
		\fi
		% Generate an anchor for a \label.
		% \phantomcaption is defined in the caption-package but
		% documented in subcaption for the subfigure environment.
		% It appears that here (without a subfigure but within a figure)
		% references are output correctly but links created by hyperref
		% which should be pointing to this object point to the previous caption.
		\phantomcaption
	}%
	% ------- label -------
	\esf@ifpgfkeyhasvalue{/object/label}{%
		\label{\pgfkeysvalueof{/object/label}}%
	}{%
		\ifobject@warnnolabel
			\PackageWarning{easyfloats}{%
				No label given.
				Without a label you cannot reference this object.
				This warning occured%
			}%
		\fi
	}%
	\esf@ifpgfkeyhasvalue{/object/add label}%
		{\label{\pgfkeysvalueof{/object/add label}}}%
		{}%
}

\newcommand{\object@longtable@prepare}{%
	\pgfkeyssetvalue{/object/type}{}%
	\pgfkeyssetvalue{/object/align}{}%
	\let\object@label=\label
	\let\object@caption=\caption
	\let\ObjectProcessArgs@csdef=\csappto
	%
	\let\esf@original@label=\label
	\let\esf@original@caption=\caption
	\let\label=\object@longtable@label
	\let\caption=\object@longtable@caption
	\let\object@longtable@caption@sep=\relax
	% it seems possible to have a label without a caption in a longtable
	% which actually makes sense because \caption may appear several times
	% so it makes sense to increase the counter somewhere else.
	\let\phantomcaption=\relax
	\object@processCaptionAndLabel
	\let\object@processCaptionAndLabel=\relax
	\object@longtable@caption@sep
	\let\label=\esf@original@label
	\let\caption=\esf@original@caption
	%
	\esf@ifpgfkeyhasvalue{/object/first head}{%
		\ObjectDefineEnvargs{longtable}{\pgfkeysvalueof{/object/first head}}%
	}{%
		\ObjectDefineEnvargs{longtable}{\pgfkeysvalueof{/object/head}}%
	}%
	\ObjectDefineEnvargs{longtable}{%
		\endfirsthead
		\pgfkeysvalueof{/object/head}%
		\endhead
		\pgfkeysvalueof{/object/foot}%
		\endfoot
	}%
	\esf@ifpgfkeyhasvalue{/object/last foot}{%
		\ObjectDefineEnvargs{longtable}{\pgfkeysvalueof{/object/last foot}}%
	}{%
		\ObjectDefineEnvargs{longtable}{\pgfkeysvalueof{/object/foot}}%
	}%
	\ObjectDefineEnvargs{longtable}{%
		\endlastfoot
	}%
}


% ---------- lookahead for subobjects ----------

\newcommand{\object@lookahead@nosubobject}{%
	\object@lookahead@begin
}
\newcommand{\object@lookahead@beforesubobject}{%
	\PackageWarning{easyfloats}{It seems you have forgotten to specify 'contains subobjects'. I am using the value of 'env' for the subobjects instead of for \@currenvir.}%
	\pgfkeysgetvalue{/object/env}{\esf@tmp@env}%
	\pgfkeys{/subobject/env/.expand once=\esf@tmp@env}%
	\pgfkeyssetvalue{/object/env}{}%
	\let\object@innerenv@end=\unskip
	%\ignorespaces unnecessary because I just checked
	%that the next token is the start of a subobject
}

\newcommand{\object@lookahead}{%
	\futurelet \next \object@lookahead@checknext
}
\newcommand{\object@lookahead@checknext}{%
	\ifx \next \@implicitspace
		\let\object@lookahead@gobble=\@gobblespace
		\let\object@lookahead@do=\object@lookahead
	\else\ifx \next \par
		\let\object@lookahead@gobble=\@gobble
		\let\object@lookahead@do=\object@lookahead
	\else\ifx \next \begin
		\let\object@lookahead@gobble=\@empty
		\let\object@lookahead@do=\object@lookahead@checkbegin
	\else\ifx \next \includegraphicsubobject
		\let\object@lookahead@gobble=\@empty
		\let\object@lookahead@do=\object@lookahead@beforesubobject
	\else
		\let\object@lookahead@gobble=\@empty
		\let\object@lookahead@do=\object@lookahead@nosubobject
	\fi \fi \fi \fi
	\expandafter \object@lookahead@do \object@lookahead@gobble
}
\newcommand{\object@lookahead@checkbegin}[2]{% #1: \begin, #2: environment name
	\ifstrequal{#2}{subobject}
		\object@lookahead@beforesubobject
		\object@lookahead@nosubobject
	#1{#2}%
}

\@firstofone{\let\@implicitspace= }
\@firstofone{\def\@gobblespace} {}


% ========== object styles ==========

\newcommand{\IfObjectStyleExists}[1]{% #1: style name or group name, #2: then block, #3: else block
	\ifcsmacro{object@style@#1}%
}
\newcommand{\IfObjectStyleNotGroup}[1]{% #1: style name, #2: then block, #3: else block
	\IfObjectStyleExists{#1}
		{\edef\esf@tmp@do{\ifcsstring{object@stylegroup@#1}{{#1}}}\esf@tmp@do}%
		\@secondoftwo
}

\newcommand{\objectstyle@init}[1]{% #1: style name or group name
	\expandafter \newcommand \csname object@style@#1\endcsname{/utils/exec=\object@directkeysfalse}%
	\expandafter \newcommand \csname object@stylegroup@#1\endcsname{}%
}

\newrobustcmd{\AddObjectStyleToGroup}[2]{% #1: group name, #2: style name
	\IfObjectStyleExists{#1}{%
	\IfObjectStyleNotGroup{#2}{%
		\AddObjectStyleToGroup@do{#1}{#2}%
	}{%
		\PackageError{easyfloats}{Undefined object style: #2}{In this case you cannot use a style group instead of a group.}%
	}}{%
		\PackageError{easyfloats}{Undefined object style group: #1}{}%
	}%
}
\newrobustcmd{\AddObjectStyleToGroup@do}[2]{% #1: group name, #2: style name
	\expandafter\eappto\csname object@stylegroup@#1\endcsname{{#2}}%
}
\newrobustcmd{\ShowObjectStylesInGroup}[1]{%
	\expandafter\show\csname object@stylegroup@#1\endcsname
}

\newcommand{\AppendOptionToObjectStyleGroups}[3]{% #1: list of style groups, #2: key, #3: value
	\CheckObjectOption{#2}{#3}{%
		\@for\esf@aotosg@group:=#1\do{%
			\GobbleLeadingSpaceIn\esf@aotosg@group
			\AppendOptionToObjectStyleGroup\esf@aotosg@group{#2}{#3}%
		}%
	}%
}
\newcommand{\AppendOptionToObjectStyleGroup}[3]{% #1: style group, #2: key, #3: value
	\IfObjectStyleExists{#1}{%
		\expandafter \let \expandafter \esf@tmp@listofstyles \csname object@stylegroup@#1\endcsname
		\def\esf@tmp@loop{\@tfor\esf@aotosg@style:=}%
		\expandafter\esf@tmp@loop\esf@tmp@listofstyles \do {%
			\AppendOptionToObjectStyle\esf@aotosg@style{#2}{#3}%
		}%
	}{%
		\PackageError{easyfloats}{Undefined object style: '#1'}{}%
	}%
}
\newcommand{\AppendOptionToObjectStyle}[3]{% #1: style, #2: key, #3: value
	\expandafter\AppendToOptionsList\csname object@style@#1\endcsname{#2}{#3}%
}
\newrobustcmd{\ShowObjectStyleOptions}[1]{%
	\IfObjectStyleNotGroup{#1}
		{\expandafter\show\csname object@style@#1\endcsname}%
		{\PackageError{easyfloats}{Undefined object style: '#1'}{}}%
}


% ---------- style groups ----------

\newrobustcmd{\NewObjectStyleGroup}[2]{% #1: group name, #2: list of style names
	\IfObjectStyleExists{#1}{%
		\PackageError{easyfloats}{Style or style group with the name '#1' exists already}{}%
	}{%
		\objectstyle@init{#1}%
		\@for\esf@nosg@style:=#2\do{%
			\GobbleLeadingSpaceIn\esf@nosg@style
			\AddObjectStyleToGroup{#1}{\esf@nosg@style}%
		}%
	}%
}

% ---------- object styles ----------

\newrobustcmd{\NewObjectStyle}[2]{% #1: name, #2: options
	\IfObjectStyleExists{#1}{%
		\PackageError{easyfloats}{Style or style group with the name '#1' exists already}{}%
	}{%
		\objectstyle@init{#1}%
		\AddObjectStyleToGroup@do{#1}{#1}%
		%hack into \objectset[#1] to retrieve the value of type
		\pgfkeyssetvalue{/object.check/type}{\pgfkeysnovalue}%
		\objectset[#1]{#2}%
		\esf@ifpgfkeyhasvalue{/object.check/type}{%
			\edef\@do{\noexpand\DeprecateStandardFloatObject{\pgfkeysvalueof{/object.check/type}}{#1object}}%
			\@do
		}{%
			\PackageError{easyfloats}{Missing required key 'type'}%
		}%
		%
		\AddObjectStyleToGroup{all}{#1}%
		%
		\newenvironment{#1object}[1]{% #1: options
			\object{%
					.esf@also from/.expand once=\csname object@style@#1\endcsname,
					/utils/exec=\object@directkeystrue,
					##1,
				}%
		}{%
			\endobject
		}%
		\pgfkeysgetvalue{/object.check/type}{\NewObjectStyle@type}%
		\edef\@do{\noexpand\AfterPreamble{%
			\noexpand\IfEnvironmentExists{sub\NewObjectStyle@type}{%
				\noexpand\PackageInfo{easyfloats}{environment 'sub\NewObjectStyle@type' exists already}%
			}{\NewObjectStyle@IfCaptionNewEnough{%
				\noexpand\PackageInfo{easyfloats}{declaring new caption subtype 'sub\NewObjectStyle@type'}%
				\noexpand\DeclareCaptionSubType{\NewObjectStyle@type}%
			}{%
				\noexpand\PackageWarning{easyfloats}{The caption package is too old for me to define the subtype automatically. Please insert `\string\AtBeginDocument{\string\DeclareCaptionSubType{\NewObjectStyle@type}}' before loading this package}%
				}%
			}%
		}}\@do
	}%
}

\@ifpackagelater{caption}{2020/08/30}
	{\let\NewObjectStyle@IfCaptionNewEnough=\@firstoftwo}
	{\let\NewObjectStyle@IfCaptionNewEnough=\@secondoftwo}


\newcommand{\DeprecateStandardFloatObject}[2]{% #1: float environment, #2: alternative object environment
	\ifesf@warnstandardfloats
	\ifcsmacro{esf@#1@alternatives}{%
		\expandafter \appto \csname esf@#1@alternatives\endcsname{ or #2}%
	}{%
		\AtBeginDocument{%
			\csletcs{esf@original#1}{#1}%
			\expandafter \preto\csname#1\endcsname{\PackageWarning{easyfloats}{In order to profit from the advantages of the easyfloats package please use \csname esf@#1@alternatives\endcsname\space instead}}%
		}%
		\expandafter \def \csname esf@#1@alternatives\endcsname{#2}%
		\ifesf@loadgraphics
			\ifstrequal{#1}{table}{%
				% do nothing
			}{%
				\expandafter \appto \csname esf@#1@alternatives\endcsname{ or \string\includegraphicobject}%
			}%
		\fi
		\eappto\object@restorestandardfloats{\let\expandonce{\csname #1\endcsname}=\expandonce{\csname esf@original#1\endcsname}}%
	}%
	\fi
}


% ---------- default styles ----------

\NewObjectStyleGroup{all}{}

\NewObjectStyle{table}{type=table}
\NewObjectStyle{figure}{type=figure}


% ========== include graphic object ==========

\newif\ifgraphicobject@autocaption
\newif\ifgraphicobject@autolabel
\newif\ifgraphicobject@autocaption@strippath
\newif\ifgraphicobject@autolabel@strippath
\newif\ifgraphicobject@warnenv
\newif\ifgraphicobject@noenv

\ifesf@loadgraphics

\newcommand\object@graphic@env{figureobject}
\newcommand\object@graphic@path{/object}
\newcommand\object@graphic@options{}

\newrobustcmd{\graphicobjectstyle}[1]{%
	\IfObjectStyleNotGroup{#1}{%
		\def\object@graphic@env{#1object}%
	}{%
		\PackageError{easyfloats}{Undefined object style: '#1'}{}%
	}%
}

% do not confuse this with \CheckObjectGraphicOption
% this corresponds to \CheckObjectOption
\newcommand{\CheckGraphicobjectOption}[2]{% #1: key, #2: value, #3: executed if valid
	% if #3 is *not* executed an error message is printed
	\let\CheckGraphicobjectOption@handleArg=\@gobble
	\esf@ifpgfkeyexists{/graphicobject/#1}{%
		\esf@ifpgfkeyrequiresvalue{/graphicobject/#1}{%
			\ifx \pgfkeysnovalue #2\relax
				\PackageError{easyfloats}{The key '#1' requires a value. I am going to ignore this key}{}%
			\else
				\let\CheckGraphicobjectOption@handleArg=\@firstofone
			\fi
		}{%
			\let\CheckGraphicobjectOption@handleArg=\@firstofone
		}%
	}{\esf@ifkeyvalkeyexists{Gin}{#1}{%
		\esf@ifkeyvalkeyrequiresvalue{Gin}{#1}{%
			\ifx \pgfkeysnovalue #2\relax
				\PackageError{easyfloats}{The key '#1' requires a value. I am going to ignore this key}{}%
			\else
				\let\CheckGraphicobjectOption@handleArg=\@firstofone
			\fi
		}{%
			\let\CheckGraphicobjectOption@handleArg=\@firstofone
		}%
	}{%
		\PackageError{easyfloats}{'#1' is not a valid graphic option key}
			{If you intended to use a graphbox option please make sure that you loaded this package with the option 'graphbox'.}%
	}}%
	\CheckGraphicobjectOption@handleArg
}

\newcommand{\AppendGraphicobjectOption}[2]{% #1: key, #2: value
	\AppendToOptionsList{\object@graphic@options}{#1}{#2}%
}
\newcommand{\includegraphicobject@SetGraphicobjectOption}[2]{% #1: key, #2: value
	\pgfkeysalso{#1={#2}}%
}

% available options
\pgfqkeys{/graphicobject}{%
	auto caption/.is if=graphicobject@autocaption,
	auto caption=true,
	%
	auto label/.is if=graphicobject@autolabel,
	auto label=true,
	%
	auto caption strip path/.is if=graphicobject@autocaption@strippath,
	auto caption strip path=false,
	%
	auto label strip path/.is if=graphicobject@autolabel@strippath,
	auto label strip path=false,
	%
	warn env/.is if=graphicobject@warnenv,
	warn env=true,
	%
	no env/.is if=graphicobject@noenv,
	no env=true,
}

\newrobustcmd{\includegraphicobject}{% [#1: graphicstyle], [#2: options], #3: graphic filename without extension
	\let\includegraphicobject@after=\@empty
	\@ifnextchar [%
		\includegraphicobject@firstarg
		\includegraphicobject@parseoptions
}
\def\includegraphicobject@firstarg[#1]{%
	\begingroup
	\@ifnextchar [%
	{%
		\begingroup
		\let\includegraphicobject@after=\endgroup
		\graphicobjectstyle{#1}%
		\includegraphicobject@parseoptions
	}%
	{%
		\includegraphicobject@parseoptions[#1]%
	}%
}

\newcommand{\includegraphicobject@parseoptions}{% [#1: options], #2: graphic filename without extension
	% expanding \object@graphic@env before is important for subcaptionbox
	% otherwise I get the error \begin{subobject} ended by \end{subobject}
	\@ifnextchar [
		{\expandafter\includegraphicobject@do\expandafter{\object@graphic@env}}
		{\expandafter\includegraphicobject@do\expandafter{\object@graphic@env}[]}%
}
\def\includegraphicobject@do#1[#2]#3{% #1: environment name, #2: options, #3: graphic filename without extension
	\global\object@isgraphictrue
	\begin{#1}{%
			/utils/exec=\def\object@graphic@options@tmp{}%
				\let\AppendGraphicobjectOption\includegraphicobject@SetGraphicobjectOption,
			.unknown/.code={%
				\esf@ifkeyvalkeyexists{Gin}{\pgfkeyscurrentname}{%
					\AppendToOptionsList{\object@graphic@options@tmp}{\pgfkeyscurrentname}{##1}%
				}{%
					\let\pgfkeys@searchalso@name=\pgfkeyscurrentname
					\pgfkeysalso{%
						/graphicobject/\pgfkeys@searchalso@name={##1},
					}%
				}%
			},
			.esf@also from=\object@graphic@options,
			#2,
			/utils/exec=
				\ifgraphicobject@autocaption
					\esf@ifpgfkeyhasvalue{\object@graphic@path/caption}{%
						% do nothing
					}{%
						\def\graphicobject@caption{#3}%
						\PatchUnderscore\graphicobject@caption
						\ifgraphicobject@autocaption@strippath
							\protected@edef\graphicobject@caption{\strippath\graphicobject@caption}%
						\fi
						\pgfkeyslet{\object@graphic@path/caption}{\graphicobject@caption}%
					}%
				\fi
				\ifgraphicobject@autolabel
					\esf@ifpgfkeyhasvalue{\object@graphic@path/label}{%
						% do nothing
					}{%
						\def\graphicobject@label{#3}%
						\ifgraphicobject@autolabel@strippath
							\protected@edef\graphicobject@label{\strippath\graphicobject@label}%
						\fi
						\pgfkeyslet{\object@graphic@path/label}{\graphicobject@label}%
					}%
				\fi
				\ifgraphicobject@warnenv
					\pgfkeysalso{env/.get=\esf@tmp@env}%
					\ifx \esf@tmp@env \@empty
					\else
						\ifdefstring{\object@graphic@env}{subobject}
							{\edef\esf@tmp@name{\string\includegraphicsubobject}}
							{\edef\esf@tmp@name{\string\includegraphicobject}}%
						\ifgraphicobject@noenv
							\PackageWarning{easyfloats}{I am ignoring 'env=\esf@tmp@env' in \esf@tmp@name}%
						\else
							\PackageWarning{easyfloats}{'env=\esf@tmp@env' in \esf@tmp@name. Is that intended?}%
						\fi
					\fi
				\fi
				\ifgraphicobject@noenv
					\pgfkeysalso{env=}%
				\fi
			,
		}
		\expandafter\includegraphics\expandafter[\object@graphic@options@tmp]{#3}%
	\end{#1}%
	% if this is \@empty it should not interfere with \ignorespacesafterend (\ignorespaces) or \@endparenv (\@doendpe)
	\includegraphicobject@after
}

\newrobustcmd{\includegraphicsubobject}{%
	\begingroup
	\def\object@graphic@env{subobject}%
	\def\object@graphic@path{/subobject}%
	\def\includegraphicobject@after{\endgroup\ignorespaces}%
	\includegraphicobject@parseoptions
}


\fi %\ifesf@loadgraphics


% ========== sub object ==========

\newif\ifsubobject@warnnolabel
\newif\ifsubobject@warnnocaption

% available options
\pgfqkeys{/subobject}{%
	% A label for the left/right part of the content
	label/.initial,
	label/.value required,
	%
	% A caption displayed above or below the sub content
	% (depending on the float style).
	% There is *no* dot added automatically behind it.
	caption/.initial,
	caption/.value required,
	%
	% A caption used in the list of figures/tables/...
	% for the left/right part of the content.
	list caption/.initial,
	list caption/.value required,
	%
	% An addition to the caption (at the object only, not in a list)
	details/.initial=,
	details/.value required,
	%
	details sep/.initial=.\space,
	details sep/.value required,
	%
	% TeX code which is inserted before env
	exec/.initial=,
	exec/.value required,
	exec +/.code=\esf@pgfkeysvalueappend{/subobject/exec}{#1},
	exec +/.value required,
	exec+/.forward to=/subobject/exec +,
	exec+/.value required,
	%
	% The width argument of \subcaptionbox or subfigure.
	% For \subcaptionbox an empty value is allowed
	% meaning that the argument is not passed so that
	% \subcaptionbox takes the width of the content.
	linewidth/.initial=.5\linewidth,
	linewidth/.value required,
	%
	% Use the \subcaptionbox command.
	% Aligns the captions.
	% Does not allow verbatim content.
	% Is *not* compatible with env=tabularx.
	subcaptionbox/.code=
		\let\subobject@begin\subobject@subcaptionbox
		\let\subobject@end\endsubobject@subcaptionbox,
	subcaptionbox/.value forbidden,
	% Use the subfigure/subtable environment.
	% Requires linewidth.
	subpage/.code=
		\let\subobject@begin\subobject@subpage
		\let\subobject@end\endsubobject@subpage,
	subpage/.value forbidden,
	%
	% ------- subcaptionbox options -------
	% Horizontal position of the content in the box.
	% Allowed values: c, l, r, s, <empty> or any
	% justification defined with \DeclareCaptionJustification
	% (see the caption package documentation).
	% An empty value means that this optional argument is
	% *not* passed to the \subcaptionbox command.
	% This option has no effect if linewidth is empty.
	% I discourage using this option because it destroys
	% the alignment of (sub)object and (sub)caption.
	subcaptionbox inner pos/.initial=,
	subcaptionbox inner pos/.value required,
	%
	% ------- subfigure/subtable options -------
	% The subfigure/subtable environments are minipages
	% and take the same arguments.
	% http://tug.org/texinfohtml/latex2e.html#minipage
	%
	% The vertical position of the minipage on the baseline.
	% Allowed values: c, t, b, auto, <empty>.
	% auto means t if the caption is displayed at the top
	% or b if the caption is displayed at the bottom
	% so that the captions are aligned if the minipages
	% have the same height.
	% <empty> is equivalent to c.
	subpage outer pos/.initial=auto,
	subpage outer pos/.value required,
	%
	% The height of the minipage.
	% An empty value means that this optional argument is
	% *not* passed to the subfigure/subtable environment.
	subpage height/.initial=,
	subpage height/.value required=,
	%
	% The vertical position of the content on the minipage.
	% Allowed values: t, c, b, s, <empty>.
	% <empty> means that this optional argument is
	% *not* passed to the subfigure/subtable environment.
	% This option has no effect if subpage height is <empty>.
	subpage inner pos/.initial=,
	subpage inner pos/.value required,
	%
	% TeX code which is inserted at the beginning of the subfigure/subtable environment
	subpage align/.initial=\centering,
	subpage align/.value required,
	%
	% ------- separator options -------
	% Insert a separator in front of each subobject
	% except for the first subobject.
	sep/.initial=,
	sep/.value required,
	%
	% Place the subobjects side by side.
	hor/.code=
		\esf@pgfkeyscopyvalue{/subobject/sep}{/subobject/hor sep}%
		\esf@pgfkeysvalueappend{/subobject/sep}{#1},
	hor/.default=,
	%
	% The separator to be used when the subobjects
	% shall be placed side by side.
	hor sep/.initial=,
	hor sep/.value required,
	hor sep+/.forward to=/subobject/hor sep +,
	hor sep+/.value required,
	hor sep +/.code=\esf@pgfkeysvalueappend{/subobject/hor sep}{#1},
	hor sep +/.value required,
	%
	% Place the subobjects below each other.
	ver/.code=
		\esf@pgfkeyscopyvalue{/subobject/sep}{/subobject/ver sep}%
		\esf@pgfkeysvalueappend{/subobject/sep}{#1},
	ver/.default=,
	%
	% The separator to be used when the subobjects
	% shall be placed below eachother.
	ver sep/.initial=\par\bigskip,
	ver sep/.value required,
	ver sep+/.forward to=/subobject/ver sep +,
	ver sep+/.value required,
	ver sep +/.code=\esf@pgfkeysvalueappend{/subobject/ver sep}{#1},
	ver sep +/.value required,
	%
	%
	% ------- warnings -------
	%
	warn no caption/.is if=subobject@warnnocaption,
	warn no caption=true,
	%
	warn no label/.is if=subobject@warnnolabel,
	warn no label=false,
	%
	warn other env/.forward to=/object/warn other env,
	%value is allowed but not necessary
	%
	%
	% ------- debugging -------
	%
	show env args/.forward to=/object/show env args,
	%
	%
	% ------- inner environment -------
	%
	% An additional inner environment to wrap the content in like tabular, tabularx, tikzpicture
	env/.code=\IfEnvironmentExistsOrIsEmpty{#1}{%
		\ifstrequal{#1}{longtable}{%
			\PackageError{easyfloats}{You cannot use 'env=longtable' in a subobject}
				{Either use 'env=tabular' instead or combine the subobjects into one longtable.}%
			\pgfkeyssetvalue{/subobject/env}{tabular}%
		}{%
			\pgfkeyssetvalue{/subobject/env}{#1}%
		}%
	}{%
		\PackageError{easyfloats}{Environment #1 which you passed to the key 'env' does not exist}{}%
	},
	env/.value required,
	env=,
	%
	% ------- unknown handler / env args -------
	%
	% see /object
	%
	.unknown/.code={%
		% Using \ObjectProcessKeyPattern instead of \ObjectProcessArgs
		% means that keys like `graphic width` can be used in subobjects as well.
		% For a directly used subobject this should not be allowed.
		% But I am leaving it in for \includegraphicsubobject so that the same key
		% which works in \objectset and \includegraphicobject also works there.
		\ObjectProcessKeyPattern{\pgfkeyscurrentname}{#1}{%
			\pgfkeysgetvalue{/subobject/.really unknown/.@cmd}{\esf@tmp@err}%
			\esf@tmp@err#1\pgfeov
		}%
	},
	% Setting an unknown key handler shall not override env args but set an additional unknown handler.
	.unknown/.code/.code={%
		\pgfkeysdef{/subobject/.really unknown}{#1}%
	},
	.really unknown/.code={%
		\edef\do{\noexpand\pgfkeys{/errors/unknown key={\pgfkeyscurrentkey}{\unexpanded{#1}}}}%
		\do
	},
}%

\newenvironment{subobject}[1]{% #1: options
	\if@inobject
	\else
		\PackageError{easyfloats}{subobject environment may not be used outside of an object}{Did you mean to use the object environment instead?}%
	\fi
	%
	\subobject@hook
	\ifobject@isgraphic
		\object@graphic@hook
		\global\object@isgraphicfalse
	\fi
	%
	\let\label=\esf@original@label
	\let\caption=\esf@original@caption
	%
	% ------- options -------
	%
	\renewcommand\@object@envkey{/subobject/env}%
	\pgfqkeys{/subobject}{#1}%
	\ifsubobject@warnnocaption
		\pgfkeys{/subobject/caption/.esf@recommended}%
	\fi
	\ifsubobject@warnnolabel
		\pgfkeys{/subobject/label/.esf@recommended}%
	\fi
	%
	\esf@ifpgfkeyhasvalue{/subobject/list caption}%
		{\pgfkeys{/subobject/list caption/.get=\subobject@caption@short}}%
		{\pgfkeys{/subobject/caption/.get=\subobject@caption@short}}%
	\pgfkeysgetvalue{/subobject/linewidth}{\subobject@linewidth}%
	%
	\pgfkeysgetvalue{/subobject/details}{\subobject@details}%
	\ifx \subobject@details \@empty
		% do nothing
	\else
		\preto\subobject@details{\pgfkeysvalueof{/subobject/details sep}}%
	\fi
	%
	\pgfkeysgetvalue{/subobject/env}{\esf@tmp@env}%
	\ifx \esf@tmp@env \@empty
		\def\subobject@innerenv@begin{\ignorespaces}%
		\def\subobject@innerenv@end{\unskip}%
	\else
		\edef\subobject@innerenv@begin{\expandonce{\csname\esf@tmp@env\endcsname}}%
		\edef\subobject@innerenv@end{\expandonce{\csname end\esf@tmp@env\endcsname}}%
		\ObjectAppendEnvargs{\subobject@innerenv@begin}%
		\ifobject@showenv
			\show\subobject@innerenv@begin
		\fi
	\fi
	%
	% ------- execute -------
	%
	\if@firstsubobject
	\else
		% No need to worry about \parskip.
		% Inside of a floating environment it appears to be always zero.
		\pgfkeysvalueof{/subobject/sep}%
	\fi
	\subobject@begin
}{%
	\subobject@end
	\global \@firstsubobjectfalse
}

\NewEnviron{subobject@subcaptionbox}{%
	\def\subobject@box{%
		\subcaptionbox[% list caption
			\subobject@caption@short
		]{% caption
			\pgfkeysvalueof{/subobject/caption}%
			\subobject@details
			\esf@ifpgfkeyhasvalue{/subobject/label}
				{\label{\pgfkeysvalueof{/subobject/label}}}
				{}%
		}%
	}%
	\ifdefvoid\subobject@linewidth{%
		% do nothing
	}{%
		\appto\subobject@box{[\subobject@linewidth]}%
		\pgfkeysgetvalue{/subobject/subcaptionbox inner pos}{\subobject@innerpos}%
		\ifx \subobject@innerpos \@empty
			% do nothing
		\else
			\eappto\subobject@box{[\subobject@innerpos]}%
		\fi
	}%
	\subobject@box{% content
		\begingroup
		\renewcommand{\label}[1]{\PackageError{easyfloats}{\string\label\space is not allowed in subobject. Please use the 'label' option instead}{}}%
		\renewcommand{\caption}[2][]{\PackageError{easyfloats}{\string\caption\space is not allowed in subobject. Please use the 'caption' option instead}{}}%
		\pgfkeysvalueof{/subobject/exec}%
		\subobject@innerenv@begin
		\BODY
		\subobject@innerenv@end
		\endgroup
	}%
}

\newenvironment{subobject@subpage}{%
	\ifx \subobject@linewidth \@empty
		\PackageError{easyfloats}{'linewidth' may not be empty when using the 'subpage' backend}{}%
	\fi
	\edef\subobject@subpage@envname{sub\pgfkeysvalueof{/object/type}}%
	\def\subobject@subpage@begin{\csname\subobject@subpage@envname\endcsname}%
	\def\subobject@subpage@end{\csname end\subobject@subpage@envname\endcsname}%
	%
	\pgfkeysgetvalue{/subobject/subpage outer pos}{\subobject@outerpos}%
	\ifx \subobject@outerpos \@empty
		\appto\subobject@subpage@begin{[c]}%
	\else \ifdefstring \subobject@outerpos {auto}{%
		\caption@iftop
			{\eappto\subobject@subpage@begin{[t]}}%
			{\eappto\subobject@subpage@begin{[b]}}%
	}{\ifdefstring \subobject@outerpos {Auto}{%
		\caption@iftop
			{\eappto\subobject@subpage@begin{[T]}}%
			{\eappto\subobject@subpage@begin{[B]}}%
	}{%
		\eappto\subobject@subpage@begin{[\subobject@outerpos]}%
	}}\fi
	\pgfkeysgetvalue{/subobject/subpage height}{\subobject@height}%
	\ifx \subobject@height \@empty
	\else
		\appto\subobject@subpage@begin{[\subobject@height]}%
		\pgfkeysgetvalue{/subobject/subpage inner pos}{\subobject@innerpos}%
		\ifx \subobject@innerpos \@empty
		\else
			\eappto\subobject@subpage@begin{[\subobject@innerpos]}%
		\fi
	\fi
	%
	\subobject@subpage@begin{\subobject@linewidth}%
		\pgfkeysvalueof{/subobject/subpage align}%
		% \caption@iftop is defined by caption package
		\caption@iftop{\subobject@subpage@captionAndLabel}{}%
		\begingroup
		\renewcommand{\label}[1]{\PackageError{easyfloats}{\string\label\space is not allowed in subobject. Please use the 'label' option instead}{}}%
		\renewcommand{\caption}[2][]{\PackageError{easyfloats}{\string\caption\space is not allowed in subobject. Please use the 'caption' option instead}{}}%
		\pgfkeysvalueof{/subobject/exec}%
		\subobject@innerenv@begin
}{%
		\subobject@innerenv@end
		\endgroup
		\caption@iftop{}{\subobject@subpage@captionAndLabel}%
	\subobject@subpage@end
	\ignorespacesafterend
}
\newcommand{\subobject@subpage@captionAndLabel}{%
	% ------- caption -------
	% position of caption (above/below) is handled by float package (see float style)
	\esf@ifpgfkeyhasvalue{/subobject/caption}{%
		\caption[\subobject@caption@short]{\pgfkeysvalueof{/subobject/caption}\subobject@details}%
	}{%
		% Generate an anchor for a \label.
		\phantomcaption
	}%
	% ------- label -------
	\esf@ifpgfkeyhasvalue{/subobject/label}%
		{\label{\pgfkeysvalueof{/subobject/label}}}%
		{}%
}

\pgfqkeys{/subobject}{subpage}
