Ipelib
Lua bindings for Document, Page, and StyleSheet

Stylesheet

A stylesheet can be constructed by reading from a file (in XML format), or by creating an empty stylesheet:

-- create from file:
-- returns sheet or nil, error message
sheet = ipe.Sheet("filename")
-- create from XML data:
-- returns sheet or nil, error message
sheet = ipe.Sheet(nil, "<ipestyle> ... </ipestyle>")
-- create empty stylesheet:
sheet = ipe.Sheet()    

Stylesheets have the following methods:

sheet:clone()            -- returns a private copy of the stylesheet
sheet:xml(with_bitmaps)  -- return XML representation 
sheet:add(kind, symbolic, value)
sheet:addFrom(other_sheet, kind, symbolic)
sheet:remove(kind, symbolic)
sheet:allNames(kind)
sheet:find(kind, name)
sheet:setAttribute(kind, name, absolute)
sheet:set(kind, value)  -- only for preamble, linejoin, linecap, fillrule
sheet:isStandard()      -- is this the built-in stylesheet?
sheet:name()            -- name of the stylesheet
sheet:setName(name)

sheet:add adds a definition for symbolic name to the stylesheet. Permissible values for kind are:

 pen, symbolsize, arrowsize, color, dashstyle, textsize, textstretch, 
 textstyle, gridsize, anglesize, opacity, symbol, 

sheet:addFrom copies a definition for a gradient, tiling, or effect from another stylesheet. This makes it possible to create such definitions by creating a temporary stylesheet from an XML string.

sheet:remove removes the definition of a symbolic name from the stylesheet. This also works for symbols, gradients, tilings, and effects. It is permissible to remove a symbolic name that is not defined in the stylesheet.

sheet:set sets a value. Permissible values for kind are:

 preamble, linecap, linejoin, fillrule

Stylesheet cascade

Documents own an entire stack of style sheets, the style sheet cascade. An empty style sheet cascade can be created like this:

sheets = ipe.Sheets()

or you can obtain one from an Ipe document.

Style sheet cascades have the following methods:

sheets1 = sheets:clone()              -- make a private copy
sheets:allNames(kind)
sheets:find(kind, symbolic)
sheets:has(kind, symbolic)
sheets:count()                        -- return number of sheets
sheets:sheet(index)                   -- return sheet at index
sheets:insert(index, style_sheet)     -- insert sheet at index
sheets:remove(index)                  -- delete a sheet
log = sheets:update(directory)        -- update style sheets from directory

sheets:allNames returns a table with all symbolic names defined in the entire stylesheet cascade. Permissible values for kind are:

 pen, symbolsize, arrowsize, color, dashstyle, textsize, textstretch, 
 textstyle, gridsize, anglesize, opacity, tiling, symbol, gradient, effect

sheet:has checks whether a symbolic name is defined in the cascade. Permissible values for kind are as for allNames.

sheet:find looks up a symbolic definition. Permissible values for kind are:

 pen, symbolsize, arrowsize, color, dashstyle, textsize, textstretch, 
 textstyle, gridsize, anglesize, opacity, symbol, 
 preamble, linecap, linejoin, fillrule, layout

Page

Page objects can be created like this:

p = ipe.Page()   -- create basic page with one layer and one view
p1 = p:clone()   -- returns a copy of the page

The following methods get and set the page style. The style name must be defined by a <pagestyle> element in a style sheet. Passing nil to setStyle clears the page style.

name = p:style()       -- return the page style name
p:setStyle(name)       -- use "normal" to reset

The following methods act on the views of a page. Note that views are indexed starting from 1, as usual in Lua.

p:countViews() 
p:effect(view)
p:setEffect(view, effect)
p:active(view)
p:setActive(view, layer)
p:insertView(view, layer)
p:removeView(view)
p:clearViews()
f = p:markedView(view) -- return true/false
p:setMarkedView(view, f)
name = p:viewName(view)
p:setViewName(view, name)
map = p:viewMap(view)
p:setViewMap(view, map)
lm = p:layerMatrices(view)
p:setLayerMatrices(view, lm)

The following methods act on the layers of a page. Note that layers are always accessed by name, not by index as in C++.

p:countLayers()
p:layers()        -- return table of layer names
p:isLocked(layer)
p:setLocked(layer, boolean)
p:layerData(layer)    -- free-use text for that layer
p:setLayerData(layer, data)
mode = p:snapping(layer)  -- "never", "visible", or "always"
p:setSnapping(layer, mode)
p:renameLayer(oldname, newname)
-- if layer is nil, automatically create name for new layer:
p:addLayer(layer)     -- add layer at the end, return name of new layer
p:removeLayer(layer)
p:reorderLayers(order) -- order is a table with layer names
p:moveLayer(layer, target_index)
p:visible(view, layer)   -- is layer visible in view?
p:setVisible(view, layer, boolean)

The objects of a page can be accessed by indexing, using an iterator, or with various methods. Note that object indices start with 1, as usual in Lua.

-- objects:

#p      -- return number of objects on page
p[i]    -- return object #i, where 1 <= i <= #p

-- iterate over objects of page:
for i, obj, sel, layer in p:objects() do
  print(i, obj, sel, layer)
end

