Skip to content

Instantly share code, notes, and snippets.

@wtsnjp
Created December 31, 2020 04:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wtsnjp/20f222d6a272319e30288b6bad789d3b to your computer and use it in GitHub Desktop.
Save wtsnjp/20f222d6a272319e30288b6bad789d3b to your computer and use it in GitHub Desktop.
#!/usr/bin/env texlua
--
-- This is file 'wtspingif.lua'.
--
-- Copyright 2020 Takuto ASAKURA (wtsnjp)
-- GitHub: https://github.com/wtsnjp
-- Twitter: @wtsnjp
--
-- This package is distributed under the MIT License.
--
-- program information
prog_name = 'wtspingif'
version = '0.1'
release_date = '2020-12-31'
author = 'Takuto ASAKURA (wtsnjp)'
-- option flags (default)
option_density = 72
option_delay = 1
verbosity_level = 0
-- exit codes
exit_ok = 0
exit_error = 1
exit_usage = 2
----------------------------------------
-- library
local lfs = require 'lfs'
local pdfe = require 'pdfe'
-- global functions
function log(label, msg, ...)
io.stderr:write(prog_name .. ' ' .. label .. ': ' .. msg:format(...) .. '\n')
end
function err_print(err_type, msg, ...)
if (verbosity_level > 0) or (err_type == 'error')
or (verbosity_level > -1 and err_type == 'warning') then
log(err_type, msg, ...)
end
end
function dbg_print(dbg_type, msg, ...)
if debug[dbg_type] then
log('debug-' .. dbg_type, msg, ...)
end
end
----------------------------------------
do
local function execute(cmd)
err_print('info', 'Running command: ' .. cmd)
local status = os.execute(cmd)
if status > 0 then
llmk.util.err_print('error',
'Fail running %s (exit code: %d)', cmd, status)
os.exit(exit_error)
end
end
function convert(pdf_file)
local basename = pdf_file:sub(1, -5)
local function png_name(i)
return string.format('__wtsg%s_%03d.png', basename, i)
end
-- estimate number of frames
local doc = pdfe.open(pdf_file)
local nof_frames = pdfe.getnofpages(doc)
pdfe.close(doc)
-- generating filenames
local png_files = {}
for i = 0, nof_frames - 1 do
table.insert(png_files, png_name(i))
end
local png_args = table.concat(png_files, ' ')
-- convert PDF -> PNG images
for i = 0, nof_frames - 1 do
execute(string.format(
'magick %s[%d] -background white -alpha remove -density %d %s',
pdf_file, i, option_density, png_name(i)))
end
-- convert PNG images -> animated GIF
execute(string.format(
'magick -delay %d %s %s.gif', option_delay, png_args, basename))
-- remove PNG images
for _, fn in pairs(png_files) do
os.remove(fn)
end
end
end
----------------------------------------
do
-- help texts
local usage_text = [[
Usage: wtspingif [OPTION]... PDF_FILE
]]
local version_text = [[
%s %s
]]
-- show uasage / help
local function show_usage(out)
out:write(usage_text)
end
-- execution functions
local function read_options()
if #arg == 0 then
show_usage(io.stderr)
os.exit(exit_usage)
end
local curr_arg
local action = false
-- modified Alternative Get Opt
-- cf. http://lua-users.org/wiki/AlternativeGetOpt
local function getopt(arg, options)
local tmp
local tab = {}
local saved_arg = { table.unpack(arg) }
for k, v in ipairs(saved_arg) do
if string.sub(v, 1, 2) == "--" then
table.remove(arg, 1)
local x = string.find(v, "=", 1, true)
if x then
table.insert(tab, { string.sub(v, 3, x-1), string.sub(v, x+1) })
else
table.insert(tab, { string.sub(v, 3), true })
end
elseif string.sub(v, 1, 1) == "-" then
table.remove(arg, 1)
local y = 2
local l = string.len(v)
local jopt
while (y <= l) do
jopt = string.sub(v, y, y)
if string.find(options, jopt, 1, true) then
if y < l then
tmp = string.sub(v, y+1)
y = l
else
table.remove(arg, 1)
tmp = saved_arg[k + 1]
end
if string.match(tmp, '^%-') then
table.insert(tab, { jopt, false })
else
table.insert(tab, { jopt, tmp })
end
else
table.insert(tab, { jopt, true })
end
y = y + 1
end
end
end
return tab
end
opts = getopt(arg, 'db')
for _, tp in ipairs(opts) do
k, v = tp[1], tp[2]
if #k == 1 then
curr_arg = '-' .. k
else
curr_arg = '--' .. k
end
-- action
if (curr_arg == '-h') or (curr_arg == '--help') then
action = 'help'
elseif (curr_arg == '-V') or (curr_arg == '--version') then
action = 'version'
elseif (curr_arg == '-v') or (curr_arg == '--verbose') then
verbosity_level = 1
elseif (curr_arg == '-d') or (curr_arg == '--density') then
option_density = tonumber(v)
elseif (curr_arg == '-b') or (curr_arg == '--boost') then
option_delay = tonumber(v)
else
err_print('error', 'unknown option: ' .. curr_arg)
err_print('error', error_msg)
os.exit(exit_error)
end
end
return action
end
local function do_action()
if action == 'help' then
show_usage(io.stdout)
elseif action == 'version' then
io.stdout:write(version_text:format(
prog_name, version, release_date, author))
end
end
function main()
action = read_options()
if action then
do_action()
os.exit(exit_ok)
end
convert(arg[1])
os.exit(exit_ok)
end
end
----------------------------------------
main()
-- EOF
@wtsnjp
Copy link
Author

wtsnjp commented Dec 31, 2020

Example: process the following with llmk:

% +++
% latex = "pdflatex"
% sequence = ["latex", "wtspingif"]
% extra_clean_files = ["%B.synctex.gz"]
% clobber_files = ["%B.gif", "%B.pdf"]
%
% [programs.wtspingif]
% command = "wtspingif"
% opts = ["-d 144", "-b 5"]
% args = ["%B.pdf"]
% +++
\documentclass{article}
\usepackage[paperwidth=15cm, paperheight=15cm, margin=0cm]{geometry}
\usepackage[svgnames]{xcolor}
\usepackage{scsnowman}
\usepackage{tcfaspin}

\makeatletter
\chardef\faAllTicks=32
\chardef\faStopTicks=0

\long\def\beginframe#1\endframe{%
  #1\clearpage
  \edef\x{\noexpand\chardef\noexpand\faStopTicks
    =\number\numexpr\faStopTicks+1\relax}\x\relax
  \ifnum\faStopTicks<\faAllTicks\beginframe #1\endframe\fi}
\makeatother

\pagestyle{empty}

\begin{document}

\centering

\beginframe
\mbox{}\vfill
\faSpin{\scsnowman[scale=40,
  muffler=Red,hat=Green,buttons=RoyalBlue,
  arms=Brown,snow=SkyBlue]}%
\vfill
\endframe

\end{document}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment