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

Reply via email to