[Tex/LaTex] Overlapping quotes with periods and commas

typography

I am looking for a way to automatically overlap quotes with periods and commas, as shown in the last two rows of the example image below. The methods I have found so far do this via macros; I want to do it completely automatically, without adding anything inside \begin and \end {document}, and ideally also work on automatically generated text like bibliographies.

Is there a package for this? Or a way to implement a dumb find/replace function in plain LaTeX macros?

\documentclass{minimal}

\begin{document}

``quoted.''

``quoted''.

``quoted\rlap{.}''

``quoted\rlap{''}.

\end{document}

pdf output

Update 4

Now I found a way to not rely on any arbitrary lengths for anything. The period/comma and quotes are perfectly overlapped, and the space after the construct seems to be the same as after the quote (double or single).

The initial case of the next bibliography field is uppercased without adding another period with \nopunct\printunit{\bibsentence}. This is trial and error, to me black magic, but it has worked for my purposes thus far.

update 4 pdf output

\documentclass{article}
\usepackage[american]{babel}
\usepackage[autostyle=true,autopunct=true]{csquotes}
\usepackage{biblatex}
\addbibresource{example.bib}

\begin{filecontents}{example.bib}
@incollection{smith:title,
    title = {The Title of the Part},
    author = {Smith, John},
    year = {2017},
    booktitle = {The Containing Book},
    publisher = {Some Publisher},
}

\end{filecontents}

\makeatletter

\DeclareQuotePunctuation{.,}
\DeclareAutoPunct{,.}

\DeclareFieldFormat%
        [article,inbook,incollection,inproceedings,patent,thesis,unpublished]%
        {title}{\textquote{#1}.\nopunct\printunit{\bibsentence}\addspace}

\renewcommand*{\mktextquote}[6]{#1#2#4{%
        \ifx#5.%
                \ifpunct{}{\rlap{#5}}%
        \else%
                \ifx#5,%
                        \ifpunct{}{\rlap{#5}}%
                \else%
                \fi%
        \fi%
        }#3#6%
}

\makeatother

\begin{document}

here are the \verb+\textquote+'s

``Hello. A kerning test.

``Hello'' A kerning test.

\textquote{Hello}. A kerning test.

\textquote{Hello.} A kerning test.

``Hello, A kerning test.

``Hello'' A kerning test.

\textquote{Hello}, A kerning test.

\textquote{Hello,} A kerning test.

\textcite{smith:title} is saying \textquote{a thing}.

\printbibliography

\end{document}

Update 3

I'm getting even closer. The only caveat I found so far is that you need to use \textquote{} instead of “ '' for normal text. Exclamation marks and such seem to work now.

\documentclass{article}
\usepackage[american]{babel}
\usepackage[autostyle=true,autopunct=true]{csquotes}
\usepackage{biblatex}
\addbibresource{example.bib}

\begin{filecontents}{example.bib}
@incollection{smith:title,
    title = {The Title of the Part},
    author = {Smith, John},
    year = {2017},
    booktitle = {The Containing Book},
    publisher = {Some Publisher},
}

\end{filecontents}

\makeatletter

\DeclareQuotePunctuation{.,}
\DeclareAutoPunct{,.}

\DeclareFieldFormat
  [article,inbook,incollection,inproceedings,patent,thesis,unpublished]
  {title}{\textquote{#1}.\nopunct}

\renewcommand{\mktextquote}[6]{#1#2#4{%
    \ifx#5.%
        \ifpunct{}{\rlap{#5}}%
    \else%
        \ifx#5,%
            \ifpunct{}{\rlap{#5}}%
        \else%
        \fi%
    \fi%
    }#3#6%
}

\makeatother

\begin{document}

here are the \verb+\textquote+'s

``Hello. A kerning test.

``Hello'' A kerning test.

\textquote{Hello}. A kerning test.

\textquote{Hello.} A kerning test.

``Hello, A kerning test.

``Hello'' A kerning test.

\textquote{Hello}, A kerning test.

\textquote{Hello,} A kerning test.

\textcite{smith:title} is saying \textquote{a thing}.

\printbibliography

\end{document}

Update 2

I'm getting somewhere with this, though it has some weird errors with the \adddot and similar commands defined at line 153 in biblatex.def.

\documentclass{article}
\usepackage[brazilian]{babel}
\usepackage[autostyle=true,autopunct=true]{csquotes}
\usepackage{biblatex}
\addbibresource{example.bib}

\begin{filecontents}{example.bib}
@inbook{smith2017,
    title = {The Title of the Part},
    author = {Smith, John},
    booktitle = {The Containing Book},
}
\end{filecontents}

\makeatletter

% original definition of textquote from csquotes, applied to enquote as well
\renewrobustcmd*{\enquote}{%
  \@ifstar
    {\csq@getcargs{\csq@tquote{}{}{\csq@iqopen@i}}}
    {\csq@getcargs{\csq@tquote{}{}{\csq@oqopen@i}}}}

% from biblatex.sty but replaced enquote with textquote
\renewrobustcmd*{\mkbibquote}{\textquote}
\protected\def\blx@imc@mkbibquote{%
  \blx@ifuspunct\blx@usquote\textquote}

\newcommand\EatDot[2]{}
\DeclareFieldFormat[inbook]{title}{\textquote{#1}.\printunit{\space}}

\uspunctuation
\DeclareAutoPunct{.,}
\DeclareQuotePunctuation{.,}
\renewcommand{\mktextquote}[6]{#1#2#4{%
    \ifx#5.%
        \rlap{#5}%
    \else%
        \ifx#5,%
            \rlap{#5}%
        \else%
        \fi%
    \fi%
    }#3#6%
}

\makeatother

\begin{document}

here are the \verb+\textquote+'s

``Hello. A kerning test.

``Hello'' A kerning test.

\textquote{Hello}. A kerning test.

\textquote{Hello.} A kerning test.

``Hello, A kerning test.

``Hello'' A kerning test.

\textquote{Hello}, A kerning test.

\textquote{Hello,} A kerning test.

\textcite{smith2017} is saying \textquote{a thing}.

\printbibliography

here are the \verb+\enquote+'s

``Hello. A kerning test.

``Hello'' A kerning test.

\enquote{Hello}. A kerning test.

\enquote{Hello.} A kerning test.

``Hello, A kerning test.

``Hello'' A kerning test.

\enquote{Hello}, A kerning test.

\enquote{Hello,} A kerning test.

\end{document}

Update 1

Aside from some confusing spacing errors on the single quotes, and row 4 and 8 in the example image below not working, I'm getting closer. The tricky part is the double quote, as it consists of not two but three characters, making \@ifnextchar useless(?).

\documentclass{minimal}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}

\makeatletter

\let\normalperiod=.%
\catcode`\.=\active
\let\normalcomma=,%
\catcode`\,=\active
\let\normalquote='%
\catcode`\'=\active

\let\tqdr=\textquotedblright%

\def.{\@ifnextchar'{\rlap{\normalperiod{}}}{\normalperiod{}}}
\def,{\@ifnextchar'{\rlap{\normalcomma{}}}{\normalcomma{}}}

\def¶#1{}% removes the character following it, useful to remove the extraneous
         % single quote after the double quote gets substituted

\def'{%
    \@ifnextchar.{%
    \rlap{\textquoteright{}}%
    } {%
    \@ifnextchar,{%
        \rlap{\textquoteright{}}%
    } {%
        \@ifnextchar'{%
        \textquotedblright{}¶%
        } {%
        \textquoteright{}%
        }%
    }%
    }%
}%

\makeatother

\begin{document}

`This is a quote.' and this is some text

`This is a quote'. and this is some text

``This is a quote.'' and this is some text

``This is a quote''. and this is some text

`This is a quote,' and this is some text

`This is a quote', and this is some text

``This is a quote,'' and this is some text

``This is a quote'', and this is some text

`This is a quote?' and this is some text

`This is a quote'? and this is some text

``This is a quote?'' and this is some text

``This is a quote''? and this is some text

``This is a quote'' and this is some text

`This is a quote' and this is some text

\end{document}

pdf output update

Best Answer

The following example works for me with LuaLaTeX (version 0.95 in TeXlive 2016):

\documentclass{minimal}
\directlua{
fonts.handlers.otf.addfeature {
    name = "kern1",
    {
        type = "pair",
        data = {
            [0x002E] = {
                [0x201D] = { false, { -45, 0, -45, 0 } },
                [0x2019] = { false, { -45, 0, -45, 0 } },
            },
        }
    }
}
}
\usepackage{fontspec}
\setmainfont{Latin Modern Roman}[Ligatures=TeX,RawFeature=+kern1]
\begin{document}
``quoted.''
`quoted.'
\end{document}

It's based on this question and adds some negative kerning between the period and closing quotes. I don't know if I can use some relative dimensions like the width of a character, so -45 is selected by some trial and error. The result looks like this:

period-quote kerns