Created libraries from commonly used functions. Moved official/debug-helpers.lua into lib/dtutils/debug.lua. Created a logging library to provide various levels of logging. Documented all libraries and functions. Created tools to extract documentation in text, man page, and pdf formats.

This commit is contained in:
Bill Ferguson
2016-10-15 01:47:43 -04:00
parent e74b253f64
commit a254ac87f7
10 changed files with 2160 additions and 93 deletions

457
lib/dtutils.lua Normal file
View File

@@ -0,0 +1,457 @@
--[[
dtutils.lua - common darktable lua functions
Copyright (C) 2016 Bill Ferguson <wpferguson@gmail.com>.
Copyright (C) 2016 Tobias Jakobs
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
]]
local dtutils = {}
dtutils.libdoc = {
Sections = {"Name", "Synopsis", "Description", "See_Also", "License"},
Name = [[dtutils - A Darktable lua utilities library]],
Synopsis = [[local du = require "lib/dtutils"]],
Description = [[dtutils provides a common library of functions used to build
lua scripts. There are also sublibraries that provide more functions.]],
See_Also = [[dtutils.debug(3), dtutils.extensionts(3), dtutils.file(3), dtutils.processor(3)]],
License = [[This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.]],
functions = {}
}
local dt = require "darktable"
local log = require "lib/libLog"
dt.configuration.check_version(...,{3,0,0})
--[[
NAME
split - split a string on a specified separator
SYNOPSIS
local du = require "lib/dtutils"
local result = du.split(str, pat)
str - string - the string to split
pat - string - the pattern to split on
DESCRIPTION
split separates a string into a table of strings. The strings are separated at each
occurrence of the supplied pattern.
RETURN VALUE
result - a table of strings on success, or an empty table on error
EXAMPLE
split("/a/long/path/name/to/a/file.txt", "/") would return a table like
{"a", "long", "path", "name", "to", "a", "file.txt"}
]]
dtutils.libdoc.functions[#dtutils.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value", "Example"},
Name = [[split - split a string on a specified separator]],
Synopsis = [[local du = require "lib/dtutils"
local result = du.split(str, pat)
str - string - the string to split
pat - string - the pattern to split on]],
Description = [[split separates a string into a table of strings. The strings are separated at each
occurrence of the supplied pattern.]],
Return_Value = [[result - a table of strings on success, or an empty table on error]],
Example = [[split("/a/long/path/name/to/a/file.txt", "/") would return a table like
{"a", "long", "path", "name", "to", "a", "file.txt"}]],
}
-- Thanks to http://lua-users.org/wiki/SplitJoin
function dtutils.split(str, pat)
local t = {} -- NOTE: use {n = 0} in Lua-5.0
local fpat = "(.-)" .. pat
local last_end = 1
local s, e, cap = str:find(fpat, 1)
while s do
if s ~= 1 or cap ~= "" then
table.insert(t,cap)
end
last_end = e+1
s, e, cap = str:find(fpat, last_end)
end
if last_end <= #str then
cap = str:sub(last_end)
table.insert(t, cap)
end
return t
end
--[[
NAME
join - join a table of strings with a specified separator
SYNOPSIS
local du = require "lib/dtutils"
local result = du.join(tabl, pat)
tabl - a table of strings
pat - a separator
DESCRIPTION
join assembles a table of strings into a string with the specified pattern
in between each string
RETURN VALUE
result - the joined string on success, or an empty string on failure
EXAMPLE
join({"a", "long", "path", "name", "to", "a", "file.txt"}, " ") would return the string
"a long path name to a file.txt"
]]
dtutils.libdoc.functions[#dtutils.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value", "Example"},
Name = [[join - join a table of strings with a specified separator]],
Synopsis = [[local du = require "lib/dtutils"
local result = du.join(tabl, pat)
tabl - a table of strings
pat - a separator]],
Description = [[join assembles a table of strings into a string with the specified pattern
in between each string]],
Return_Value = [[result - the joined string on success, or an empty string on failure]],
Example = [[join({"a", "long", "path", "name", "to", "a", "file.txt"}, " ") would return the string
"a long path name to a file.txt"]],
}
-- Thanks to http://lua-users.org/wiki/SplitJoin
function dtutils.join(tabl, pat)
returnstr = ""
for i,str in pairs(tabl) do
returnstr = returnstr .. str .. pat
end
return string.sub(returnstr, 1, -(pat:len() + 1))
end
--[[
NAME
prequire - a protected lua require
SYNOPSIS
local du = require "lib/dtutils"
local result = du.prequire(req_name)
req_name - the filename of the lua code to load without the ".lua" filetype
DESCRIPTION
prequire is a protected require that can survive an error in the code being loaded without
bringing down the calling routine.
RETURN VALUE
result - the code or true on success, otherwise an error message
EXAMPLE
prequire("lib/dtutils.file") which would load lib/dtutils/file.lua
]]
dtutils.libdoc.functions[#dtutils.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value", "Example"},
Name = [[prequire - a protected lua require]],
Synopsis = [[local du = require "lib/dtutils"
local result = du.prequire(req_name)
req_name - the filename of the lua code to load without the ".lua" filetype]],
Description = [[prequire is a protected require that can survive an error in the code being loaded without
bringing down the calling routine.]],
Return_Value = [[result - the code or true on success, otherwise an error message]],
Example = [[prequire("lib/dtutils.file") which would load lib/dtutils/file.lua]],
}
function dtutils.prequire(req_name)
local status, lib = pcall(require, req_name)
if status then
log.msg(log.info, "Loaded " .. req_name)
else
log.msg(log.info, "Error loading " .. req_name)
end
return lib
end
--[[
NAME
push - push a value on a stack
SYNOPSIS
local du = require "lib/dtutils"
du.push(stack, value)
stack - table - a table being used as the stack
value - any type - the value to be put on the stack
DESCRIPTION
Push a value on a stack
RETURN VALUE
none
]]
dtutils.libdoc.functions[#dtutils.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value"},
Name = [[push - push a value on a stack]],
Synopsis = [[local du = require "lib/dtutils"
du.push(stack, value)
stack - table - a table being used as the stack
value - any type - the value to be put on the stack]],
Description = [[Push a value on a stack]],
Return_Value = [[none]],
}
dtutils.push = table.insert
--[[
NAME
pop - pop a value from a stack
SYNOPSIS
local du = require "lib/dtutils"
local result = du.pop(stack)
stack - table - a table being used as a stack
DESCRIPTION
Remove the last value pushed on the stack and return it
RETURN VALUE
result = nil if stack isn't a table or the stack is empty, otherwise the value removed from the stack
]]
dtutils.libdoc.functions[#dtutils.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value"},
Name = [[pop - pop a value from a stack]],
Synopsis = [[local du = require "lib/dtutils"
local result = du.pop(stack)
stack - table - a table being used as a stack]],
Description = [[Remove the last value pushed on the stack and return it]],
Return_Value = [[result = nil if stack isn't a table or the stack is empty, otherwise the value removed from the stack]],
}
dtutils.pop = table.remove
--[[
NAME
update_combobox_choices - change the list of choices in a combobox
SYNOPSIS
local du = require "lib/dtutils"
du.update_combobox_choices(combobox_widget, choice_table)
combobox_widget - lua_combobox - a combobox widget
choice_table - table - a table of strings for the combobox choices
DESCRIPTION
Set the combobox choices to the supplied list. Remove any extra choices from the end
RETURN VALUE
none
]]
dtutils.libdoc.functions[#dtutils.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value"},
Name = [[update_combobox_choices - change the list of choices in a combobox]],
Synopsis = [[local du = require "lib/dtutils"
du.update_combobox_choices(combobox_widget, choice_table)
combobox_widget - lua_combobox - a combobox widget
choice_table - table - a table of strings for the combobox choices]],
Description = [[Set the combobox choices to the supplied list. Remove any extra choices from the end]],
Return_Value = [[none]],
}
function dtutils.update_combobox_choices(combobox, choice_table)
local items = #combobox
local choices = #choice_table
for i, name in ipairs(choice_table) do
log.msg(log.debug, "Setting choice " .. i .. " to " .. name)
combobox[i] = name
end
if choices < items then
for j = items, choices + 1, -1 do
log.msg(log.debug, "Removing choice " .. j)
combobox[j] = nil
end
end
combobox.value = 1
end
--[[
NAME
spairs - an iterator that provides sorted pairs from a table
SYNOPSIS
local du = require "lib/dtutils"
for key, value in du.spairs(t, order) do
t - table - table of key, value pairs
order - function - an optional function to sort the pairs
if none is supplied, table.sort() is used
DESCIRPTION
spairs is an iterator that returns key, value pairs from a table in sorted
order. The sorting order is the result of table.sort() if no function is
supplied, otherwise sorting is done as specified in the function.
EXAMPLE
HighScore = { Robin = 8, Jon = 10, Max = 11 }
-- basic usage, just sort by the keys
for k,v in spairs(HighScore) do
print(k,v)
end
--> Jon 10
--> Max 11
--> Robin 8
-- this uses an custom sorting function ordering by score descending
for k,v in spairs(HighScore, function(t,a,b) return t[b] < t[a] end) do
print(k,v)
end
--> Max 11
--> Jon 10
--> Robin 8
REFERENCE
Code copied from http://stackoverflow.com/questions/15706270/sort-a-table-in-lua
]]
dtutils.libdoc.functions[#dtutils.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Example", "Reference"},
Name = [[spairs - an iterator that provides sorted pairs from a table]],
Synopsis = [[local du = require "lib/dtutils"
for key, value in du.spairs(t, order) do
t - table - table of key, value pairs
order - function - an optional function to sort the pairs
if none is supplied, table.sort() is used]],
Description = [[spairs is an iterator that returns key, value pairs from a table in sorted
order. The sorting order is the result of table.sort() if no function is
supplied, otherwise sorting is done as specified in the function.]],
Example = [[HighScore = { Robin = 8, Jon = 10, Max = 11 }
-- basic usage, just sort by the keys
for k,v in spairs(HighScore) do
print(k,v)
end
--> Jon 10
--> Max 11
--> Robin 8
-- this uses an custom sorting function ordering by score descending
for k,v in spairs(HighScore, function(t,a,b) return t[b] < t[a] end) do
print(k,v)
end
--> Max 11
--> Jon 10
--> Robin 8]],
Reference = [[Code copied from http://stackoverflow.com/questions/15706270/sort-a-table-in-lua]],
}
-- Sort a table
function dtutils.spairs(_table, order) -- Code copied from http://stackoverflow.com/questions/15706270/sort-a-table-in-lua
-- collect the keys
local keys = {}
for _key in pairs(_table) do keys[#keys + 1] = _key end
-- if order function given, sort by it by passing the table and keys a, b,
-- otherwise just sort the keys
if order then
table.sort(keys, function(a,b) return order(_table, a, b) end)
else
table.sort(keys)
end
-- return the iterator function
local i = 0
return function()
i = i + 1
if keys[i] then
return keys[i], _table[keys[i]]
end
end
end
--[[
NAME
urlencode - encode a string in a websage manner
SYNOPOIS
local du = require "lib/dtutils"
local result = du.urlencode(str)
str - string - the string that needs to be made websafe
DESCRIPTION
urlencode converts a string into a websafe version suitable for
use in a web browser.
RETURN_VALUE
result - string - a websafe string
REFERENCE
https://forums.coronalabs.com/topic/43048-remove-special-characters-from-string/
]]
dtutils.libdoc.functions[#dtutils.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value", "Reference"},
Name = [[urlencode - encode a string in a websage manner]],
Synopsis = [[local du = require "lib/dtutils"
local result = du.urlencode(str)
str - string - the string that needs to be made websafe]],
Description = [[urlencode converts a string into a websafe version suitable for
use in a web browser.]],
Return_Value = [[result - string - a websafe string]],
Reference = [[https://forums.coronalabs.com/topic/43048-remove-special-characters-from-string/]],
}
function dtutils.urlencode(str)
if (str) then
str = string.gsub (str, "\n", "\r\n")
str = string.gsub (str, "([^%w ])", function () return string.format ("%%%02X", string.byte()) end)
str = string.gsub (str, " ", "+")
end
return str
end
return dtutils

View File

@@ -0,0 +1,23 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-10-01 00:36-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: de_DE\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: dtutils.processor.lua:42
#, lua-format
msgid "Export Image %i/%i"
msgstr "Exportiere Bild %i/%i"

219
lib/dtutils/debug.lua Normal file
View File

@@ -0,0 +1,219 @@
--[[
This file is part of darktable,
copyright (c) 2014 Jérémy Rosen
darktable is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
darktable is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with darktable. If not, see <http://www.gnu.org/licenses/>.
]]
--[[
DEBUG HELPERS
A collection of helper functions to help debugging lua scripts.
require it as
dhelpers = require "official/debug-helpers"
Each function is documented in its own header
]]
local dt = require "darktable"
local io = require "io"
local table = require "table"
require "darktable.debug"
local log = require "lib/libLog"
local M = {} -- The actual content of the module
M.libdoc = {
Sections = {"Name", "Synopsis", "Description", "License"},
Name = [[dtutils.debug - debugging helpers used in developing darktable lua scripts]],
Synopsis = [[local dd = require "lib/dtutils.debug"]],
Description = [[dtutils.debug provides an interface to the darktable debugging routines.]],
License = [[This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.]],
functions = {}
}
--[[
NAME
tracepoint - print out a tracepoint and dump the arguments
SYNOPSIS
local dd = require "lib/dtutils.debug"
local result = tracepoint(name, ...)
name - string - the name of the tracepoint to print out
... - arguments - variables to dump the contents of
DESCRIPTION
tracepoint prints its name and dumps its parameters using
dt.debug
RETURN VALUE
result - ... - the supplied argument list
]]
M.libdoc.functions[#M.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value"},
Name = [[tracepoint - print out a tracepoint and dump the arguments]],
Synopsis = [[local dd = require "lib/dtutils.debug"
local result = tracepoint(name, ...)
name - string - the name of the tracepoint to print out
... - arguments - variables to dump the contents of]],
Description = [[tracepoint prints its name and dumps its parameters using
dt.debug]],
Return_Value = [[result - ... - the supplied argument list]],
}
function M.tracepoint(name,...)
log.always(4, "***** "..name.." ****")
params = {...}
print(dt.debug.dump(params,"parameters"))
return ...;
end
--[[
NAME
new_tracepoint - create a function returning a tracepoint
SYNOPSIS
local dd = require "lib/dtutils.debug"
local result = new_tracepoint(name, ...)
name - string - the name of the tracepoint to print out
... - arguments - variables to dump the contents of
DESCRIPTION
A function that returns a tracepoint function with the given name
This is mainly used to debug callbacks.
RETURN VALUE
result - function - a function that returns the result of a tracepoint
EXAMPLE
register_event(event, dd.new_tracepoint("hit callback"))
will print the following each time the callback is called
**** hit callback ****
<all the callback's parameters dumped>
]]
M.libdoc.functions[#M.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value", "Example"},
Name = [[new_tracepoint - create a function returning a tracepoint]],
Synopsis = [[local dd = require "lib/dtutils.debug"
local result = new_tracepoint(name, ...)
name - string - the name of the tracepoint to print out
... - arguments - variables to dump the contents of]],
Description = [[A function that returns a tracepoint function with the given name
This is mainly used to debug callbacks.]],
Return_Value = [[result - function - a function that returns the result of a tracepoint]],
Example = [[register_event(event, dd.new_tracepoint("hit callback"))
will print the following each time the callback is called
**** hit callback ****
<all the callback's parameters dumped>]],
}
function M.new_tracepoint(name)
return function(...) return M.tracepoint(name,...) end
end
--[[
NAME
dprint - pass a variable to dt.debug.dump and print the results to stdout
SYNOPSIS
local dd = require "lib/dtutils.debug"
dd.dprint(var)
var - variable - any variable that you want to see the contents of
DESCRIPTION
Wrapper around debug.dump, will directly print to stdout,
same calling convention
]]
M.libdoc.functions[#M.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description"},
Name = [[dprint - pass a variable to dt.debug.dump and print the results to stdout]],
Synopsis = [[local dd = require "lib/dtutils.debug"
dd.dprint(var)
var - variable - any variable that you want to see the contents of]],
Description = [[Wrapper around debug.dump, will directly print to stdout,
same calling convention]],
}
function M.dprint(...)
log.always(4, dt.debug.dump(...))
end
--[[
NAME
terse_dump - set dt.debug.known to shorten all image dumps to a single line
SYNOPSIS
local dd = require "lib/dtutils.debug"
dd.terse_dump()
DESCRIPTION
terse_dump sets dt.debug.known to shorten all images to a single line.
If you don't need to debug the content of images, this will avoid them flooding your logs
]]
M.libdoc.functions[#M.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description"},
Name = [[terse_dump - set dt.debug.known to shorten all image dumps to a single line]],
Synopsis = [[local dd = require "lib/dtutils.debug"
dd.terse_dump()]],
Description = [[terse_dump sets dt.debug.known to shorten all images to a single line.
If you don't need to debug the content of images, this will avoid them flooding your logs]],
}
function M.terse_dump()
for _,v in ipairs(dt.database) do
dt.debug.known[v] = tostring(v)
end
end
return M
-- vim: shiftwidth=2 expandtab tabstop=2 cindent
-- kate: tab-indents: off; indent-width 2; replace-tabs on; remove-trailing-space on;

185
lib/dtutils/extensions.lua Normal file
View File

@@ -0,0 +1,185 @@
--[[
NAME
dtutils/extensions.lua - a library of extenstions to the lua libraries
SYNOPSIS
require "lib/dtutils.extensions"
DESCRIPTION
This library contains extensions to the lua libraries.
]]
local dtutils_extensions = {}
dtutils_extensions.libdoc = {
Sections = {"Name", "Synopsis", "Description", "License"},
Name = "dtutils.extensions - a library of extenstions to the lua libraries",
Synopsis = [[require "lib/dtutils.extensions"]],
Description = [[This library contains extensions to the lua libraries.]],
License = [[This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.]],
functions = {}
}
--[[
NAME
string.strip_accents - strip accents from characters
SYNOPSIS
require "lib/dtutils.extensions"
result = string.strip_accents(str)
str - string - the string with characters that need accents removed
DESCRIPTION
strip_accents removes accents from accented characters returning the
unaccented character.
RETURN VALUE
result - string - the string containing unaccented characters
]]
dtutils_extensions.libdoc.functions[#dtutils_extensions.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value"},
Name = [[string.strip_accents - strip accents from characters]],
Synopsis = [[require "lib/dtutils.extensions"
result = string.strip_accents(str)
str - string - the string with characters that need accents removed]],
Description = [[strip_accents removes accents from accented characters returning the
unaccented character.]],
Return_Value = [[result - string - the string containing unaccented characters]],
}
-- Strip accents from a string
-- Copied from https://forums.coronalabs.com/topic/43048-remove-special-characters-from-string/
function string.strip_accents( str )
local tableAccents = {}
tableAccents["à"] = "a"
tableAccents["á"] = "a"
tableAccents["â"] = "a"
tableAccents["ã"] = "a"
tableAccents["ä"] = "a"
tableAccents["ç"] = "c"
tableAccents["è"] = "e"
tableAccents["é"] = "e"
tableAccents["ê"] = "e"
tableAccents["ë"] = "e"
tableAccents["ì"] = "i"
tableAccents["í"] = "i"
tableAccents["î"] = "i"
tableAccents["ï"] = "i"
tableAccents["ñ"] = "n"
tableAccents["ò"] = "o"
tableAccents["ó"] = "o"
tableAccents["ô"] = "o"
tableAccents["õ"] = "o"
tableAccents["ö"] = "o"
tableAccents["ù"] = "u"
tableAccents["ú"] = "u"
tableAccents["û"] = "u"
tableAccents["ü"] = "u"
tableAccents["ý"] = "y"
tableAccents["ÿ"] = "y"
tableAccents["À"] = "A"
tableAccents["Á"] = "A"
tableAccents["Â"] = "A"
tableAccents["Ã"] = "A"
tableAccents["Ä"] = "A"
tableAccents["Ç"] = "C"
tableAccents["È"] = "E"
tableAccents["É"] = "E"
tableAccents["Ê"] = "E"
tableAccents["Ë"] = "E"
tableAccents["Ì"] = "I"
tableAccents["Í"] = "I"
tableAccents["Î"] = "I"
tableAccents["Ï"] = "I"
tableAccents["Ñ"] = "N"
tableAccents["Ò"] = "O"
tableAccents["Ó"] = "O"
tableAccents["Ô"] = "O"
tableAccents["Õ"] = "O"
tableAccents["Ö"] = "O"
tableAccents["Ù"] = "U"
tableAccents["Ú"] = "U"
tableAccents["Û"] = "U"
tableAccents["Ü"] = "U"
tableAccents["Ý"] = "Y"
local normalizedString = ""
for strChar in string.gmatch(str, "([%z\1-\127\194-\244][\128-\191]*)") do
if tableAccents[strChar] ~= nil then
normalizedString = normalizedString..tableAccents[strChar]
else
normalizedString = normalizedString..strChar
end
end
return normalizedString
end
--[[
NAME
string.escape_xml_characters - escape characters for xml documents
SYNOPSIS
require "lib/dtutils.extensions"
result = string.escape_xml_characters(str)
str - string - the string that needs escaped
DESCRIPTION
escape_xml_characters provides the escape sequences for
"&", '"', "'", "<", and ">" with the corresponding "&amp;",
"&quot;", "&apos;", "&lt;", and "&gt;".
RETURNN VALUE
result - string - the string containing escapes for the xml characters
]]
dtutils_extensions.libdoc.functions[#dtutils_extensions.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value"},
Name = "string.escape_xml_characters - escape characters for xml documents",
Synopsis = [[ require "lib/dtutils.extensions"
result = string.escape_xml_characters(str)
str - string - the string that needs escaped]],
Description = [[escape_xml_characters provides the escape sequences for
"&", '"', "'", "<", and ">" with the corresponding "&amp;",
"&quot;", "&apos;", "&lt;", and "&gt;".]],
Return_Value = [[result - string - the string containing escapes for the xml characters]],
}
-- Escape XML characters
-- Keep &amp; first, otherwise it will double escape other characters
-- https://stackoverflow.com/questions/1091945/what-characters-do-i-need-to-escape-in-xml-documents
function string.escape_xml_characters( str )
str = string.gsub(str,"&", "&amp;")
str = string.gsub(str,"\"", "&quot;")
str = string.gsub(str,"'", "&apos;")
str = string.gsub(str,"<", "&lt;")
str = string.gsub(str,">", "&gt;")
return str
end
return dtutils_extensions

523
lib/dtutils/file.lua Normal file
View File

@@ -0,0 +1,523 @@
--[[
dtutils/file.lua - common darktable lua file functions
Copyright (C) 2016 Bill Ferguson <wpferguson@gmail.com>.
Copyright (C) 2016 Tobias Jakobs
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
]]
local dtutils_file = {}
dtutils_file.libdoc = {
Sections = {"Name", "Synopsis", "Description", "License"},
Name = [[dtutils.file - common darktable lua file functions]],
Synopsis = [[local df = require "lib/dtutils.file"]],
Description = [[dtutils.file provides common file manipulation functions used in
constructing Darktable lua scripts]],
License = [[This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.]],
functions = {}
}
local dt = require "darktable"
local log = require "lib/libLog"
local gettext = dt.gettext
dt.configuration.check_version(...,{3,0,0})
-- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("dtutils.file",dt.configuration.config_dir.."/lua/locale/")
local function _(msgid)
return gettext.dgettext("dtutils.file", msgid)
end
--[[
NAME
check_if_bin_exists - check if an executable is in the path
SYNOPSIS
local df = require "lib/dtutils.file"
local result = df.check_if_bin_exists(bin)
bin - string - the binary to check for
DESCRIPTION
check_if_bin_exists checks to see if the specified binary executable is
in the path.
RETURN VALUE
result - boolean - true if the executable was found, false if not
]]
dtutils_file.libdoc.functions[#dtutils_file.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value"},
Name = [[check_if_bin_exists - check if an executable is in the path]],
Synopsis = [[local df = require "lib/dtutils.file"
local result = df.check_if_bin_exists(bin)
bin - string - the binary to check for]],
Description = [[check_if_bin_exists checks to see if the specified binary executable is
in the path.]],
Return_Value = [[result - boolean - true if the executable was found, false if not]],
}
function dtutils_file.check_if_bin_exists(bin)
local result = os.execute("which " .. bin)
if not result then
result = false
end
return result
end
--[[
NAME
split_filepath - split a filepath into parts
SYNOPSIS
local df = require "lib/dtutils.file"
local result = df.split_filepath(filepath)
filepath - string - path and filename
DESCRIPTION
split_filepath splits a filepath into the path, filename, basename and filetype and puts
that in a table
RETURN VALUE
result - table - a table containing the path, filename, basename, and filetype
]]
dtutils_file.libdoc.functions[#dtutils_file.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value"},
Name = [[split_filepath - split a filepath into parts]],
Synopsis = [[local df = require "lib/dtutils.file"
local result = df.split_filepath(filepath)
filepath - string - path and filename]],
Description = [[split_filepath splits a filepath into the path, filename, basename and filetype and puts
that in a table]],
Return_Value = [[result - table - a table containing the path, filename, basename, and filetype]],
}
function dtutils_file.split_filepath(str)
local result = {}
-- Thank you Tobias Jakobs for the awesome regular expression, which I tweaked a little
result["path"], result["filename"], result["basename"], result["filetype"] = string.match(str, "(.-)(([^\\/]-)%.?([^%.\\/]*))$")
return result
end
--[[
NAME
get_path - get the path from a file path
SYNOPSIS
local df = require "lib/dtutils.file"
local result = df.get_path(filepath)
filepath - string - path and filename
DESCRIPTION
get_path strips the filename and filetype from a path and returns the path
RETURN VALUE
result - string - the path
]]
dtutils_file.libdoc.functions[#dtutils_file.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value"},
Name = [[get_path - get the path from a file path]],
Synopsis = [[local df = require "lib/dtutils.file"
local result = df.get_path(filepath)
filepath - string - path and filename]],
Description = [[get_path strips the filename and filetype from a path and returns the path]],
Return_Value = [[result - string - the path]],
}
function dtutils_file.get_path(str)
local parts = dtutils_file.split_filepath(str)
return parts["path"]
end
--[[
NAME
get_filename - get the filename and extension from a file path
SYNOPSIS
local df = require "lib/dtutils.file"
local result = df.get_filename(filepath)
filepath - string - path and filename
DESCRIPTION
get_filename strips the path from a filepath and returns the filename
RETURN VALUE
result - string - the file name and type
]]
dtutils_file.libdoc.functions[#dtutils_file.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value"},
Name = [[get_filename - get the filename and extension from a file path]],
Synopsis = [[local df = require "lib/dtutils.file"
local result = df.get_filename(filepath)
filepath - string - path and filename]],
Description = [[get_filename strips the path from a filepath and returns the filename]],
Return_Value = [[result - string - the file name and type]],
}
function dtutils_file.get_filename(str)
local parts = dtutils_file.split_filepath(str)
return parts["filename"]
end
--[[
NAME
get_basename - get the filename without the path or extension
SYNOPSIS
local df = require "lib/dtutils.file"
local result = df.get_basename(filepath)
filepath - string - path and filename
DESCRIPTION
get_basename returns the name of the file without the path or filetype
RETURN VALUE
result - string - the basename of the file
]]
dtutils_file.libdoc.functions[#dtutils_file.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value"},
Name = [[get_basename - get the filename without the path or extension]],
Synopsis = [[local df = require "lib/dtutils.file"
local result = df.get_basename(filepath)
filepath - string - path and filename]],
Description = [[get_basename returns the name of the file without the path or filetype]],
Return_Value = [[result - string - the basename of the file]],
}
function dtutils_file.get_basename(str)
local parts = dtutils_file.split_filepath(str)
return parts["basename"]
end
--[[
NAME
get_filetype - get the filetype from a filename
SYNOPSIS
local df = require "lib/dtutils.file"
local result = df.get_filetype(filepath)
filepath - string - path and filename
DESCRIPTION
get_filetype returns the filetype from the supplied filepath
RETURN VALUE
result - string - the filetype
]]
dtutils_file.libdoc.functions[#dtutils_file.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value"},
Name = [[get_filetype - get the filetype from a filename]],
Synopsis = [[local df = require "lib/dtutils.file"
local result = df.get_filetype(filepath)
filepath - string - path and filename]],
Description = [[get_filetype returns the filetype from the supplied filepath]],
Return_Value = [[result - string - the filetype]],
}
function dtutils_file.get_filetype(str)
local parts = dtutils_file.split_filepath(str)
return parts["filetype"]
end
--[[
NAME
check_if_file_exists - check if a file or path exist
SYNOPSIS
local df = require "lib/dtutils.file"
local result = df.check_if_file_exists(filepath)
filepath - string - a file or path to check
DESCRIPTION
check_if_file_exists checks to see if a file or path exists
RETURN VALUE
result - boolean - true if the file or path exists, false if it doesn't
]]
-- Thanks Tobias Jakobs for the idea
dtutils_file.libdoc.functions[#dtutils_file.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value"},
Name = [[check_if_file_exists - check if a file or path exist]],
Synopsis = [[local df = require "lib/dtutils.file"
local result = df.check_if_file_exists(filepath)
filepath - string - a file or path to check]],
Description = [[check_if_file_exists checks to see if a file or path exists]],
Return_Value = [[result - boolean - true if the file or path exists, false if it doesn't]],
}
function dtutils_file.check_if_file_exists(filepath)
local result = os.execute("test -e " .. filepath)
if not result then
result = false
end
return result
end
--[[
NAME
chop_filetype - remove a filetype from a filename
SYNOPSIS
local df = require "lib/dtutils.file"
local result = df.chop_filetype(path)
path - string - a filename with or without a path
DESCRIPTION
chop_filetype removes the filetype from the filename
RETURN VALUE
result - string - the path and filename without the filetype
]]
dtutils_file.libdoc.functions[#dtutils_file.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value"},
Name = [[chop_filetype - remove a filetype from a filename]],
Synopsis = [[local df = require "lib/dtutils.file"
local result = df.chop_filetype(path)
path - string - a filename with or without a path]],
Description = [[chop_filetype removes the filetype from the filename]],
Return_Value = [[result - string - the path and filename without the filetype]],
}
function dtutils_file.chop_filetype(path)
local length = dtutils_file.get_filetype(path):len() + 2
return string.sub(path, 1, -length)
end
--[[
NAME
file_copy - copy a file to another name/location
SYNOPSIS
local df = require "lib/dtutils.file"
local result = df.file_copy(fromFile, toFile)
fromFile - string - name of file to copy from
toFile - string - name of file to copy to
DESCRIPTION
copy a file using a succession of methods from operating system
to a pure lua solution
RETURN VALUE
result - boolean - nil on error, true on success
]]
dtutils_file.libdoc.functions[#dtutils_file.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value"},
Name = [[file_copy - copy a file to another name/location]],
Synopsis = [[local df = require "lib/dtutils.file"
local result = df.file_copy(fromFile, toFile)
fromFile - string - name of file to copy from
toFile - string - name of file to copy to]],
Description = [[copy a file using a succession of methods from operating system
to a pure lua solution]],
Return_Value = [[result - boolean - nil on error, true on success]],
}
function dtutils_file.file_copy(fromFile, toFile)
local result = nil
-- if cp exists, use it
if dtutils_file.check_if_bin_exists("cp") then
result = os.execute("cp '" .. fromFile .. "' '" .. toFile .. "'")
end
-- if cp was not present, or if cp failed, then a pure lua solution
if not result then
local fileIn, err = io.open(fromFile, 'rb')
if fileIn then
local fileOut, errr = io.open(toFile, 'w')
if fileOut then
local content = fileIn:read(4096)
while content do
fileOut:write(content)
content = fileIn:read(4096)
end
result = true
fileIn:close()
fileOut:close()
else
log.msg(log.error, errr)
end
else
log.msg(log.error, err)
end
end
return result
end
--[[
NAME
file_move - move a file from one directory to another
SYNOPSIS
local df = require "lib/dtutils.file"
local result = df.file_move(fromFile, toFile)
fromFile - string - name of the original file
toFile - string - the new file location and name
DESCRIPTION
Move a file from one place to another. Try a succession of methods from
builtin to operating system to a pure lua solution.
RETURN VALUE
result - boolean - nil on error, some value on success
]]
dtutils_file.libdoc.functions[#dtutils_file.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value"},
Name = [[file_move - move a file from one directory to another]],
Synopsis = [[local df = require "lib/dtutils.file"
local result = df.file_move(fromFile, toFile)
fromFile - string - name of the original file
toFile - string - the new file location and name]],
Description = [[Move a file from one place to another. Try a succession of methods from
builtin to operating system to a pure lua solution.]],
Return_Value = [[result - boolean - nil on error, some value on success]],
}
function dtutils_file.file_move(fromFile, toFile)
local success = os.rename(fromFile, toFile)
if not success then
-- an error occurred, so let's try using the operating system function
if dtutils_file.check_if_bin_exists("mv") then
success = os.execute("mv '" .. fromFile .. "' '" .. toFile .. "'")
end
-- if the mv didn't exist or succeed, then...
if not success then
-- pure lua solution
success = dtutils_file.file_copy(fromFile, toFile)
if success then
os.remove(fromFile)
else
log.msg(log.error, "Unable to move " .. fromFile .. " to " .. toFile .. ". Leaving " .. fromFile .. " in place.")
end
end
end
return success -- nil on error, some value if success
end
--[[
NAME
filename_increment - add a two digit increment to a filename
SYNOPSIS
local df = require "lib/dtutils.file"
local result = df.filename_increment(filepath)
filepath - string - filename to increment
DESCRIPTION
filename_increment solves the problem of filename confllict by adding an
increment to the filename. If the supplied filename has no increment then
"01" is added to the basename. If the filename already has an increment, then
1 is added to it and the filename returned.
RETURN VALUE
result - string - the incremented filename
]]
dtutils_file.libdoc.functions[#dtutils_file.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value"},
Name = [[filename_increment - add a two digit increment to a filename]],
Synopsis = [[local df = require "lib/dtutils.file"
local result = df.filename_increment(filepath)
filepath - string - filename to increment]],
Description = [[filename_increment solves the problem of filename confllict by adding an
increment to the filename. If the supplied filename has no increment then
"01" is added to the basename. If the filename already has an increment, then
1 is added to it and the filename returned.]],
Return_Value = [[result - string - the incremented filename]],
}
function dtutils_file.filename_increment(filepath)
-- break up the filepath into parts
local path = dtutils_file.get_path(filepath)
local basename = dtutils_file.get_basename(filepath)
local filetype = dtutils_file.get_filetype(filepath)
-- check to see if we've incremented before
local increment = string.match(basename, "_(%d-)$")
if increment then
-- we do 2 digit increments so make sure we didn't grab part of the filename
if string.len(increment) > 2 then
-- we got the filename so set the increment to 01
increment = "01"
else
increment = string.format("%02d", tonumber(increment) + 1)
basename = string.gsub(basename, "_(%d-)$", "")
end
else
increment = "01"
end
local incremented_filepath = path .. basename .. "_" .. increment .. "." .. filetype
return incremented_filepath
end
return dtutils_file

266
lib/dtutils/processor.lua Normal file
View File

@@ -0,0 +1,266 @@
--[[
This file is part of darktable,
copyright (c) 2016 Bill Ferguson
darktable is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
darktable is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with darktable. If not, see <http://www.gnu.org/licenses/>.
]]
local dtutils_processor = {}
dtutils_processor.libdoc = {
Sections = {"Name", "Synopsis", "Description", "License"},
Name = [[dtutils.processor - Darktable lua functions for building processor scripts]],
Synopsis = [[local dp = require "lib/dtutils.processor"]],
Description = [[dtutils.processor provides common functions used for building scripts
that send images out to an external process, process them, and return the result (i.e. processors).]],
License = [[This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.]],
functions = {}
}
local dt = require "darktable"
local dtutils = require "lib/dtutils"
local df = require "lib/dtutils.file"
local log = require "lib/libLog"
local gettext = dt.gettext
dt.configuration.check_version(...,{3,0,0})
-- Tell gettext where to find the .mo file translating messages for a particular domain
-- This should be $HOME/.config/darktable/lua/locale/
gettext.bindtextdomain("dtutils",dt.configuration.config_dir.."/lua/locale/")
local function _(msgid)
return gettext.dgettext("dtutils.processor", msgid)
end
--[[
NAME
exporter_status - show the status while exporting images
SYNOPSIS
local dp = require "lib/dtutils.processor"
local result = dp.exporter_status(storage, image, format, filename, number, total, high_quality, extra_data)
storage - dt_imageio_module_storage_t - the storage that the status is being shown for
image - dt_lua_image_t - the current image
format - dt_imageio_module_format_t - the format of the export image
filename - string - the filename being exported to
number - integer - the number of this image in the sequence
total - integer - the total number of images being exported
high_quality - boolean
extra_data - extra data from the storage
DESCRIPTION
exporter_status runs prior to each image being exported and prints out a status that lets the user
know how many images, out of the total, have been exported
RETURN VALUE
none
]]
dtutils_processor.libdoc.functions[#dtutils_processor.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value"},
Name = [[exporter_status - show the status while exporting images]],
Synopsis = [[local dp = require "lib/dtutils.processor"
local result = dp.exporter_status(storage, image, format, filename, number, total, high_quality, extra_data)
storage - dt_imageio_module_storage_t - the storage that the status is being shown for
image - dt_lua_image_t - the current image
format - dt_imageio_module_format_t - the format of the export image
filename - string - the filename being exported to
number - integer - the number of this image in the sequence
total - integer - the total number of images being exported
high_quality - boolean
extra_data - extra data from the storage]],
Description = [[exporter_status runs prior to each image being exported and prints out a status that lets the user
know how many images, out of the total, have been exported]],
Return_Value = [[none]],
}
function dtutils_processor.exporter_status(storage, image, format, filename,
number, total, high_quality, extra_data)
dt.print(string.format(_("Export Image %i/%i"), number, total))
end
--[[
NAME
extract_image_list - assemble the exported image filenames from an image_table into a string
SYNOPSIS
local dp = require "lib/dtutils.processor"
result = dp.extract_image_list(image_table)
image_table - table - a table of images such as supplied by the exporter or by libPlugin.build_image_table
DESCRIPTION
extract_image_list concatenates the exported image names into a space separated string suitable for
passing as an argument to a processor. Each filename is bracketed with single quotes to protect against
spaces and special characters in the filepath
RETURN VALUE
result - string - the assembled image list on success, or an empty image list on error
]]
dtutils_processor.libdoc.functions[#dtutils_processor.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value"},
Name = [[extract_image_list - assemble the exported image filenames from an image_table into a string]],
Synopsis = [[local dp = require "lib/dtutils.processor"
result = dp.extract_image_list(image_table)
image_table - table - a table of images such as supplied by the exporter or by libPlugin.build_image_table]],
Description = [[extract_image_list concatenates the exported image names into a space separated string suitable for
passing as an argument to a processor. Each filename is bracketed with single quotes to protect against
spaces and special characters in the filepath]],
Return_Value = [[result - string - the assembled image list on success, or an empty image list on error]],
}
function dtutils_processor.extract_image_list(image_table)
local img_list = ""
for _,exp_img in pairs(image_table) do
img_list = img_list .. " " .. "'" .. exp_img .. "'"
end
return img_list
end
--[[
NAME
extract_collection_path - extract the collection path from an image table
SYNOPSIS
local dp = require "lib/dtutils.processor"
local result = dp.extract_collection_path(image_table)
image_table - table - a table of images such as supplied by the exporter or by libPlugin.build_image_table
DESCRIPTION
extract_collection_path looks at the first image in the image_table and returns the path
RETURN VALUE
result - string - the collection path on success, or nil if there was an error
LIMITATIONS
The collection path is determined from the first image. If the table consists of images from different
collections, then only the first collection is used.
]]
dtutils_processor.libdoc.functions[#dtutils_processor.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value", "Limitations"},
Name = [[extract_collection_path - extract the collection path from an image table]],
Synopsis = [[local dp = require "lib/dtutils.processor"
local result = dp.extract_collection_path(image_table)
image_table - table - a table of images such as supplied by the exporter or by libPlugin.build_image_table]],
Description = [[extract_collection_path looks at the first image in the image_table and returns the path]],
Return_Value = [[result - string - the collection path on success, or nil if there was an error]],
Limitations = [[The collection path is determined from the first image. If the table consists of images from different
collections, then only the first collection is used.]],
}
function dtutils_processor.extract_collection_path(image_table)
collection_path = nil
for i,_ in pairs(image_table) do
collection_path = i.path
break
end
return collection_path
end
--[[
NAME
make_output_filename- make an output filename from an image list
SYNOPSIS
local dp = require "lib/dtutils.processor"
local result = dtutils.make_output_filename(img_list)
img_list - string - a space separated list of filenames
DESCRIPTION
make_output_filename takes a string of filenames, breaks them apart, then
combines them in a way that makes some sense. This is useful in routines
that do panoramas, hdr, or focus stacks. The returned filename is a representation
of the files used to construct the image. If there are 3 or fewer images, then the file
basenames are concatenated with a separator. If there is more than 3 images, the first and
last file basenames are concatenated with a separator.
RETURN VALUE
result - string - the constructed filename on success, nil on error
]]
dtutils_processor.libdoc.functions[#dtutils_processor.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value"},
Name = [[make_output_filename- make an output filename from an image list]],
Synopsis = [[local dp = require "lib/dtutils.processor"
local result = dtutils.make_output_filename(img_list)
img_list - string - a space separated list of filenames]],
Description = [[make_output_filename takes a string of filenames, breaks them apart, then
combines them in a way that makes some sense. This is useful in routines
that do panoramas, hdr, or focus stacks. The returned filename is a representation
of the files used to construct the image. If there are 3 or fewer images, then the file
basenames are concatenated with a separator. If there is more than 3 images, the first and
last file basenames are concatenated with a separator.]],
Return_Value = [[result - string - the constructed filename on success, nil on error]],
}
function dtutils_processor.make_output_filename(img_list)
local images = {}
local cnt = 1
local max_distinct_names = 3
local name_separator = "-"
local outputFileName = nil
log.msg(log.debug, "img_list is ", img_list)
local result = dtutils.split(img_list, " ")
table.sort(result)
for _,img in pairs(result) do
images[cnt] = df.get_basename(img)
cnt = cnt + 1
end
cnt = cnt - 1
if cnt > 1 then
if cnt > max_distinct_names then
-- take the first and last
outputFileName = images[1] .. name_separator .. images[cnt]
else
-- join them
outputFileName = dtutils.join(images, name_separator)
end
else
-- return the single name
outputFileName = images[cnt]
end
return outputFileName
end
return dtutils_processor

378
lib/libLog.lua Normal file
View File

@@ -0,0 +1,378 @@
--[[
libLog.lua - darktable lua logging library
Copyright (C) 2016 Bill Ferguson <wpferguson@gmail.com>.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
]]
local libLog = {}
libLog.libdoc = {
Sections = {"Name", "Synopsis", "Description", "License", "Copyright"},
Name = "libLog - darktable lua logging library",
Synopsis = [[local log = require "lib/libLog"]],
Description = [[libLog provides a multi-level logging solution for use with
the darktable lua scripts.]],
License = [[This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.]],
Copyright = "Copyright (C) 2016 Bill Ferguson <wpferguson@gmail.com>.",
functions = {}
}
local dt = require "darktable"
-- set the default log levels
libLog.debug = {"DEBUG:", false}
libLog.info = {"INFO:", false}
libLog.warn = {"WARN:", false}
libLog.error = {"ERROR:", true}
libLog.success = {"SUCCESS:", true}
libLog.libdoc.functions[#libLog.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value"},
Name = "caller - get the name of the calling routine",
Synopsis = [[local log = require "lib/libLog"
result = log.caller(level)
level - number - the number of stack levels to go down to retrieve the caller routine information]],
Description = "caller gets the name of the calling routine and returns it",
Return_Value = [[result - string - the name and line number of the calling function or 'callback: ' if the attempt to get the
caller returns nil]],
}
--[[
NAME
caller - get the name of the calling routine
SYNOPSIS
local log = require "lib/libLog"
local result = log.caller(level)
level - number - the number of stack levels to go down to retrieve the caller routine information
DESCRIPTION
caller gets the name of the calling routine and returns it
RETURN VALUE
result - string - the name of the calling function and line number or 'callback: ' if the attempt to get the
caller returns nil
]]
function libLog.caller(level)
local name = debug.getinfo(level).name
local lineno = nil
local source = nil
if name then
lineno = debug.getinfo(level).currentline
-- returns the path to the file prefixed with @
source = debug.getinfo(level).source
-- we just need the filename, so grab it from the string
-- Thanks, Tobias Jakobs :-)
source = string.match(source, "@.-([^\\/]-%.?[^%.\\/]*)$")
return name .. ": " .. source .. ": " .. lineno .. ":"
else
return "callback:"
end
end
libLog.libdoc.functions[#libLog.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Limitations"},
Name = "msg - print a log message",
Synopsis = [[local log = require "lib/libLog"
log.msg(level, ...)
level - table - the type of message, one of:
libLog.debug - debugging messages
libLog.info - informational messages
libLog.warn - warning messages
libLog.error - error messages
libLog.success - success messages
... - string(s) - the message to print, which could be a comma separated set of strings]],
Description = [[msg checks the level to see if it is enabled, then prints the level type and message if it is.
debug messages print straight to the console, info and warn messages to dt.print_error() and error
and success to dt.print()]],
Limitations = [[If you use libLog.msg in a callback, the name of the calling routine can't be determined. A solution
is to include some means of reference such as the name of the callback as an argument, i.e.
libLog.msg(libLog.debug, "libPlugin.format_combobox:", "value is " .. self.value)
which would result in
DEBUG: callback: libPlugin.format_combobox: value is JPEG]],
}
--[[
NAME
msg - print a log message
SYNOPSIS
local log = require "lib/libLog"
log.msg(level, ...)
level - table - the type of message, one of:
libLog.debug - debugging messages
libLog.info - informational messages
libLog.warn - warning messages
libLog.error - error messages
libLog.success - success messages
... - string(s) - the message to print, which could be a comma separated set of strings
DESCRIPTION
msg checks the level to see if it is enabled, then prints the level type and message if it is.
debug messages print straight to the console, info and warn messages to dt.print_error() and error
and success to dt.print()
RETURN VALUE
none
LIMITATIONS
If you use libLog.msg in a callback, the name of the calling routine can't be determined. A solution
is to include some means of reference such as the name of the callback as an argument, i.e.
libLog.msg(libLog.debug, "libPlugin.format_combobox:", "value is " .. self.value)
which would result in
DEBUG: callback: libPlugin.format_combobox: value is JPEG
]]
function libLog.msg(level, ...)
local message = {...}
if level[2] == true then
libLog.print(level[1], libLog.caller(3), unpack(message))
end
end
libLog.libdoc.functions[#libLog.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value", "Reference"},
Name = "print - print the supplied arguments",
Synopsis = [[local log = require "lib/libLog"
log.print(...)
... - arguments to be printed that are converted with tostring()]],
Description = [[print loops through the arguments converting each to a string then writing
them to stdout. Spaces are put between each argument on output.]],
Return_Value = "none",
Reference = "http://stackoverflow.com/questions/7148678/lua-print-on-the-same-line",
}
--[[
NAME
libLog.print - print the supplied arguments
SYNOPSIS
local log = require "lib/libLog"
log.print(...)
... - arguments to be printed that are converted with tostring()
DESCRIPTION
print loops through the arguments converting each to a string then writing
them to stdout. Spaces are put between each argument on output.
RETURN VALUE
none
REFERENCE
http://stackoverflow.com/questions/7148678/lua-print-on-the-same-line
]]
function libLog.print(...)
local write = io.write
local n = select("#",...)
for i = 1,n do
local v = tostring(select(i,...))
write(v)
if i~=n then
write(' ')
end
end
write('\n')
end
libLog.libdoc.functions[#libLog.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value"},
Name = [[set_level - set the logging level]],
Synopsis = [[local log = require "lib/libLog"
log.set_level(level)
level - string - a string specifying the level, one of: "debug", "info", "warn", "error", "success"]],
Description = [[set_level sets the logging level to the level specified. Every level above the specified level
is also enabled, therefore specifying "debug" would turn on all levels and specifying "success"
would turn all levels off except libLog.success.]],
Return_Value = "none",
}
--[[
NAME
set_level - set the logging level
SYNOPSIS
local log = require "lib/libLog"
log.set_level(level)
level - string - a string specifying the level, one of: "debug", "info", "warn", "error", "success"
DESCRIPTION
set_level sets the logging level to the level specified. Every level above the specified level
is also enabled, therefore specifying "debug" would turn on all levels and specifying "success"
would turn all levels off except libLog.success.
RETURN VALUE
none
]]
function libLog.set_level(level)
level = level:lower()
if string.match(level, "debug") then
-- turn everything on
libLog.debug[2] = true
libLog.info[2] = true
libLog.warn[2] = true
libLog.error[2] = true
libLog.success[2] = true
elseif string.match(level, "info") then
-- turn off debug and everything else on
libLog.debug[2] = false
libLog.info[2] = true
libLog.warn[2] = true
libLog.error[2] = true
libLog.success[2] = true
elseif string.match(level, "warn") then
-- turn off debug and info and everything else on
libLog.debug[2] = false
libLog.info[2] = false
libLog.warn[2] = true
libLog.error[2] = true
libLog.success[2] = true
elseif string.match(level, "error") or string.match("reset") then
-- everything off except error and success
libLog.debug[2] = false
libLog.info[2] = false
libLog.warn[2] = false
libLog.error[2] = true
libLog.success[2] = true
elseif string.match(level, "success") then
-- everything off except success
libLog.debug[2] = false
libLog.info[2] = false
libLog.warn[2] = false
libLog.error[2] = false
libLog.success[2] = true
else
-- leave everything unchanged and return
return
end
end
libLog.libdoc.functions[#libLog.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description", "Return_Value"},
Name = [[get_level - get the current logging level]],
Synopsis = [[local log = require "lib/libLog"
local result = libLog.get_level()]],
Description = "get_level returns a string representing the current log level.",
Return_Value = [[result - string - one of "debug", "info", "warn", "error", or "success"]],
}
--[[
NAME
get_level - get the current logging level
SYNOPSIS
local log = require "lib/libLog"
local result = log.get_level()
DESCRIPTION
get_level returns a string representing the current log level.
RETURN VALUE
result - string - one of "debug", "info", "warn", "error", or "success"
]]
function libLog.get_level()
local result = nil
if libLog.debug[2] == true then
result = "debug"
elseif libLog.info[2] == true then
result = "info"
elseif libLog.warn[2] == true then
result = "warn"
elseif libLog.error[2] == true then
result = "error"
elseif libLog.success[2] == true then
result = "success"
end
return result
end
libLog.libdoc.functions[#libLog.libdoc.functions + 1] = {
Sections = {"Name", "Synopsis", "Description"},
Name = [[always - always write out a log message]],
Synopsis = [[local log = require "lib/libLog"
log.always(level, ...)
level - number - the number of stack levels to go back to identify the caller
... - string(s) - the message to print]],
Description = [[always is meant specifically for the dtutils.debug library, but may be used for other
purposes. always is independent of the log level setting so that it will always work.
The number of stack levels to look back for the caller may be specified. It is normally
3 when called from a routine, but is 4 when called from the debugging routines since the
debugging routines add another stack level.]],
}
--[[
NAME
always - always write out a log message
SYNOPSIS
local log = require "lib/libLog"
log.always(level, ...)
level - number - the number of stack levels to go back to identify the caller
... - string(s) - the message to print
DESCRIPTION
always is meant specifically for the dtutils.debug library, but may be used for other
purposes. always is independent of the log level setting so that it will always work.
The number of stack levels to look back for the caller may be specified. It is normally
3 when called from a routine, but is 4 when called from the debugging routines since the
debugging routines add another stack level.
]]
function libLog.always(level, ...)
local message = {...}
libLog.print(libLog.caller(level), unpack(message))
end
return libLog

View File

@@ -1,93 +0,0 @@
--[[
This file is part of darktable,
copyright (c) 2014 Jérémy Rosen
darktable is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
darktable is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with darktable. If not, see <http://www.gnu.org/licenses/>.
]]
--[[
DEBUG HELPERS
A collection of helper functions to help debugging lua scripts.
require it as
dhelpers = require "official/debug-helpers"
Each function is documented in its own header
]]
local dt = require "darktable"
local io = require "io"
local table = require "table"
require "darktable.debug"
local M = {} -- The actual content of the module
--[[
A function which prints its name and its parameters
* name : the name of the tracepoint
* ... : anything, will be dumped using dt.debug
]]
function M.tracepoint(name,...)
print("***** "..name.." ****")
params = {...}
print(dt.debug.dump(params,"parameters"))
return ...;
end
--[[
A function that returns a tracepoint function with the given name
This is mainly used to debug callbacks,
register_event(event, dhelpers.new_tracepoint("hit callback"))
will print the following each time the callback is called
**** hit callback ****
<all the callback's parameters dumped>
]]
function M.new_tracepoint(name)
return function(...) return M.tracepoint(name,...) end
end
--[[
Wrapper around debug.dump, will directly print to stdout,
same calling covention
]]
function M.dprint(...)
print(dt.debug.dump(...))
end
--[[
sets dt.debug.known to shorten all images to a single line
if you don't need to debug the content of images, this will avoid them flooding your logs
]]
function M.terse_dump()
for _,v in ipairs(dt.database) do
dt.debug.known[v] = tostring(v)
end
end
return M
-- vim: shiftwidth=2 expandtab tabstop=2 cindent
-- kate: tab-indents: off; indent-width 2; replace-tabs on; remove-trailing-space on;

View File

@@ -0,0 +1,63 @@
--[[
get_lib_manpages.lua - retrieve the included library documentation and output it as man pages
Copyright (c) 2016, Bill Ferguson
]]
local dt = require "darktable"
local du = require "lib/dtutils"
local libname = nil
dt.configuration.check_version(...,{3,0,0})
local function output_man(d)
local parts = du.split(d[d.Sections[1]], " - ")
if not libname then
libname = parts[1]
end
local fname = "/tmp/" .. parts[1] .. ".3"
local mf = io.open(fname, "w")
if mf then
mf:write(".TH " .. string.upper(parts[1]) .. " 3 \"\" \"\" \"Darktable " .. libname .. " functions\"\n")
for _,section in pairs(d.Sections) do
if d[section] then
mf:write(".SH " .. string.upper(string.gsub(section, "_", " ")) .. "\n")
mf:write(d[section] .. "\n")
end
end
mf:close()
os.execute("groff -man " .. fname .. " | ps2pdf - " .. fname .. ".pdf")
else
log.msg(log.error, "Can't open file " .. fname .. "for writing")
end
end
-- find the libraries
local output = io.popen("cd "..dt.configuration.config_dir.."/lua/lib ;find . -name \\*.lua -print | sort")
-- loop through the libraries
for line in output:lines() do
line = string.gsub(line, "/", ".")
local lib_name = line:sub(3,-5)
if lib_name:len() > 2 then
lib_name = "lib/" .. lib_name
local lib = require(lib_name)
-- print the documentation for the library
if lib.libdoc then
local doc = lib.libdoc
if doc then
output_man(doc)
for _,fdoc in pairs(doc.functions) do
-- print the documentation for each of the functions
output_man(fdoc)
end
end
end
end
libname = nil
end

46
tools/get_libdoc.lua Normal file
View File

@@ -0,0 +1,46 @@
--[[
get_libdoc.lua - retrieve the included library documentation and output it
Copyright (c) 2016, Bill Ferguson
]]
local dt = require "darktable"
dt.configuration.check_version(...,{3,0,0})
local function output_doc(d)
for _,section in pairs(d.Sections) do
if d[section] then
print(string.upper(string.gsub(section, "_", " ")))
print("\t" .. d[section] .. "\n")
end
end
print("\f")
end
-- find the libraries
local output = io.popen("cd "..dt.configuration.config_dir.."/lua/lib ;find . -name \\*.lua -print | sort")
-- loop through the libraries
for line in output:lines() do
line = string.gsub(line, "/", ".")
local lib_name = line:sub(3,-5)
if lib_name:len() > 2 then
lib_name = "lib/" .. lib_name
local lib = require(lib_name)
-- print the documentation for the library
if lib.libdoc then
local doc = lib.libdoc
output_doc(doc)
for _,fdoc in pairs(doc.functions) do
-- print the documentation for each of the functions
output_doc(fdoc)
end
end
end
end