p:select(objno)              -- returns nil, 1, or 2
p:setSelect(objno, status)   -- status is nil, 1, or 2
p:layerOf(objno)
p:setLayerOf(objno, layer)
p:visible(view, objno)       -- is object visible in view?
p:bbox(objno)                -- cached by page
p:invalidateBBox(objno)      -- invalidate cached bbox
p:insert(objno, object, select, layer)  -- objno == nil means append
p:remove(objno)
p:replace(objno, object)     -- automatically clones object
p:transform(objno, matrix)

-- returns distance from pos if it is less than bound
p:distance(objno, pos, bound)

-- returns true if attribute was actually changed
p:setAttribute(objno, property, value)

Finally, there are various methods to query and modify the currently selected objects, the page title and sections, etc.:

-- selection:
obj = p:primarySelection() -- nil if no primary selection
p:hasSelection()           -- true or false
p:deselectAll()
p:ensurePrimarySelection()

-- variant is optional (a variant name), and defaults to no variant
title, skipped = p:title(variant)    -- skipped: is page skipped in this variant?
p:setTitle(variant, title, skipped)  -- use variant == nil for no variant

-- t is a table with fields "section" and "subsection"
-- if section or subsection is not a string, the title is used
t = p:sections()
p:setSections(t)

-- n is a string
n = p:notes()
p:setNotes(n)

-- f is true/false
f = p:marked()
p:setMarked(f)

-- XML representation, what is "ipepage" or "ipeselection" (only the
-- selected objects)
xml = p:xml(what)

-- find an edge of the page's objects near pos in the view, for snapping
-- returns origin (a vector) and direction (an angle), or nothing
origin, dir = p:findEdge(view, pos)

Document

Documents have the following methods:

doc = ipe.Document()         -- empty document
doc = ipe.Document(filename) -- load from file
doc = ipe.Document(filename, true) -- load from file, ignoring too recent Ipe version
-- returns either document or nil, error message, error code

#doc    -- number of pages
doc[i]  -- return page #i, where 1 <= i <= #doc

copy = doc:clone() -- independent pages, stylesheets, and properties;
                   -- PDF resources are not copied: run LaTeX before saving as PDF

-- format == nil: guess from filename
-- flags is either nil, or a table with keys
-- "export", "nozip", "lastview", "nocolor"
-- if format == "xml", imageFolder can be omitted
doc:save(filename, format, flags, imageFolder)  
doc:exportPages(filename, flags, fromPage, toPage, imageFolder)
doc:exportView(filename, format, flags, pageNo, viewNo, imageFolder)

-- iterating over pages of document:
for i, p in doc:pages() do
  print("Page number",  i, p)
end

doc:set(no, page)         -- replace with a clone of page, returns old page
doc:insert(no, page)
doc:append(page)
doc:remove(no)            -- returns page and removes from document
doc:countTotalViews()
doc:sheets()              -- returns style sheet cascade
old = doc:replaceSheets(sheets)  -- replace and return old cascade
doc:has(what)  -- where what in { "truetype", "gradients", "tilings", "transparency" }
doc:runLatex(docname)  -- pass filename of Ipe document or nil
-- returns either true, nil, result code, logfile
--         or     false, error message, result code, logfile

-- Running Latex in the background, as done by doc:runLatex:
-- success is true or false; if false, errmsg and errcode describe the error;
-- converter is an opaque object to be passed to completeLatexRun
-- (errcode "notext" with success true means there is no text to convert)
success, converter, errmsg, errcode = doc:prepareLatexRun()
-- returns the command line to run (a string), or nil
command = doc:howToRunLatex(docname)  -- pass filename of Ipe document or nil
-- after running the command, load the result:
-- returns success, errmsg, errcode, logfile
success, errmsg, errcode, log = doc:completeLatexRun(converter)

doc:fontPool()    -- returns light userdata
doc:checkStyle()  -- returns list of undefined symbolic names
t = doc:properties()  -- returns table
doc:setProperties(t)  -- changes properties set in table

doc:properties() returns a table with the following fields (all fields exist even if their contents is an empty string):

title author subject keywords language preamble created modified creator
fullscreen cropbox numberpages sequentialtext tex

Other functions

ipe.fileFormat(filename)      -- returns one of "xml", "pdf", "eps", "ipe5", "unknown"
ipe.fileExists(filename)      -- returns true or false
ipe.realPath(filename)        -- convert relative path to absolute path
ipe.directory(path)           -- return table of files in directory
ipe.openFile(path, mode)      -- replacement for io.open handling Unicode on Windows
ipe.mkdir(path)               -- create directory; returns true, or nil, error message, error code
beziers = ipe.splineToBeziers(spline, is_closed, old_style)
ipelet = ipe.Ipelet(dllname)  -- loads C++ ipelet from absolute path
-- returns ipelet or nil, error message
-- run function number 'num' (starting from 1) of the ipelet;
-- returns true if the document was modified (so undo information is needed)
-- layer is the name of a layer, attributes and snap are the tables
-- described for the user interface, and helper is a table of methods
-- (message, messageBox, getString, and a table parameters) that implement the ipelet's user interface
ok = ipelet:run(num, page, doc, pageno, viewno, layer, attributes, snap, helper)

-- return the path of an Ipe folder, followed by the optional file name:
-- name is one of "lua", "icons", "doc", "styles", "ipelets", "scripts",
-- "user-styles", "user-ipelets", "user-scripts", "config", "latex"
-- (the "latex" folder is created if it does not exist)
path = ipe.folder(name)
path = ipe.folder(name, filename)