一位 LaTeX 用户在 itemize/enumerate 环境中使用引号时遇到了问题。当在项目的开头使用 csquotes 的自动引号时,引号无法正确突出到“边距”中。有没有办法以可持续的方式解决这个问题?
理想情况下,我试图找到一个解决方案,它由一些可以包含在文档序言中的代码组成,之后每个 \item“引号”实例都会自动具有正确的突出部分。否则,我将不得不手动更改文档中的数百个引号,这将非常痛苦。
LaTeX 版本
pdfTeX 3.141592653-2.6-1.40.23 (TeX Live 2022/dev)
LuaLaTeX 版本(这是此 MWP 中使用的内容,尽管看起来没有什么区别)
This is LuaHBTeX, Version 1.13.2 (TeX Live 2021)
最小工作示例:
%!TEX TS-program = lualatex
\documentclass[12pt]{article}
\usepackage{fontspec}
\usepackage{libertinus-otf}
\usepackage{lipsum}
\usepackage{csquotes} \MakeOuterQuote{"}
\usepackage{microtype}
\SetProtrusion
{ encoding = *}
{
% char right left
{.} = { , 1000},
{,} = { , 1000},
{«} = {1000, },
{»} = { , 1000},
{(} = {1000, },
{)} = { , 1000},
{-} = { , 500 },
\textquotedblleft
= {1000, },
\textquotedblright
= { , 1000},
\quotedblbase
= {1000, }
}
\begin{document}
\noindent
"\lipsum[1-1]"
\begin{enumerate}
\item First regular item, without quotations.
\item "Second item with quotation marks."
\item First multi-line item, contains content that is deliberately very, very long: "with multiple clauses that are designed to trigger a line break," so that it will wrap around and create multiple rows.
\item "Second multi-line item, but keep in mind it is different from the first since it is quoted. However it also contains content that is deliberately very, very long: with multiple clauses just like the first."
\item "Multiple quoted lines with a single item"
\item \leftprotrusion
"This is the second quoted line"
This is a third unquoted line.
\end{enumerate}
\end{document}
尝试修复:按以下方式添加 \items 似乎可以解决问题:
\item \leftprotrusion ``quotes''
但是,这意味着需要进行大量的手动调整。(顺便说一句,此解决方法也不能与一起使用csquotes
,也就是说\item \leftprotrusion "something in quotes"
也无法得到正确的结果。)
答案1
我现在没时间解释,所以现在我只会添加代码。(如果你在 2021-01-10 之后读到这篇文章,而到现在为止还没有用适当的解释来代替这篇文章,请随时提醒我。)
需要 LuaLaTeX。
%!TEX TS-program = lualatex
\documentclass[12pt]{article}
\usepackage{fontspec}
\usepackage{libertinus-otf}
\usepackage{lipsum}
\usepackage{csquotes} \MakeOuterQuote{"}
\usepackage{microtype}
\SetProtrusion
{ encoding = *}
{
% char right left
{.} = { , 1000},
{,} = { , 1000},
{«} = {1000, },
{»} = { , 1000},
{(} = {1000, },
{)} = { , 1000},
{-} = { , 500 },
\textquotedblleft
= {1000, },
\textquotedblright
= { , 1000},
\quotedblbase
= {1000, }
}
\directlua{
local func = luatexbase.new_luafunction'betterprotrusionboundary'
local my_whatsit = luatexbase.new_whatsit'betterprotrusionboundary'
local whatsit_id = node.id'whatsit'
local glyph_id = node.id'glyph'
local user_defined = node.subtype'user_defined'
token.set_lua('betterprotrusionboundary', func, 'protected')
local modes = tex.getmodevalues()
lua.get_functions_table()[func] = function()
local mode = tex.nest.top.mode
if mode < 0 then mode = -mode end
if modes[mode] == 'vertical' then
token.put_next(token.new(func, token.command_id'lua_call'))
return tex.forcehmode()
end
local n = node.new(whatsit_id, user_defined)
n.user_id = my_whatsit
n.type = 100
n.value = token.scan_int()
node.write(n)
end
luatexbase.add_to_callback('pre_linebreak_filter', function(head)
for n, s in node.traverse_id(whatsit_id, head) do if s == user_defined and n.user_id == my_whatsit then
assert(n.value == 1, 'boundarytypes beside 1 not yet supported')
if n.value & 1 == 1 then
for nn, id in node.traverse(n.next) do
local char, fid = node.is_glyph(nn)
if char then
token.put_next(token.create'lpcode', token.new(fid, token.command_id'set_font'), token.new(char, token.command_id'char_given'))
local width = (font.getparameters(fid).quad or 0) * token.scan_int() // 1000
if not (width == 0) then
local kern = node.new('kern', 1)
% local kern = node.new('margin_kern', 0)
kern.kern = -width
% kern.glyph = char
head = node.insert_after(head, n, kern)
end
break
elseif not node.protrusion_skippable(nn) then
break
elseif fid == whatsit_id and nn.subtype == user_defined and nn.user_id == my_whatsit and nn.value & 1 == 1 then
break
end
end
end
if n.value & 2 == 2 then
local nn = n.prev
while nn do
local char, fid = node.is_glyph(nn)
if char then
token.put_next(token.create'rpcode', token.new(fid, token.command_id'set_font'), token.new(char, token.command_id'char_given'))
local width = (font.getparameters(fid).quad or 0) * token.scan_int() // 1000
if not (width == 0) then
local kern = node.new('kern', 1)
% local kern = node.new('margin_kern', 1)
kern.kern = -width
% kern.glyph = char
head = node.insert_before(head, n, kern)
end
break
elseif not node.protrusion_skippable(nn) then
break
end
nn = nn.prev
end
end
head = node.remove(head, n)
end end
return head
end, 'betterprotrusionboundary')
}
\begin{document}
\showoutput
\renewcommand\leftprotrusion{\betterprotrusionboundary1\relax}
\noindent
"\lipsum[1-1]"
\begin{enumerate}
\item First regular item, without quotations.
\item "Second item with quotation marks."
\item First multi-line item, contains content that is deliberately very, very long: "with multiple clauses that are designed to trigger a line break," so that it will wrap around and create multiple rows.
\item "Second multi-line item, but keep in mind it is different from the first since it is quoted. However it also contains content that is deliberately very, very long: with multiple clauses just like the first."
\item "Multiple quoted lines with a single item"
\item \leftprotrusion ``quotes''
"This is the second quoted line"
This is a third unquoted line.
\end{enumerate}
\end{document}