Hi
I've bashed out an initial implementation of Lua-based imposition plans.
It's not hooked up to the main podofoimpose code yet, but so far it
looks to be embarrassingly simple. I really only have to export one
function to the Lua interpreter (to register a new imposition record)
and push a couple of environment variables in to the initial execution
context.
I'll attach the code - which hasn't been built and will need fixes since
I've done a less comprehensive reading of the Lua docs than I should've
- to give you a vague idea what's involved.
The Plan class is very close to the existing PageRecord class; I
should've just separated that out instead.
I should have a working patch plus some sample plans over the next few
days, though that depends a lot on just how busy those next few days are.
This is just code intended as a guide for how it'll look. It can replace
the parser and expression evaluation stuff in podofoimpose entirely.
Additionally, it'd be possible to use a SAX parser to load imposition
plans from XML rather trivially, too, since the Plan structure is
entirely separate from any podofoimpose implementation details. This
should make people who want to write GUI planners in other languages and
use the podofoimpose executable as a backend happy.
Like where I'm going?
--
Craig Ringer
/*
* Embed the Lua interpreter
*/
extern "C" {
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
}
#include <iostream>
#include <stdexcept>
#include <string>
#include "plan.h"
namespace PoDoFo { namespace Impose {
/* Functions we export to the Lua interpreter; defined in lua_func.cpp */
extern int addrec(lua_State* L);
/**
* Initialize the Lua interpreter for use.
*/
LuaInterpreter::LuaInterpreter()
{
int error;
/* Init the Lua interpreter */
L = lua_open();
if (!L)
{
throw std::exception("Whoops! Failed to open lua!");
}
/* Init the Lua libraries we want users to have access to.
* Note that the `os' and `io' libraries MUST NOT be included,
* as providing access to those libraries to the user would
* make running plan files unsafe. */
error = luaopen_base(L);
if (error)
{
lua_close(L);
throw std::exception("Whoops! Failed top init lua base libs.");
}
error = luaopen_table(L);
if (error)
{
lua_close(L);
throw std::exception("Whoops! Failed top init lua table libs.");
}
error = luaopen_string(L);
if (error)
{
lua_close(L);
throw std::exception("Whoops! Failed top init lua string libs.");
}
error = luaopen_math(L);
if (error)
{
lua_close(L);
throw std::exception("Whoops! Failed top init lua math libs.");
}
error = initFunctions();
if (error)
{
lua_close(L);
throw std::exception("Whoops! Failed to init custom lua functions.");
}
}
LuaInterpreter::~LuaInterpreter()
{
lua_close(L);
}
void LuaInterpreter::initFunctions()
{
/* Register the `addrec' function used to add a new imposition record. */
/* Needs a better name. */
lua_pushcfunction(&addrec);
lua_setglobal(L, "addrec");
}
/**
* Set the current plan being operated on by any Lua code that calls our
* plan manipulation functions.
*/
void LuaInterpreter::setPlan(Plan* plan)
{
/* TODO */
}
Plan* LuaInterpreter::getPlan()
{
/* TODO */
}
double LuaInterpreter::getNumber(const std::string& name)
{
lua_getglobal(L, name.c_str());
if (!lua_isnumber(L, -1))
throw std::exception("Requested variable is non-number");
double d = lua_tonumber(L, -1);
lua_pop(L, 1);
return d;
}
bool LuaInterpreter::getBoolean(const std::string& name)
{
lua_getglobal(L, name.c_str());
if (!lua_isboolean(L, -1))
throw std::exception("Requested variable is not boolean");
bool b = lua_toboolean(L, -1);
lua_pop(L, 1);
return b;
}
std::string LuaInterpreter::getString(const std::string& name)
{
lua_getglobal(L, name.c_str());
if (!lua_isstring(L, -1))
throw std::exception("Requested variable is not a string");
size_t len;
const char * str = lua_tolstring(L, -1, &len);
std::string s(str, len);
lua_pop(L, 1);
return s;
}
void LuaInterpreter::setNumber(const std::string& name, double value)
{
lua_pushnumber(L, value);
lua_setglobal(L, name.c_str()); /* pops stack */
}
void LuaInterpreter::setBoolean(const std::string& name, bool value)
{
lua_pushboolean(L, value);
lua_setglobal(L, name.c_str()); /* pops stack */
}
void LuaInterpreter::setString(const std::string& name, const std::string& value)
{
lua_pushlstring(L, value.data(), value.size());
lua_setglobal(L, name.c_str()); /* pops stack */
}
Plan * LuaInterpreter::getPlan()
{
lua_getglobal(L, "plan");
if (!lua_islightuserdata(L, -1))
throw std::exception("Value of plan variable is not userdata");
Plan * p = static_cast<Plan*>(lua_touserdata(L,-1));
lua_pop(L, 1);
return p;
}
void LuaInterpreter::setPlan(Plan * p)
{
lua_pushlightuserdata(L, static_cast<void*>(p));
lua_setglobal(L, "plan"); /* pops stack */
}
/**
* Run a script in this Lua interpreter context.
*/
void LuaInterpreter::runScript(std::istream& script)
{
static const int BUFF_SIZE = 255;
char buff[BUFF_SIZE];
int error;
/* Get the exception mask for the stream so we can restore it,
* and set it to throw on all exceptions other than EOF.
*/
const std::ios::iostate mask = script.exceptions();
script.exceptions( std::ios::failbit | std::ios::badbit )
/* Then load and execute the plan script, which will act on the
* plan object we provided to this interpreter. */
while (!script.eof())
{
script.read(&buff, BUFF_SIZE);
error = luaL_loadbuffer(L, buff, strlen(buff), "line") ||
lua_pcall(L, 0, 0, 0);
if (error) {
const std::string errmsg ("Lua error: ");
errmsg += lua_tostring(l, -1);
throw std::exception(errmsg);
}
}
script.exceptions(mask);
}
Plan load_plan (const std::istream& planfile, int pageCount, double sourceWidth, double sourceHeight) {
Plan p;
LuaInterpreter l;
l.setNumber("PageCount", pageCount);
l.setNumber("SourceWidth", sourceWidth);
l.setNumber("SourceHeight", sourceHeight);
l.setPlan(p);
l.runScript(planfile);
return p;
}
}; }; /* End namespace PoDoFo::Impose */
/*
* Lua interpreter used for loading PoDoFoImpose plans,
* and a couple of convenience functions for its use.
*/
#include <istream>
#include <string>
struct lua_State;
namespace PoDoFo { namespace Impose {
class Plan;
/**
* Object to manage a Lua interpreter context through C++ lifetime/scope rules
* (RAII), including running scripts and managing the interpreter environment.
* Defined in lua.cpp.
*
* Includes some PoDoFoImpose specific functions in the interpreter, defined in
* lua_funcs.cpp.
*/
class LuaInterpreter
{
lua_State* L;
void initFunctions();
public:
LuaInterpreter();
~LuaInterpreter();
/** Execute the contents of the passed stream as a Lua script
* in the context of the current intrepreter. */
void runScript(std::istream& script);
/** Get the value of the named global from the Lua environment */
double getNumber(const std::string& name);
/** Set a global to the passed value */
void setNumber(const std::string& name, double value);
/** Get the value of the named global from the Lua environment */
std::string getString(const std::string& name);
/** Set a global to the passed value */
void setString(const std::string& name, const std::string& value);
/** Get the value of the named global from the Lua environment */
bool getBoolean(const std::string& name);
/** Set a global to the passed value */
void setBoolean(const std::string& name, bool value);
/** Set the plan scripts run in this interpreter should modify */
void setPlan(Plan* plan);
/** Obtain the current plan any scripts run in the interpreter will modify */
Plan* plan();
};
/**
* Initialize a new Lua interpreter context and a new empty plan,
* then populate the plan using the passed Lua script. If all goes
* well, return the plan.
*
* Values for the PageCount, SourceWidth and SourceHeight globals must be
* passed so they can be correctly set in the interpreter.
*/
Plan load_plan (const std::istream& planfile, int pageCount, double sourceWidth, double sourceHeight);
}; };
#include <plan.h>
namespace PoDoFo { namespace Impose {
/**
* Virtual dtor for Plan structure to ensure vtable creation
*/
Plan::~Plan()
{
}
}; };
/*
* plan.cpp provides pure C++ methods for the creation of an imposition plan.
* It doesn't know about looping, expression evaluation, variables, or anything
* clever like that.
*
* A program that embeds podofoimpose may use these functions to define its own
* imposition plan without going through the intermediary Lua-based plan language,
* by doing its own plan calculations and assembling a list of imposition records
* into a Plan object.
*/
namespace PoDoFo { namespace Impose {
/**
* An imposition plan, composed of a list of imposition records.
*/
class Plan {
public:
/**
* An individual imposition action within the plan.
*/
struct Record {
/*** source page */
int sPage;
/*** destination page */
int dPage;
/*** rotation (degrees clockwise) of page. IS THIS CORRECT? */
double rotation;
/*** Horizontal translation (units? axis direction?) for page. */
double xTrans;
/*** Vertical translation (units? axis direction?) for page. */
double yTrans;
Record(int sPage, int dPage, double rotation, double xTrans, double yTrans);
Record();
~Record() { }
bool isValid() const;
};
private:
double m_width;
double m_height;
double m_scale;
std::vector<const Record> m_records;
public:
/* TODO: default to A4 plan */
Plan(double width = 0, double height = 0, double scale = 1.0) : m_width(width), m_height(height), m_scale(scale), m_records() { }
virtual ~Plan();
int appendRecord( const Record& rec );
const std::vector<const Record> & recordList() const;
double width() const { return m_width; )
void setWidth(double width);
double height() const { return m_height; }
void setHeight(double height);
};
/**
* Construct a new Record with user-specified parameters.
*/
inline Plan::Record::Record(int sPage, int dPage, double rotation, double xTrans, double yTrans)
: sPage(sPage), dPage(dPage), rotation(rotation), xTrans(xTrans), yTrans(yTrans)
{
/* Validate me! */
}
/**
* Construct a default, invalid record.
*/
inline Plan::Record::Record()
: sPage(0), dPage(0), rotation(0), xTranslate(0), yTranslate(0)
{
}
/**
* Return whether or not the record appears to be valid.
*/
inline bool Plan::Record::isValid() {
return sPage > 0 && dPage > 0;
}
/**
* Add a record to the imposition plan.
*/
inline int Plan::appendRecord( const Plan::Record& rec)
{
m_records.push_back(rec);
}
/**
* Obtain a read-only reference to the list of records that
* compose this imposition plan. This reference remains valid
* until the Plan object is destroyed.
*
* \warning Copy this to a new vector if you want to modify it,
* or do not control the lifetime of the Plan object.
*/
inline const std::vector<const Plan::Record> & Plan::recordList() const
{
return m_records;
}
inline void Plan::setWidth(double width)
{
if (width <= 0)
throw std::exception("Width must be >= 0");
m_width = width;
}
inline void Plan::setHeight(double height)
{
if (height <= 0)
throw std::exception("Height must be >= 0");
m_height = height;
}
}; }; /* end namespace PoDoFo::Impose */
/**
* Implement the Lua functions required to manipulate imposition plans.
*/
#include <string>
namespace PoDoFo { namespace Impose {
/**
* Add a record, with arguments:
*
* srcPage, dstPage, rotation, xtrans, ytrans
*
* Lua will take care of all argument expression evaluation etc for us.
*/
int addrec(lua_State *L)
{
/* TODO: check stack for space! */
if (! (lua_isnumber(L, 1) &&
lua_isnumber(L, 2) &&
lua_isnumber(L, 3) &&
lua_isnumber(L, 4) &&
lua_isnumber(L, 5) ))
{
throw std::exception("One or more arguments to addrec were not numbers");
}
/* Get the plan in use from the Lua environment */
lua_getglobal(L, "plan");
if (! lua_islightuserdata(L, -1))
throw std::exception("plan env var wasn't valid user data");
Plan * p = static_cast<Plan*>( lua_touserdata(L, -1) );
lua_pop(L, 1);
/* and add a new record to it */
p.addRecord( Plan::Record(
lua_tonumber(L, 1),
lua_tonumber(L, 2),
lua_tonumber(L, 3),
lua_tonumber(L, 4),
lua_tonumber(L, 5)
));
lua_pop(5);
}
}; };
-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
Podofo-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/podofo-users