%D \module
%D   [       file=pack-obj,
%D        version=1998.01.15,
%D          title=\CONTEXT\ Packaging Macros,
%D       subtitle=Objects,
%D         author=Hans Hagen,
%D           date=\currentdate,
%D      copyright={PRAGMA / Hans Hagen \& Ton Otten}]
%C
%C This module is part of the \CONTEXT\ macro||package and is
%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
%C details.

\writestatus{loading}{ConTeXt Packaging Macros / Objects}

\unprotect

\let\objectreference\gobblefourarguments % catch mkii tuo stuff

\registerctxluafile{pack-obj}{1.001}

% \startluacode
% local texbox, texdimen, texcount, texwrite = tex.box, tex.dimen, tex.count, tex.write
% local pdfxform, pdfrefxform = pdf.xform, pdf.refxform
%
% function pdf.xform   (l) texbox["objectbox"] = nil return l end
% function pdf.refxform(l) return node.copy_list(l) end
%
% backends.codeinjections.register = pdf.xform
% backends.codeinjections.restore  = pdf.refxform
%
% local codeinjections = backends.codeinjections
%
% objects = objects or { }
%
% local data = { }
%
% objects.data = data
% objects.n    = 0
%
% function objects.register(name)
%     objects.n = objects.n + 1
%     local list = texbox.objectbox
%     nodes.process_page(list)
%     data[name] = {
%         codeinjections.restore(list),
%         texdimen.objectwd,
%         texdimen.objectht,
%         texdimen.objectdp,
%         texdimen.objectoff,
%     }
% end
%
% function objects.restore(name)
%     local d = data[name]
%     if d then
%         texbox  .objectbox = codeinjections.restore(d[1])
%         texdimen.objectwd  = d[2]
%         texdimen.objectht  = d[3]
%         texdimen.objectdp  = d[4]
%         texdimen.objectoff = d[5]
%     else
%         texbox  .objectbox = nil
%         texdimen.objectwd  = 0
%         texdimen.objectht  = 0
%         texdimen.objectdp  = 0
%         texdimen.objectoff = 0
%     end
% end
%
% function objects.reference(name)
%     local d = data[name]
%     texwrite((d and d[1]) or 0)
% end
%
% function objects.enhance(name)
%     local d = data[name]
%     if d then
%         d[6] = texcount.realpageno
%     end
% end
%
% function objects.page(name)
%     local d = data[name]
%     texwrite((d and d[6]) or texcount.realpageno)
% end
%
% function objects.doifelse(name)
%     commands.testcase(data[name])
% end
% \stopluacode
%
% \newbox  \objectbox
% \newtoks \everyobject
% \newif   \ifinobject
%
% \newdimen\objectoff  \def\objectmargin{\the\objectoff}
% \newdimen\objectwd   \def\objectwidth {\the\objectwd }
% \newdimen\objectht   \def\objectheight{\the\objectht }
% \newdimen\objectdp   \def\objectdepth {\the\objectdp }
%
% \def\objectoffset{1cm}
%
% \everyobject{\the\everyPDFxform}
%
% \let\doresetobjects\relax
%
% \def\setobject     #1#2{\begingroup\objectoff\objectoffset\inobjecttrue\the\everyobject\dowithnextbox{\dosetobject{#1}{#2}}}
% \def\settightobject#1#2{\begingroup\objectoff\zeropoint   \inobjecttrue\the\everyobject\dowithnextbox{\dosetobject{#1}{#2}}}
%
% \let\objectsetvbox\vbox  %\def\objectsetvbox{\ruledvbox}
% \let\objectgetvbox\vbox  %\def\objectgetvbox{\ruledvbox}
% \let\objectsethbox\hbox  %\def\objectsethbox{\ruledhbox}
% \let\objectgethbox\hbox  %\def\objectgethbox{\ruledhbox}
%
% \def\dosetobject#1#2%
%   {\objectwd\wd\nextbox
%    \objectht\ht\nextbox
%    \objectdp\dp\nextbox
%    \ifdim\objectoff=\zeropoint\relax
%      \setbox\objectbox\box\nextbox
%    \else
%      \setbox\objectbox\objectsetvbox spread 2\objectoff{\vss\objectsethbox spread 2\objectoff{\hss\flushnextbox\hss}\vss}%
%    \fi
%    \ctxlua{objects.register("#1::#2")}%
%    \endgroup}
%
% \def\getobject#1#2%
%   {\begingroup
%    \ctxlua{objects.restore("#1::#2")}%
%    \ifdim\objectoff=\zeropoint\relax \else
%      \setbox\objectbox\objectgetvbox to \dimexpr\objectht+\objectdp\relax
%        {\vss\objectgethbox to \objectwd{\hss\box\objectbox\hss}\vss}%
%      \wd\objectbox\objectwd
%      \ht\objectbox\objectht
%      \dp\objectbox\objectdp
%    \fi
%    \box\objectbox
%    \endgroup}
%
% \def\getpageobject#1#2%
%   {\begingroup
%    \ctxlua{objects.restore("#1::#2")}%
%    \ifdim\objectoff=\zeropoint\relax
%       \setbox\objectbox\objectgethbox{\ctxlatelua{objects.enhance("#1::#2")}\box\objectbox}
%    \else
%      \setbox\objectbox\objectgetvbox to \dimexpr\objectht+\objectdp\relax
%        {\vss\objectgethbox to \objectwd{\ctxlatelua{objects.enhance("#1::#2")}\hss\box\objectbox\hss}\vss}%
%      \wd\objectbox\objectwd
%      \ht\objectbox\objectht
%      \dp\objectbox\objectdp
%    \fi
%    \box\objectbox
%    \endgroup}
%
% \def\setobjectdirectly           #1#2{\ctxlua{objects.register("#1::#2")}}
% \def\getobjectdirectly           #1#2{\ctxlua{objects.restore ("#1::#2")}}
% \def\getobjectdimensions         #1#2{\ctxlua{objects.restore ("#1::#2")}}
% \def\doifobjectfoundelse         #1#2{\ctxlua{objects.doifelse("#1::#2")}}
% \def\doifobjectreferencefoundelse#1#2{\ctxlua{objects.doifelse("#1::#2")}}
%
% \let\objectreferenced\relax
% \let\driverreferenced\relax
%
% \def\doregisterobjectreference{\writestatus{objects}{obsolete: register object reference}\gobblethreearguments}
% \def\dooverloadobjectreference{\writestatus{objects}{obsolete: overload object reference}\gobblethreearguments}
% \def\dosetobjectreference     {\writestatus{objects}{obsolete: set      object reference}\gobblethreearguments}
% \def\dosetdriverreference     {\writestatus{objects}{obsolete: set      driver reference}\gobblethreearguments}
%
% \def\defaultobjectreference{0}
% \def\defaultobjectpage     {\realfolio}
%
% \def\dogetobjectreference    #1#2#3{\xdef#3{\ctxlua{objects.reference("#1::#2)}}}
% \def\dogetobjectreferencepage#1#2#3{\xdef#3{\ctxlua{objects.page("#1::#2))}}}
%
% \protect
%
% \starttext
%     test \setobject{a}{b}\ruledhbox{xxx}\getobject{a}{b} test
%     \vskip3cm
%     test \settightobject{a}{b}\ruledhbox{xxx}\getobject{a}{b} test
%     test \settightobject{a}{c}\ruledhbox{xxx}\getobject{a}{c} test
%     \dorecurse{5000}{test \setobject{a}{b}\ruledhbox{xxx}\getobject{a}{b} test }
% \stoptext

%D \macros
%D   {setobject,getobject,ifinobject}
%D
%D Boxes can be considered reuable objects. Unfortunaltely once
%D passed to the \DVI\ file, such objects cannot be reused. In
%D \PDF\ however, reusing is possible and sometimes even a
%D necessity. Therefore, \CONTEXT\ supports reusable objects.
%D
%D During the \TEX\ processing run, boxes can serve the purpose
%D of objects, and the \DVI\ driver module implements objects
%D using packed boxes.
%D
%D The \PDF\ and \PDFTEX\ driver modules implement objects
%D using \PDF\ forms. There is no (real) restriction on the
%D number of objects there.
%D
%D The first application of objects in \CONTEXT\ concerned
%D \METAPOST\ graphics and fill||in form fields. The first
%D application can save lots of bytes, while the latter use is
%D more a necessity than byte saving.
%D
%D \starttyping
%D \setobject{class}{name}\somebox{}
%D \getobject{class}{name}
%D \stoptyping
%D
%D Here \type{\somebox} can be whatever box specification suits
%D \TEX. We save the dimensions of an object, although some
%D drivers will do so themselves. This means that when for
%D instance using \PDFTEX\ we could save a hash entry plus some
%D 20+ memory locations per object by delegating this
%D housekeeping to the driver. The current approach permits
%D us to keep the box characteristic too.

