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:set(kind, value)
    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 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:viewMarked(view) -- return true/false
    p:setViewMarked(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)
    p:hasSnapping(layer)
    p:setSnapping(layer)
    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: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 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()

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

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

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

# Document

Documents have the following methods:

    doc = ipe.Document()         -- empty document
    doc = ipe.Document(filename) -- load from file
    -- returns either document or nil, error message, error code

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

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

    -- 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

    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 preamble created modified creator
    fullscreen cropbox numberpages 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 list of files in directory
    ipe.openFile(path, mode)      -- replacement for io.open
    beziers = ipe.splineToBeziers(spline, is_closed, old_style)
    ipelet = ipe.Ipelet(dllname)  -- loads C++ ipelet from absolute path
    -- returns ipelet or nil, error message