Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,5 @@ sympy-plots-for-*.tex/
# lyluatex temp directories
*tmp-ly/
/lyluatex.tex
lyluatex.code-workspace
lyluatex.lua.old
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
test:
TEXINPUTS=luaoptions: lualatex -interaction=nonstopmode -shell-escape test.tex
TEXINPUTS=luaoptions: lualatex -interaction=nonstopmode -shell-escape test-clipregions.tex

manual:
@lua -e "if tonumber(io.popen('pandoc -v'):read():gsub('pandoc (.*)', '%1'):sub(1,1)) < 2 then print('Pandoc >= 2 required') ; os.exit(1) ; end"
Expand Down
153 changes: 143 additions & 10 deletions lyluatex.lua
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,30 @@ local LY_HEAD = [[
]]


--- Clip-regions engraver to force a snippet to start on a new system. It can manage multiple fragments, but in lyluatex the feature is limited to a single fragment in the form: "clip-regions=aa-bb", where aa and bb are measure numbers.
local CLIP_BREAK_ENGRAVER = [[
#(define (Clip_break_engraver context)
(let ((break-measures '()))
(make-engraver
(acknowledgers
((non-musical-paper-column-interface engraver grob source-engraver)
(if (memv (ly:context-property context 'currentBarNumber) break-measures)
(ly:grob-set-property! grob 'line-break-permission 'force))))
((initialize engraver)
(let* ((paper (ly:context-output-def context))
(regions (ly:output-def-lookup paper 'clip-regions)))
(if (list? regions)
(for-each
(lambda (reg)
;; reg is (start-loc . end-loc), loc is (measure . moment)
(let ((start (car (car reg)))
(fine (car (cdr reg))))
(set! break-measures
(cons* start fine break-measures))))
regions)))))))
]]


--[[ ========================== Helper functions ========================== --]]
-- dirty fix as info doesn't work as expected
local oldinfo = info
Expand Down Expand Up @@ -237,8 +261,50 @@ end

local function set_lyscore(score)
ly.score = score

if score['clip-regions'] and score['clip-regions'] ~= '' then
score.protrusion_left = 0
score.indent_offset = 0
score.leftgutter = 0

local base_name = score.output:match("[^/]*$")
local clips = {}

pcall(function()
for file in lfs.dir(score.tmpdir) do
if file:find("^" .. base_name) and file:find("%-clip") and file:sub(-4) == ".pdf" then
local clip_name = file:gsub("%.pdf$", "")
table.insert(clips, clip_name)
end
end
end)

table.sort(clips, function(a, b)
local num_a = a:match("%-clip%-(%d+)$")
local num_b = b:match("%-clip%-(%d+)$")

local n_a = num_a and tonumber(num_a)
local n_b = num_b and tonumber(num_b)

if n_a and n_b then return n_a > n_b end
if n_a and not n_b then return true end
if not n_a and n_b then return false end
return a < b
end)

for _, clip_file in ipairs(clips) do
table.insert(score, score.tmpdir .. '/' .. clip_file)
end

score.nsystems = #clips
score.range = {}
for i = 1, score.nsystems do table.insert(score.range, i) end

return
end

ly.score.nsystems = ly.score:count_systems()
if score.insert ~= 'fullpage' then -- systems and inline
if score.insert ~= 'fullpage' then
local hoffset = ly.score.protrusion or 0
if hoffset == '' then hoffset = 0 end
ly.score.hoffset = hoffset..'pt'
Expand Down Expand Up @@ -357,13 +423,26 @@ function latex_includesystems(filename, range, protrusion, gutter, staffsize, in
local h_offset = protrusion + indent_offset
local texoutput = '\\ifx\\preLilyPondExample\\undefined\\else\\preLilyPondExample\\fi\n'
texoutput = texoutput..'\\par\n'

local is_clip = ly.score and ly.score['clip-regions'] and ly.score['clip-regions'] ~= ''

for index, system in pairs(range) do
if not lfs.isfile(filename..'-'..system..'.eps') then break end
local path_graphic
local h_space = h_offset + gutter

if is_clip then
path_graphic = ly.score[index]
if not path_graphic then break end
else -- Default lyluatex behavior
if not lfs.isfile(filename..'-'..system..'.eps') then break end
path_graphic = filename..'-'..system
end

texoutput = texoutput..
string.format([[
\noindent\hspace*{%fpt}\includegraphics{%s}%%
]],
h_offset + gutter, filename..'-'..system
h_offset + gutter,path_graphic
)
if index < #range then
texoutput = texoutput..
Expand Down Expand Up @@ -763,10 +842,20 @@ function Score:count_systems(force)
local count = self.system_count
if force or not count then
count = 0
local systems = self.output:match("[^/]*$").."%-%d+%.eps"
for f in lfs.dir(self.tmpdir) do
if f:match(systems) then
count = count + 1
local base_name = self.output:match("[^/]*$")

if self['clip-regions'] and self['clip-regions'] ~= '' then
Comment thread
MacLotsen marked this conversation as resolved.
for f in lfs.dir(self.tmpdir) do
if f:find(base_name, 1, true) and f:find("-clip", 1, true) and f:sub(-4) == ".pdf" then
count = count + 1
end
end
else -- original logic
local systems = base_name.."%-%d+%.eps"
for f in lfs.dir(self.tmpdir) do
if f:match(systems) then
count = count + 1
end
end
end
self.system_count = count
Expand All @@ -776,11 +865,23 @@ end

function Score:delete_intermediate_files()
for _, filename in pairs(self.output_names) do
if self.insert == 'fullpage' then os.remove(filename..'.ps')
if self.insert == 'fullpage' then
os.remove(filename..'.ps')
else
os.remove(filename..'-systems.tex')
os.remove(filename..'-systems.texi')
os.remove(filename..'.eps')

-- If clip-regions
if self['clip-regions'] and self['clip-regions'] ~= '' then
local base_name = filename:match("[^/]*$")
for file in lfs.dir(self.tmpdir) do
if file:find(base_name, 1, true) and file:sub(-4) == ".eps" then
os.remove(self.tmpdir..'/'..file)
end
end
else
os.remove(filename..'.eps')
end
end
end
end
Expand Down Expand Up @@ -834,6 +935,16 @@ function Score:header()
warn([[Ignoring 'write-headers' for non-file score.]])
end
end

if self['clip-regions'] and self['clip-regions'] ~= '' then
header = CLIP_BREAK_ENGRAVER .. "\n" .. header
header = header:gsub(
[[%\include "lilypond%-book%-preamble.ly"]],
[[\include "lilypond-book-preamble.ly"
#(define default-toplevel-book-handler print-book-with-defaults)]] --This is to allow dclip-systems to work
)
end

return header
end

Expand Down Expand Up @@ -863,6 +974,12 @@ function Score:lilypond_cmd()
local cmd = shell_quote(self.program) .. ' '
.. (self.insert == "fullpage" and "" or "-E ")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid EPS runs, we must ommit the option -E:

((self.insert == "fullpage" or clip_regions) and "" or "-E ")

And before make the bool available:

local clip_regions = self['clip-regions'] and self['clip-regions'] ~= ''

.. "-dno-point-and-click -djob-count=2 -dno-delete-intermediate-files "

if self['clip-regions'] and self['clip-regions'] ~= '' then
cmd = cmd .. "-dclip-systems "
cmd = cmd:gsub("%-E%s*", "") --remove -E option if present.
end

if self:lilypond_version() >= ly.v{2, 24} then cmd = cmd.."-dtall-page-formats=pdf " end
if self['optimize-pdf'] and self:lilypond_has_TeXGS() then
cmd = cmd.."-O TeX-GS -dgs-never-embed-fonts "
Expand Down Expand Up @@ -1085,7 +1202,23 @@ function Score:ly_staffprops()
if self.notiming then timing = [[\context { \Score timing = ##f }]] end
if self.notimesig then timesig = [[\context { \Staff \remove "Time_signature_engraver" }]] end
if self.nostaffsymbol then staff = [[\context { \Staff \remove "Staff_symbol_engraver" }]] end
return string.format('%s\n%s\n%s\n%s', clef, timing, timesig, staff)

local clip_code = '%% no clip-regions set'
if self['clip-regions'] and self['clip-regions'] ~= '' then
local clean = self['clip-regions']:gsub('%s+', '')
local start, fine = clean:match("(%d+)%-(%d+)")
if start and fine then
clip_code = string.format([[
clip-regions = #(list (cons (make-rhythmic-location %s 0 1) (make-rhythmic-location %s 0 1)))
\context {
\Score
\consists \Clip_break_engraver
}]], start, tonumber(fine) + 1)
else
clip_code = '%% Wrong format for clip-regions. Should be start-end, e.g. 52-74 for measures 52 to 74. Ignoring.'
end
end
return string.format('%s\n%s\n%s\n%s\n%s', clef, timing, timesig, staff, clip_code)
end

function Score:ly_twoside() if self.twoside then return 't' else return 'f' end end
Expand Down
1 change: 1 addition & 0 deletions lyluatex.sty
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
['addversion'] = {'false', 'true', ''},
['autoindent'] = {'true', 'false', ''},
['cleantmp'] = {'false', 'true', ''},
['clip-regions'] = {''},
['currfiledir'] = {},
['debug'] = {'false', 'true', ''},
['extra-bottom-margin'] = {'0', _opt.is_dim},
Expand Down
23 changes: 23 additions & 0 deletions test-clipregions.tex
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
\documentclass[a4paper,fontsize=12pt]{scrbook}
\usepackage[program=lilypond]{lyluatex}

\title{}
\author{}

\begin{document}

Clip-regions example

\begin{enumerate}
\item Single system, no ragged--right
\lilypondfile[staffsize=13, clip-regions=5-9 ]{ly/Beethoven/opus-18-1.ly}

\item Single system, ragged--right
\lilypondfile[staffsize=14, clip-regions=5-9, ragged-right=true]{ly/Beethoven/opus-18-1.ly}

\item Multiple system, ragged--right
\lilypondfile[clip-regions=2-14,ragged-right=true]{ly/Ave Maria De Lourdes/Ave Maria.ly}

\end{enumerate}

\end{document}