\newif\ifinobject

\def\objectplaceholder{NOT YET FLUSHED}%

\def\presetobject#1#2% \global added
  {\ifcsname\r!object#1::#2\endcsname\else
     \global\@EA\let\csname\r!object#1::#2\endcsname\objectplaceholder
   \fi}

\def\dosetobject#1#2#3% \initializepaper this will move to \everyshipout
  {% \initializepaper
   \ifcsname\r!object#2::#3\endcsname
     \expandafter\gobblefivearguments
   \else % tzt, overload internal referenced objects to save entries
     \expandafter\dodosetobject
   \fi
   {#1}{#2}{#3}}

\def\resetobject#1#2%
  {\letbeundefined{\r!object#1::#2}}

%D \macros
%D   {finalizeobjectbox}
%D
%D This one provides a hook for last minute object box processing
%D we need this in \MKIV.

\ifx\finalizeobjectbox\undefined
    \let\finalizeobjectbox\gobbleoneargument
\fi

%D Somehow there is a rounding error problem in either \PDFTEX\
%D or in viewers, or maybe it is conforming the specs. The next
%D variable compensate for it by removing the rather tight
%D clip.

\def\objectoffset{1cm}

\def\dodosetobject#1#2#3%
  {\bgroup
   \globalpushmacro\crossreferenceobject \objectreferenced
   \inobjecttrue
   \dowithnextbox
     {\globalpopmacro\crossreferenceobject
      \dododosetobject{#1}{#2}{#3}\egroup}}

\def\dododosetobject#1#2#3%
  {\begingroup
   \dontshowcomposition % rather fuzzy in \setxvalue ... \hbox
   \scratchdimen\objectoffset
   \@EA\xdef\csname\r!object#2::#3\endcsname
     {\noexpand\dohandleobject{#2}{#3}%
        {\ifhbox\nextbox\hbox\else\vbox\fi}%
        {\number\nextboxwd}{\number\nextboxht}{\number\nextboxdp}%
        {\number\scratchdimen}}%
   \expanded % freeze the dimensions since \dostartobject may use \nextbox
     {\dostartobject{#2}{#3}{\the\nextboxwd}{\the\nextboxht}{\the\nextboxdp}}%
   \ifcase#1\relax\else \ifdim\objectoffset>\zeropoint
     \setbox\nextbox\vbox spread 2\scratchdimen
       {\forgetall \offinterlineskip
        \vss\hbox spread 2\scratchdimen{\hss\flushnextbox\hss}\vss}%
   \fi \fi
   \flushnextbox
   \dostopobject
   \endgroup}

\def\getobject#1#2%
  {\begingroup
   \let\dohandleobject\dogetobject
   \csname\r!object#1::#2\endcsname}

\def\dogetobject#1#2#3#4#5#6#7% don't change this, should work for dvi & pdf
  {% \initializepaper
   \forgetall
   \dontshowcomposition
   \setbox\scratchbox\vbox
     {\doinsertobject{#1}{#2}}%
   \setbox\scratchbox#3%
     {\vbox to #5\scaledpoint
        {\ifdim\ht\scratchbox>#5\scaledpoint
           \vss\hbox to #4\scaledpoint{\hss\box\scratchbox\hss}\vss
         \else\ifdim\wd\scratchbox>#4\scaledpoint
           \vss\hbox to #4\scaledpoint{\hss\box\scratchbox\hss}\vss
         \else
          %\vss\box\scratchbox
           \vss\hbox to #4\scaledpoint{\box\scratchbox\hss}% fix Chof
         \fi\fi}}%
   \box\scratchbox
   \endgroup}

%D If needed one can ask for the dimensions of an object with:
%D
%D \starttyping
%D \getobjectdimensions{class}{name}
%D \stoptyping
%D
%D The results are reported in \type {\objectwidth}, \type
%D {\objectheight} and \type {\objectdepth}.

\def\dogetobjectdimensions#1#2#3#4#5#6#7%
  {\def\objectwidth {#4\s!sp}%
   \def\objectheight{#5\s!sp}%
   \def\objectdepth {#6\s!sp}%
   \def\objectmargin{#7\s!sp}}

\def\getobjectdimensions#1#2%
  {\let\dohandleobject\dogetobjectdimensions
   \let\objectwidth \!!zeropoint
   \let\objectheight\!!zeropoint
   \let\objectdepth \!!zeropoint
   \csname\r!object#1::#2\endcsname}

%D Apart from this kind of objects, that have typeset content,
%D we can have low level driver specific objects. Both types
%D can have references to internal representations, hidden for
%D the user. We keep track of such references by means of a
%D dedicated cross reference mechanism. Normally, objects are
%D defined before they are used, but forward referencing
%D sometimes occurs.
%D
%D \starttyping
%D \dosetobjectreference {class} {identifier} {reference value} {page}
%D \dogetobjectreference {class} {identifier} \csname
%D \stoptyping
%D
%D These commands are to be called by the \type{\startobject},
%D \type{\stopobject} and \type{\insertobject} specials.

\def\objectreferenced{\global\chardef\crossreferenceobject\plusone}
\def\driverreferenced{\global\chardef\crossreferenceobject\zerocount}

\objectreferenced

% no undefined test ! ! ! ! (pdftex fails on undefined objects)

\def\doregisterobjectreference#1#2#3{\normalexpanded{\noexpand\ctxlatelua{jobobjects.save("#1::#2",#3,\noexpand\the\realpageno)}}}
\def\dooverloadobjectreference#1#2#3{\ctxlua{jobobjects.set("#1::#2",#3,\the\realpageno)}}

\def\dosetobjectreference
  {\ifcase\crossreferenceobject
     \objectreferenced
     \expandafter\dooverloadobjectreference
   \else
     \expandafter\doregisterobjectreference
   \fi}

\def\dosetdriverreference
  {\driverreferenced\dosetobjectreference}

\def\defaultobjectreference#1#2{0} % driver dependent
\def\defaultobjectpage     #1#2{\realfolio}

\def\dogetobjectreference    #1#2#3{\xdef#3{\ctxlua{jobobjects.number("#1::#2","\defaultobjectreference{#1}{#2}")}}}
\def\dogetobjectreferencepage#1#2#3{\xdef#3{\ctxlua{jobobjects.page("#1::#2","\defaultobjectpage{#1}{#2}")}}}

\def\setobject     {\driverreferenced\dosetobject1}
\def\settightobject{\driverreferenced\dosetobject0}

%D \macros
%D   {doifobjectfoundelse,doifobjectreferencefoundelse}
%D
%D To prevent redundant definition of objects, one can use
%D the next tests:
%D
%D \starttyping
%D \doifobjectfoundelse{class}{object}{do then}{do else}
%D \doifobjectreferencefoundelse{class}{object}{do then}{do else}
%D \stoptyping

\def\doifobjectfoundelse#1#2%
  {\ifcsname\r!object#1::#2\endcsname
     \expandafter\firstoftwoarguments
   \else
     \expandafter\secondoftwoarguments
   \fi}

\def\doifobjectreferencefoundelse#1#2{\ctxlua{jobobjects.doifelse("#1::#2")}}

\protect \endinput
