On Monday 22 September 2003 2:39 pm, Jean-Marc Lasgouttes wrote: > %% Do command #1 if file #2 exists, else print a warning > \newcommand{\DoOrWarn}[2]{ > \IfFileExists{#2}{#1{#2}}{\warnNotFound{#2}} > }
Guess what? I got greedy! I'm trying to write a design document describing how to implement 'modifiers' for InsetExternal. It's quite an education ;-) Ultimately, the problem really boils down to how do we create an appropriate external template syntax? Let's consider XFig output first. I would like to generate the LaTeX \rotatebox{45{\resizebox{2cm}{3cm}{\input{myfile.pstex_t}}} if the file exists and warn the user if it doesn't. Clearly, if the file does not exist, none of the above should be executed. In the case of output from InsetGraphics, the LaTeX is of the form \includegraphics[a bunch of options]{myfile.eps} Now, my first stab at a 'design document' describes how to create the modifiers, but it falls over totally when it comes to the external template syntax. I attach it anyway, just for fun. I guess stab 2 should concentrate on the syntax first ;-) Angus
#LyX 1.3 created this file. For more info see http://www.lyx.org/ \lyxformat 221 \textclass article \begin_preamble %% Turn off page numbering [EMAIL PROTECTED]@empty [EMAIL PROTECTED] \date{} \end_preamble \language english \inputencoding auto \fontscheme ae \graphics default \paperfontsize default \spacing single \papersize a4paper \paperpackage widemarginsa4 \use_geometry 0 \use_amsmath 0 \use_natbib 0 \use_numerical_citations 0 \paperorientation portrait \secnumdepth 3 \tocdepth 3 \paragraph_separation indent \defskip medskip \quotes_language english \quotes_times 2 \papercolumns 1 \papersides 1 \paperpagestyle default \layout Title Transforming \family typewriter InsetExternal \family default . Version 1 \layout Standard If we are to merge \family typewriter InsetGraphics \family default into \family typewriter InsetExternal \family default , some way must be found to handle the complex transformations that \family typewriter InsetGraphics \family default supports. Moreover, it would be nice to give the user the ability to manipulate an XFig \family typewriter InsetExternal \family default in the same sort of way. \layout Standard InsetGraphics makes use of the \family typewriter graphicx \family default LaTeX package, meaning that transformation data is passed to the \family typewriter \backslash insetgraphics \family default command as options. Here are the supported options (obviously, not all of them are output at the same time): \layout Standard \paragraph_spacing single \family typewriter \begin_inset Tabular <lyxtabular version="3" rows="1" columns="1"> <features> <column alignment="block" valignment="top" width="5in"> <row> <cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none"> \begin_inset Text \layout Standard \family typewriter [draft, \newline bb=23 45 321 345,clip, \newline scale=0.5,width=2cm,height=5cm,keepaspectratio, \newline angle=45,origin=B, \newline \SpecialChar \ldots{} ] \end_inset </cell> </row> </lyxtabular> \end_inset \newline \family default where \SpecialChar \ldots{} are any \begin_inset Quotes eld \end_inset special \begin_inset Quotes erd \end_inset options not supported by the GUI. \layout Standard Transformation of an XFig \family typewriter InsetExternal \family default would use the LaTeX commands \family typewriter \backslash rotatebox \family default and \family typewriter \backslash resizebox \family default , something like: \layout Standard \family typewriter \backslash rotatebox{45}{ \backslash resizebox{2cm}{3cm}{ \backslash include{my_figure.pstex_t}}} \layout Section The transformations as seen by \family typewriter InsetExternal \layout Standard I think that it is clear that the various possibilities are too complex to be handled entirely by the external template language and that some support within LyX itself is needed. Nonetheless, I think that this can all be handled in a fairly clean manner if we envisage Modifier classes (for rotation, resizing and clipping) to handle the output of the data. They would need three member functions, \family typewriter Modifier::before() \family default , \family typewriter Modifier::after() \family default and \family typewriter Modifier::option() \family default to fill a \family typewriter std::ostream \family default . Consider an InsetExternal class that has magically-instantiated member pointers \family typewriter latex_rotation \family default and \family typewriter latex_resize \family default . The class would appear something like this: \layout LyX-Code class InsetExternal { \layout LyX-Code boost::scoped_ptr<Rotation> latex_rotation; \layout LyX-Code boost::scoped_ptr<Resize> latex_resize; \layout LyX-Code \layout LyX-Code int latex(Buffer const & buf, ostream & os, \layout LyX-Code LatexRunParams const &) const \layout LyX-Code { \layout LyX-Code std::ostringstream os; \layout LyX-Code if (latex_rotation.get()) \layout LyX-Code os << latex_rotation->before(); \layout LyX-Code if (latex_resize.get()) \layout LyX-Code os << latex_resize->before(); \layout LyX-Code // write will insert the contents of Modifier::option (if any) \layout LyX-Code // in the correct place. \layout LyX-Code write("LaTeX", buf, os, external_in_tmpdir); \layout LyX-Code if (latex_resize.get()) \layout LyX-Code os << latex_resize->after(); \layout LyX-Code if (latex_rotation.get()) \layout LyX-Code os << latex_rotation->after(); \layout LyX-Code } \layout LyX-Code }; \layout Section An example transformation: implementing rotation \layout Standard Let us consider rotation in greater detail. In general, the transformation will require that the same basic data be output differently for LaTeX, LinuxDoc and DocBook. To this end, define a \family typewriter RotationData \family default class simply to handle the data. \layout LyX-Code class RotationData { \layout LyX-Code public: \layout LyX-Code enum OriginType { \layout LyX-Code DEFAULT, \layout LyX-Code TOP, \layout LyX-Code BOTTOM, \layout LyX-Code BASELINE \layout LyX-Code }; \layout LyX-Code \layout LyX-Code RotationData() : angle_(0), origin_(DEFAULT) {} \layout LyX-Code \layout LyX-Code void angle(double a) { angle_ = a; } \layout LyX-Code double angle() const { return angle_; } \layout LyX-Code \layout LyX-Code void origin(OriginType o) { origin_ = o; } \layout LyX-Code OriginType origin() const { return origin_; } \layout LyX-Code \layout LyX-Code private: \layout LyX-Code double angle_; \layout LyX-Code OriginType origin_; \layout LyX-Code }; \layout Standard \family typewriter InsetExternal \family default would have a \family typewriter RotationData \family default member variable and use modifier classes derived from a \family typewriter RotationOutput \family default base class to output data in the desired format. \layout LyX-Code class RotationOutput { \layout LyX-Code public: \layout LyX-Code RotationOutput(RotationData const & data) : data_(data) {} \layout LyX-Code \layout LyX-Code string const before(OutputType type) const { \layout LyX-Code return no_rotation() : string() ? before_impl(); \layout LyX-Code } \layout LyX-Code string const after() const { \layout LyX-Code return no_rotation() : string() ? after_impl(); \layout LyX-Code } \layout LyX-Code string const option() const { \layout LyX-Code return no_rotation() : string() ? option_impl(); \layout LyX-Code } \layout LyX-Code \layout LyX-Code protected: \layout LyX-Code RotationData const & data_; \layout LyX-Code \layout LyX-Code private: \layout LyX-Code bool no_rotation() const { return std::abs(data_.angle()) < 0.1; } \layout LyX-Code virtual string const before_impl() const { return string(); } \layout LyX-Code virtual string const after_impl()() const { return string(); } \layout LyX-Code virtual string const option_impl()() const { return string(); } \layout LyX-Code }; \layout Standard Different classes derived from \family typewriter Rotation \family default will produce different output. Consider the case of LaTeX output suitable for use as an option to the \family typewriter \backslash includegraphics \family default command: \layout LyX-Code class RotationLatexOption : public RotationOutput { \layout LyX-Code virtual string const option_impl()() const \layout LyX-Code { \layout LyX-Code std::ostringstream ss; \layout LyX-Code ss << "angle=" << data_.angle(); \layout LyX-Code if (data_.origin() == DEFAULT) \layout LyX-Code return ss.str(); \layout LyX-Code \layout LyX-Code ss << ",origin="; \layout LyX-Code switch (data_.option()) { \layout LyX-Code case DEFAULT: \layout LyX-Code break; \layout LyX-Code case TOP: \layout LyX-Code ss << 't'; \layout LyX-Code break; \layout LyX-Code case BOTTOM: \layout LyX-Code ss << 'b'; \layout LyX-Code break; \layout LyX-Code case BASELINE: \layout LyX-Code ss << 'B'; \layout LyX-Code break; \layout LyX-Code } \layout LyX-Code return ss.str(); \layout LyX-Code } \layout LyX-Code }; \layout Standard An XFig \family typewriter InsetExternal \family default , by contrast, would wish to make use of a class that overloads the \family typewriter before_impl \family default and \family typewriter after_impl \family default member functions: \layout LyX-Code class RotationLatexCommand : public Rotation { \layout LyX-Code virtual string const before_impl() const \layout LyX-Code { \layout LyX-Code std::ostringstream ss; \layout LyX-Code ss << " \backslash \backslash rotatebox{" << data_.angle() << "}{"; \layout LyX-Code return ss.str(); \layout LyX-Code } \layout LyX-Code virtual string const option_impl()() const \layout LyX-Code { \layout LyX-Code return "}"; \layout LyX-Code } \layout LyX-Code }; \layout Section Modifying the external template language \layout Standard In order for any of this to become reality, the template definitions stored in \family typewriter external_templates \family default must be able to tell LyX what modifiers a particular template can support and what class to instantiate to realise them. \layout LyX-Code Template XFig \layout LyX-Code GuiName "XFig: $$Basename" \layout LyX-Code InputFormat fig \layout LyX-Code AutomaticProduction true \layout LyX-Code \color red PossibleModifiers rotate, resize \layout LyX-Code Format LaTeX \layout LyX-Code \color red Modifier rotate RotationLatexCommand \layout LyX-Code \color red Modifier resize ResizeLatexCommand \layout LyX-Code Product " \backslash \backslash input{$$Basename.pstex_t}" \layout LyX-Code UpdateFormat pstex \layout LyX-Code UpdateResult "$$Basename.pstex_t" \layout LyX-Code Requirement "graphicx" \layout LyX-Code FormatEnd \layout Standard The downside of this proposal is that use of the modifiers is implicit rather than explicit. There is nothing saying that modifier options should be inserted between square brackets immediately after \family typewriter \backslash input \family default . Nor is there anything to control the position of the modifier commands. \layout Standard That this is unsatisfactory is illustrated by the slightly more sophisticated LaTeX output \layout LyX-Code Product " \backslash \backslash executeIfFound{ \backslash \backslash input}{$$Basename.pstex_t}" \layout Standard where \family typewriter \backslash executeIfFound \family default is a LaTeX macro \layout LyX-Code \backslash newcommand*{ \backslash executeIfFound}[2]{ \layout LyX-Code \backslash IfFileExists{#2}{#1{#2}}{ \backslash warnNotFound{#2}} \layout LyX-Code } \layout Standard In this particular case, we would like to generate LaTeX output such as \layout LyX-Code \backslash rotatebox{45}{ \backslash resizebox{2cm}{3cm}{ \backslash input{myfile.pstex_t}}} \layout Standard if the file exists and warn the user if it does not. Clearly, the \family typewriter \backslash executeIfFound \family default macro is unable to handle such a case\SpecialChar \ldots{} \layout LyX-Code \layout LyX-Code \the_end