TransWikia.com

babelshorthand "= does not work with BibLaTeX?

TeX - LaTeX Asked on August 13, 2021

I am using LaTeX with package babel and biblatex regularly in my mostly german language based documents.

Recently, I added a similar note to all of my BibTeX entries, whenever the entry referred to an PDF documentation, that is delivered with TeXLive distribution. Translated in english, the note should read something this:

note={Part of the online documentation of TeXLive distribution, file
      url{<filename>.pdf}},

This is the german text I use:

note={Bestandteil der Online"=Dokumentation von TeXLive, 
      Datei url{<filename>.pdf}},

I have added the babel shorthand "= which is enabled in german or ngerman languages. But even when I wrap the note text with an foreignlanguage{ngerman}{...} I can’t get the shorthand to be replaced by a normal hyphen, as I expected it.

If I replace it with the normal hyphen sign, the second word "Dokumentation" can’t be broken by LaTeX anymore and hence will often cause an overfull hbox error instead.

Here is an MWE (in german language of course).

documentclass[english,ngerman]{scrartcl}

usepackage[style=numeric]{biblatex}
usepackage{babel}
usepackage{csquotes}
usepackage{dtk-logos}

addbibresource{jobname.bib}

begin{filecontents}{jobname.bib}
@Manual{class:scrguide,
  title      = {KOMA-Script},
  author     = {Kohm, Markus},
  month      = May,
  year       = 2016,
  url        = {http://www.komascript.de/~mkohm/scrguide.pdf},
  langid     = {ngerman},
  note       = {Bestandteil der Online"=Dokumentation von
                  TeXLive, Datei url{scrguide.pdf}},
  keywords   = {manual},
}
end{filecontents}


begin{document}
Der Eintrag~cite{class:scrguide} aus meiner
Literatur"=Datenbank erscheint im Quellen"=Verzeichnis leider mit
einem verb|"=| in der Ausgabe.

printbibliography%

end{document}

(Dear german reader: please ignore the silly examples of the shorthand "= in the example above. They were inserted to prove, that they are replaced by the normal hyphen.)

This is the output:

enter image description here

How to solve this dilemma?

One Answer

Active characters have been widely used for years in French, German, Dutch, etc. and produce some nasty side-effects from time to time. With the TeX or pdfTeX engines, you have to live with them.

Fortunately, LuaTeX provides tools to get rid of them, I did so for babel-french and looking at your report which follows this one by (Denis Bitouzé), I decided to check if the German dblquote could also be left inactive with LuaTeX.

I wrote the following 'dblquote.sty' file as a ``proof of concept''; as is, it seems to work but as I am not a native German speaker, I can't tell if this Lua code (after improvement) could replace the current code in (n)german.ldf files.

To give it a try, just add usepackage{dblquote} to your preamble and a line shorthandoff{"} just after begin{document}.

ProvidesPackage{dblquote}
                [2021/07/04 v.0.01 Daniel Flipo]
NeedsTeXFormat{LaTeX2e}[2021/06/01]
ifdefineddirectlua
  RequirePackage{luatexbase,luacode}
else
  PackageError{This package is meant for LuaTeX only! Aborting}
               {No more information available, sorry!}
fi
newattributeDQ     DQ=1 relax
newattributetoss   toss=0 relax
ifluatex
  defmdqon{DQ=1relax}
  defmdqoff{DQ=0relax}
%else
%  defmdqon{shorthandon{"}}
%  defmdqoff{shorthandoff{"}}
fi

begin{luacode}
  dblquote = { }
  local DQ            = luatexbase.attributes['DQ']
  local toss          = luatexbase.attributes['toss']
  local has_attribute = node.has_attribute
  local traverse_id   = node.traverse_id
  local remove        = node.remove
  local insert_before = node.insert_before
  local insert_after  = node.insert_after
  local current_attr  = node.current_attr
  local new_node      = node.new
  local copy_node     = node.copy
  local copy_list     = node.copy_list
  local node_id       = node.id
  local DISC          = node_id("disc")
  local HLIST         = node_id("hlist")
  local GLUE          = node_id("glue")
  local GLYPH         = node_id("glyph")
  local KERN          = node_id("kern")
  local PENALTY       = node_id("penalty")
  local nobreak       = new_node(PENALTY,0)
  nobreak.penalty     = 10000
  local hskip0        = new_node(GLUE,0)

-- Replace "a with ä etc.
dblquote.replace = function (head)
  local t = { }
    t[string.byte("'")] = 0x201C
    t[0x2019]           = 0x201C    -- quoteright (Ligatures=TeX)
    t[string.byte("`")] = 0x201E
    t[0x2018]           = 0x201E    -- quoteleft  (Ligatures=TeX)
    t[string.byte("<")] = 0x00AB
    t[string.byte(">")] = 0x00BB
    t[string.byte("A")] = 0x00C4
    t[string.byte("a")] = 0x00E4
    t[string.byte("E")] = 0x00CB
    t[string.byte("e")] = 0x00EB
    t[string.byte("I")] = 0x00CF
    t[string.byte("i")] = 0x00EF
    t[string.byte("O")] = 0x00D6
    t[string.byte("o")] = 0x00F6
    t[string.byte("U")] = 0x00DC
    t[string.byte("u")] = 0x00FC
    t[string.byte("S")] = 0x0053
    t[string.byte("Z")] = 0x005A
  for item in traverse_id(GLYPH, head) do
    local lang = item.lang
    local char = item.char
    local DQon = has_attribute(item, DQ)
    DQon = DQon and DQon > 0
    local tossON = has_attribute(item, toss)
    tossON = tossON and tossON > 0
    if (lang == DE or lang == DEn) and DQon and
       (char == 0x201D or char == 0x22) then
       local next  = item.next
       local nchar = next.char
       if tossON then
          t[string.byte("s")] = string.byte("s")
          t[string.byte("z")] = string.byte("z")
       else
          t[string.byte("s")] = 0x00DF
          t[string.byte("z")] = 0x00DF
       end
       if t[nchar] then
          next.char = t[nchar]
          if t[nchar] == string.byte("s") or t[nchar] == string.byte("z") then
             item.char = string.byte("s")
          elseif t[nchar] == string.byte("S") or t[nchar] == string.byte("Z") then
             item.char = string.byte("S")
          else
             head = remove(head,item)
          end
       end
    end
  end
  return head
end
-- Hyphenation and ligatures
dblquote.disc = function (head)
  local to = { }
    to[string.byte("f")] = true
    to[string.byte("F")] = true
    to[string.byte("l")] = true
    to[string.byte("L")] = true
    to[string.byte("m")] = true
    to[string.byte("M")] = true
    to[string.byte("n")] = true
    to[string.byte("N")] = true
    to[string.byte("p")] = true
    to[string.byte("P")] = true
    to[string.byte("r")] = true
    to[string.byte("R")] = true
    to[string.byte("t")] = true
    to[string.byte("T")] = true
 for item in node.traverse_id(GLYPH, head) do
    local lang = item.lang
    local char = item.char
    local DQon = has_attribute(item, DQ)
    DQon = DQon and DQon > 0
    if DQon and (char == 0x201D or char == 0x22) then
       -- traditionnal German only
       if lang == DE then
          local n  = item.next
          local nn  = n.next
          local nchar = n.char
          local prev  = item.prev
          -- "ck and "CK
          if nchar == string.byte("c") or nchar == string.byte("C") then
             head = remove(head,n)
             head = remove(head,item)
             -- building d.pre looks clumsy, to be improved!
             local pre = new_node(HLIST,0)
             local hyph = copy_node(nn)
             hyph.char = string.byte("-")
             local first = copy_node(nn)
             local second = copy_node(hyph)
             pre.head = first
             first.next = second
             second.next = nil
             ----------------------------------------------
             local d = new_node(DISC,0)
             d.attr = current_attr()
             d.penalty = tex.hyphenpenalty
             d.pre = copy_list(first)
             d.replace = copy_node(n)
             insert_after(head,prev,copy_node(d))
             insert_after(head,prev,copy_node(nobreak))
             insert_before(head,nn,copy_node(nobreak))
             insert_before(head,nn,copy_node(hskip0))
          -- all others "ff, "FF, etc.
          elseif to[nchar] and nn.char and nn.char == n.char then
             head = remove(head,n)
             head = remove(head,item)
             local d = new_node(DISC,0)
             d.attr = current_attr()
             d.penalty = tex.hyphenpenalty
             -- building d.pre looks clumsy, to be improved!
             local pre = new_node(HLIST,0)
             local hyph = copy_node(nn)
             hyph.char = string.byte("-")
             local first, second, third
             -- "f is special
             if nchar == string.byte("f") then
                local ff = copy_node(n)
                ff.char = 0xFB00            -- ligature "ff"
                first = copy_node(ff)
                second = copy_node(hyph)
                second.next = nil
                d.post = copy_node(n)
                d.replace = copy_node(ff)
                head = remove(head,nn)
             else
                first = copy_node(n)
                second = copy_node(n)
                third = copy_node(hyph)
                third.next = nil
                d.replace = copy_node(n)
             end
             pre.head = first
             first.next = second
             second.next = third
             --
             d.attr = current_attr()
             d.pre = copy_list(first)
             d.penalty = tex.hyphenpenalty
             insert_after(head,prev,copy_node(hskip0))
             insert_after(head,prev,copy_node(nobreak))
             insert_after(head,prev,copy_node(d))
             insert_after(head,prev,copy_node(nobreak))
          end
       end
       -- modern and traditionnal German
       if lang == DE or lang == DEn then
          local n  = item.next
          local nchar
          if n.id == GLYPH then
             nchar = n.char
          elseif n.id == PENALTY then   -- n = node ~
             nchar = 1
          end
          local prev  = item.prev
          if nchar == string.byte("-") then
             local d = new_node(DISC,0)
             d.attr = current_attr()
             d.penalty = tex.hyphenpenalty
             local hyph = copy_node(n)
             hyph.char = string.byte("-")
             d.pre = copy_node(hyph)
             head = remove(head,n)
             head = remove(head,item)
             insert_after(head,prev,copy_node(hskip0))
             insert_after(head,prev,copy_node(nobreak))
             insert_after(head,prev,copy_node(d))
             insert_after(head,prev,copy_node(nobreak))
          elseif nchar == string.byte("|") then
             local d = new_node(DISC,0)
             d.attr = current_attr()
             d.penalty = tex.hyphenpenalty
             local hyph = copy_node(n)
             hyph.char = string.byte("-")
             d.pre = copy_node(hyph)
             local k = new_node(KERN,1)
             k.attr = current_attr()
             k.kern = 20000
             d.replace = copy_node(k)
             head = remove(head,n)
             head = remove(head,item)
             insert_after(head,prev,copy_node(hskip0))
             insert_after(head,prev,copy_node(nobreak))
             insert_after(head,prev,copy_node(d))
             insert_after(head,prev,copy_node(nobreak))
          elseif nchar == string.byte('"') then
             head = remove(head,n)
             head = remove(head,item)
             insert_after(head,prev,copy_node(hskip0))
          elseif nchar == 1 then    -- ("~)
             local hyph = copy_node(item)
             hyph.char = string.byte("-")
             head = remove(head,n.next)
             head = remove(head,n)
             head = remove(head,item)
             insert_after(head,prev,copy_node(nobreak))
             insert_after(head,prev,copy_node(hyph))
          elseif nchar == string.byte("=") then
             local hyph = copy_node(n)
             hyph.char = string.byte("-")
             head = remove(head,n)
             head = remove(head,item)
             insert_after(head,prev,copy_node(hskip0))
             insert_after(head,prev,copy_node(hyph))
             insert_after(head,prev,copy_node(nobreak))
          elseif nchar == string.byte("/") then
             local d = new_node(DISC,0)
             d.attr = current_attr()
             d.penalty = tex.hyphenpenalty
             local slash = copy_node(n)
             slash.char = string.byte("/")
             d.pre = copy_node(slash)
             d.replace = copy_node(slash)
             head = remove(head,n)
             head = remove(head,item)
             insert_after(head,prev,copy_node(hskip0))
             insert_after(head,prev,copy_node(nobreak))
             insert_after(head,prev,copy_node(d))
             insert_after(head,prev,copy_node(hskip0))
             insert_after(head,prev,copy_node(nobreak))
          end
       end
    end
  end
  return head
end
return dblquote.replace, dblquote.disc
end{luacode}

directlua{%
   DE = thel@german ;
   DEn = thel@ngerman ;
   luatexbase.add_to_callback
           ("pre_linebreak_filter",dblquote.disc,"discretionary",1)
   luatexbase.add_to_callback
           ("pre_linebreak_filter",dblquote.replace,"replace",1)
   luatexbase.add_to_callback
           ("hpack_filter",dblquote.replace,"replace")
  }

endinput`

Answered by Daniel Flipo on August 13, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP