% \iffalse meta-comment % % Copyright (C) 2011-2015 by Raphaël Pinson % --------------------------------------------------------------------------- % This work may be distributed and/or modified under the % conditions of the LaTeX Project Public License, either version 1.3 % of this license or (at your option) any later version. % The latest version of this license is in % http://www.latex-project.org/lppl.txt % and version 1.3 or later is part of all distributions of LaTeX % version 2005/12/01 or later. % % This work has the LPPL maintenance status `maintained'. % % The Current Maintainer of this work is Raphaël Pinson. % % This work consists of the files impnattypo.dtx and impnattypo.ins % and the derived filebase impnattypo.sty. % % \fi % % \iffalse %<*driver> \ProvidesFile{impnattypo.dtx} % %\NeedsTeXFormat{LaTeX2e}[1999/12/01] %\ProvidesPackage{impnattypo} %<*package> [2019/03/04 1.5 Typographic utilities inspired by the French Imprimerie Nationale] % % %<*driver> \expandafter\ifx\csname ifenglish\endcsname\relax \expandafter\newif\csname ifenglish\endcsname \englishtrue \fi \documentclass{ltxdoc} \ifenglish \usepackage[english]{babel} \else \usepackage[french]{babel} \fi \usepackage{fontspec} \usepackage[svgnames]{xcolor} \setmainfont{Linux Libertine O} \usepackage{metalogo} \usepackage[draft,hyphenation,nosingleletter,parindent,lastparline,homeoarchy,rivers]{impnattypo}[2018/06/04] \usepackage[all]{nowidow} \EnableCrossrefs \CodelineIndex \RecordChanges \begin{document} \DocInput{impnattypo.dtx} \PrintChanges \PrintIndex \end{document} % % \fi % % \CheckSum{121} % % \CharacterTable % {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z % Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z % Digits \0\1\2\3\4\5\6\7\8\9 % Exclamation \! Double quote \" Hash (number) \# % Dollar \$ Percent \% Ampersand \& % Acute accent \' Left paren \( Right paren \) % Asterisk \* Plus \+ Comma \, % Minus \- Point \. Solidus \/ % Colon \: Semicolon \; Less than \< % Equals \= Greater than \> Question mark \? % Commercial at \@ Left bracket \[ Backslash \\ % Right bracket \] Circumflex \^ Underscore \_ % Grave accent \` Left brace \{ Vertical bar \| % Right brace \} Tilde \~} % % % \changes{1.5}{2019/03/04}{Fix support for TexLive 2016 (new luatex % compatibility). Thanks to Michal Hoftich} % \changes{1.4}{2015/02/25}{Fix release date} % \changes{1.3}{2015/02/25}{Fix French documentation} % \changes{1.2}{2013/06/18}{Fix French documentation} % \changes{1.1}{2013/06/17}{Fix French documentation} % \changes{1.0}{2011/09/20}{Improve documentation, simplify internal variables} % \changes{0.9}{2011/09/20}{River detection returns false by default} % \changes{0.8}{2011/09/20}{Add river detection} % \changes{0.7}{2011/09/20}{Add homoioteleuton detection} % \changes{0.6}{2011/09/19}{Words contain at least one character} % \changes{0.5}{2011/09/19}{Add homeoarchy detection} % \changes{0.4}{2011/09/15}{Add draft mode} % \changes{0.3}{2011/09/13}{Add parindent and lastparline options} % \changes{0.2}{2011/09/13}{Add nosingleletter option} % \changes{0.1}{2011/09/11}{First version} % % \DoNotIndex{\newcommand,\newenvironment} % % \providecommand*{\url}{\texttt} % \GetFileInfo{impnattypo.dtx} % \ifenglish % \title{The \textsf{impnattypo} package} % \else % \title{Le paquet \textsf{impnattypo}} % \fi % \author{Raphaël Pinson \\ \url{raphink@gmail.com}} % \ifenglish % \date{\fileversion~from \filedate} % \else % \date{\fileversion~en date du \filedate} % \fi % % \maketitle % % \section{Introduction} % % \ifenglish % When it comes to French typography, % the \emph{Lexique des règles typographiques en usage à l'Imprimerie Nationale} % is a definite reference. % % While the majority of the recommendations of this book has been implemented % in the \textsf{frenchb} module for \textsf{babel}, % other recommendations still deserve to be automatized in order to be % implemented in \LaTeX. % % Such is the original goal of this package, initiated by a question on the % tex.stackexchange.com\footnote{\url{http://tex.stackexchange.com/questions/20493/french-typography-recommendations}} website, % and which implements several of the rules listed in this booklet % so as to make them more easily applicable to texts edited with \LaTeX. % % As this package grew, functionalities were added, including some % that were not directly related to the booklet, but improved % the typographic quality of documents. % \else % En matière de typographie française, % le \emph{Lexique des règles typographiques en usage à l'Imprimerie Nationale} % est une référence incontournable. % % Si la majorité des recommandations de cet ouvrage est implémentée dans % le module \textsf{frenchb} pour \textsf{babel}, % certaines autres recommandations méritent encore d'être automatisées pour être % implémentées en \LaTeX. % % C'est le but original de ce paquet, initié par une question sur le site % tex.stackexchange.com\footnote{\url{http://tex.stackexchange.com/questions/20493/french-typography-recommendations}}, % et qui implémente plusieurs règles édictées dans ce lexique % afin de les rendre plus facilement applicables aux textes édités avec \LaTeX. % % Au fur et à mesure que ce paquet a grandi, des fonctionnalités sont venues % s'ajouter, dont certaines ne sont pas directement liées au lexique, % mais améliorent la qualité typographique des documents. % \fi % % \ifenglish % \section{Usage} % \else % \section{Utilisation} % \fi % % \ifenglish % In order to use the \texttt{impnattypo} package, use the following line: % \else % Pour utiliser le paquet \texttt{impnattypo}, entrez la ligne: % \fi % % \begin{verbatim} % \usepackage[]{impnattypo} % \end{verbatim} % % \ifenglish % The package options are described in the following sections. % \else % Les options du paquet sont décrites dans les sections suivantes. % \fi % % \ifenglish % \subsection{Hyphenation} % % \DescribeMacro{hyphenation} % Besides the general hyphenation rules, the booklet indicates % that we should ``prevent hyphenation of words on more % than two consecutive lines.'' % % In order to simplify the code, the suggested implementation % strongly discourages hyphenation at the end of pages, as well % as hyphenation on two consecutive lines. % % To active this functionality, use the \texttt{hyphenation} option: % \else % \subsection{Césures} % % \DescribeMacro{hyphenation} % En dehors des règles générales de coupure des mots, le lexique indique % qu'il faut \og [éviter] les coupures de mots sur plus de trois lignes % consécutives. \fg{} % % Afin de simplifier le code, l'implémentation proposée décourage fortement % les césures en fin de page, ainsi que les césures sur deux lignes % consécutives. % % Pour activer cette fonctionnalité, utilisez l'option \texttt{hyphenation}: % \fi % % \begin{verbatim} % \usepackage[hyphenation]{impnattypo} % \end{verbatim} % % % \ifenglish % \subsection{Paragraph formatting} % % \DescribeMacro{parindent} % The booklet advises to indent paragraphs by 1em. % This |\parindent| setting can be achieved by using % the \texttt{parindent} option: % \else % \subsection{Formatage des paragraphes} % % \DescribeMacro{parindent} % Le lexique conseille une indentation des paragraphes de 1em. % Ce réglage de |\parindent| peut être obtenu par l'utilisation % de l'option \texttt{parindent}: % \fi % % \begin{verbatim} % \usepackage[parindent]{impnattypo} % \end{verbatim} % % \DescribeMacro{lastparline} % \ifenglish % Moreover, it is indicated in the ``Hyphenation'' section % that ``the last line of a paragraph must contain a word or % the end of a word of a width at least equal to the double % of the indent of the next paragraph.'' Since implementing % this solution exactly is quite tricky, the \texttt{lastparline} % option ensures that the last line of a paragraph is at least % as long as the double value of |\parindent|.\footnote{\url{http://tex.stackexchange.com/questions/28357/ensure-minimal-length-of-last-line}} % % When \LuaTeX{} is used, the solution provided by Patrick Gundlach\footnote{\url{http://tex.stackexchange.com/questions/28357/ensure-minimal-length-of-last-line/28361\#28361}} % is used. With other rendering engines, it is the native solution provided by % Enrico Gregorio\footnote{\url{http://tex.stackexchange.com/questions/28357/ensure-minimal-length-of-last-line/28358\#28358}} % that serves as an implementation: % \else % De plus, il est indiqué dans la section \og Coupure des mots \fg{} % que \og la dernière ligne d'un alinéa doit comporter un mot ou % une fin de mot de longueur au moins égale au double du renfoncement % de l'alinéa suivant. \fg{} À défaut d'implémenter exactement cette % solution, l'option \texttt{lastparline} s'assure que la dernière % ligne d'un alinéa est au moins aussi longue que le double % de la valeur de |\parindent|.\footnote{\url{http://tex.stackexchange.com/questions/28357/ensure-minimal-length-of-last-line}} % % Lorsque \LuaTeX{} est utilisé, la solution de Patrick Gundlach\footnote{\url{http://tex.stackexchange.com/questions/28357/ensure-minimal-length-of-last-line/28361\#28361}} % est utilisée. Avec les autres moteurs de rendu, c'est la solution native de % Enrico Gregorio\footnote{\url{http://tex.stackexchange.com/questions/28357/ensure-minimal-length-of-last-line/28358\#28358}} % qui fait office d'implémentation: % \fi % % \begin{verbatim} % \usepackage[lastparline]{impnattypo} % \end{verbatim} % % \ifenglish % When the \texttt{draft} option is activated and \LuaTeX{} is used, % the inserted ties are colored in % {\color{\intlastparlinecolor}\intlastparlinecolor}. % The color can be tuned with the \texttt{lastparlinecolor} option. % \else % Lorsque l'option \texttt{draft} est activée et que \LuaTeX{} est utilisé, % les espaces insécables insérés sont colorés en % {\color{\intlastparlinecolor}\intlastparlinecolor}. % La couleur utilisée peut être ajustée par l'option \texttt{lastparlinecolor}. % \fi % % \DescribeMacro{nosingleletter} % \ifenglish % It is also recommended to avoid hyphenation points that would isolate a single letter. % The solution proposed by Patrick Gundlach\footnote{\url{http://tex.stackexchange.com/questions/27780/one-letter-word-at-the-end-of-line}} % allows to fix this by using \LuaTeX. To activate this functionality, % you can use the \texttt{nosingleletter} option: % \else % Il est également recommandé d'éviter les coupures isolant une lettre. % La solution proposée par Patrick Gundlach\footnote{\url{http://tex.stackexchange.com/questions/27780/one-letter-word-at-the-end-of-line}} % permet de remédier à cela en utilisant \LuaTeX. Pour activer cette % fonctionalité, il faut utiliser l'option \texttt{nosingleletter}: % \fi % % \begin{verbatim} % \usepackage[nosingleletter]{impnattypo} % \end{verbatim} % % \ifenglish % When this option is activated, only \LuaTeX{} (with the |lualatex| command) % can render the document. % % When the \texttt{draft} option is activated, % the inserted ties are colored in % {\color{\intnosinglelettercolor}\intnosinglelettercolor}. % The color can be tuned by setting the \texttt{nosinglelettercolor} option. % \else % Lorsque cette option est activée, seul \LuaTeX{} (via la commande |lualatex|) % pourra effectuer le rendu du document. % % Lorsque l'option \texttt{draft} est activée, % les espaces insécables insérés sont colorés en % {\color{\intnosinglelettercolor}\intnosinglelettercolor}. % La couleur utilisée peut être ajustée par l'option \texttt{nosinglelettercolor}. % \fi % % \DescribeMacro{homeoarchy} % \ifenglish % When two consecutive lines begin (homeoarchy) or end (homoioteleuton) % with the same word or series of letters, % it can confuse the reader, so this has to be avoided. % % Fixing this problem automatically is very complex % and generally not a good idea.\footnote{\url{http://tex.stackexchange.com/questions/27588/repetition-of-a-word-on-two-lines}} % For this reason, the \texttt{homeoarchy} option in this package % only detects and highlights them. Fixing them will usually % be a matter of introducing ties in the paragraph: % \else % Lorsque deux lignes consécutives commencent ou finissent % par le même mot ou la même série de lettres, % cela peut induire le lecteur en erreur et cela est donc à éviter. % % La correction automatique de ce phénomène est très complexe % et en général non souhaitable.\footnote{\url{http://tex.stackexchange.com/questions/27588/repetition-of-a-word-on-two-lines}} % C'est pourquoi l'option % \texttt{homeoarchy} de ce paquet se contente % de les détecter et de les afficher. Leur correction % consistera en général en l'introduction d'une % espace insécable dans le paragraphe: % \fi % % \begin{verbatim} % \usepackage[homeoarchy]{impnattypo} % \end{verbatim} % % \ifenglish % When this option is activated, only \LuaTeX{} (with the |lualatex| command) % can render the document. % % This option is only effective if the \texttt{draft} option is activated. % % The inserted ties are colored with two colors: % \begin{itemize} % \item Entire words are colored in % {\color{\inthomeoarchywordcolor}\inthomeoarchywordcolor} % and this color can be set with the \texttt{homeoarchywordcolor} option; % \item Partial words are colored in % {\color{\inthomeoarchycharcolor}\inthomeoarchycharcolor} % and this color can be set by means of the \texttt{homeoarchycharcolor} option; % \end{itemize} % % A glyph sequence is considered problematic when: % % \begin{itemize} % \item The number of entire matching words is greater than \inthomeoarchymaxwords. % This parameter can be tuned with the \texttt{homeoarchymaxwords} option; % \item The number of matching characters is greaterr than \inthomeoarchymaxchars. % This parameter can be tuned with the \texttt{homeoarchymaxchars} option; % \end{itemize} % \else % Lorsque cette option est activée, seul \LuaTeX{} (via la commande |lualatex|) % pourra effectuer le rendu du document. % % Cette option n'est effective que si l'option \texttt{draft} est activée. % % Les espaces insécables insérées sont colorées de deux couleurs: % % \begin{itemize} % \item Les mots entiers sont colorés en % {\color{\inthomeoarchywordcolor}\inthomeoarchywordcolor} % et cette couleur peut être ajustée par l'option \texttt{homeoarchywordcolor}; % \item Les mots partiels sont colorés en % {\color{\inthomeoarchycharcolor}\inthomeoarchycharcolor} % et cette couleur peut être ajustée par l'option \texttt{homeoarchycharcolor}; % \end{itemize} % % Une séquence de glyphes est considérée comme problématique si: % % \begin{itemize} % \item Le nombre de mots entiers trouvés dans les deux lignes consécutives est supérieur à \inthomeoarchymaxwords. % Ce paramètre peut être ajusté par l'option \texttt{homeoarchymaxwords}; % \item Le nombre de caractères trouvés dans les deux lignes consécutives est supérieur à \inthomeoarchymaxchars. % Ce paramètre peut être ajusté par l'option \texttt{homeoarchymaxchars}; % \end{itemize} % \fi % % % \DescribeMacro{rivers} % \ifenglish % A river is a vertical alignment of spaces in a paragraph. % The \texttt{rivers} option allows to color rivers so as to % identify them. This option does not fix the detected rivers: % \else % Une lézarde est un alignement vertical d'espaces dans un paragraphe. % L'option \texttt{rivers} permet de colorer les lézardes afin de les % identifier. Cette option ne corrige pas les lézardes détectées: % \fi % % \begin{verbatim} % \usepackage[rivers]{impnattypo} % \end{verbatim} % % \ifenglish % When this option is activated, only \LuaTeX{} (with the |lualatex| command) % can render the document. % % This option is only effective if the \texttt{draft} option is activated. % % The inserted ties are colored in % {\color{\intriverscolor}\intriverscolor}. % This color can be tuned by means of the \texttt{riverscolor} option. % \else % Lorsque cette option est activée, seul \LuaTeX{} (via la commande |lualatex|) % pourra effectuer le rendu du document. % % Cette option n'est effective que si l'option \texttt{draft} est activée. % % Les espaces insécables insérées sont colorées en % {\color{\intriverscolor}\intriverscolor}. % Cette couleur peut être ajustée par l'option \texttt{riverscolor}. % \fi % % \ifenglish % \subsection{Chapter numbering} % \else % \subsection{Numérotation des chapitres} % \fi % % \DescribeMacro{frenchchapters} % \ifenglish % When it comes to chapter numbering, the booklet indicates: % ``In a title, chapter numbers are typeset in roman capital numbers, % except for the ordinal `premier' written in letters in spite % of the current fashion to write it in the cardinal form Chapter I.'' % % The \texttt{frenchchapters} option of the package implements % this recommendation: % \else % Concernant la numérotation des chapitres, le lexique indique: % \og Dans un titre, on compose en chiffres romains grandes capitales % les numéros de chapitres, à l'exception de l'ordinal \og premier \fg{} % en toutes lettres malgré la tendance actuelle qui tend à lui substituer % la forme cardinale Chapitre I. \fg{} % % L'option \texttt{frenchchapters} du paquet implémente cette recommandation: % \fi % % \begin{verbatim} % \usepackage[frenchchapters]{impnattypo} % \end{verbatim} % % \ifenglish % Should you wish to use the ordinal form `premier' % without using roman numbers for chapter numbering, % you can redefine the \texttt{frenchchapter} macro, for example: % % \begin{verbatim} % \let\frenchchapter\arabic % use arabic numbers % \let\frenchchapter\babylonian % use babylonian numbers % \end{verbatim} % \else % Si vous souhaitez bénéficier de la forme ordinale \og premier \fg{} % sans pour autant utiliser une numérotation des chapitres en chiffres romains, % il est possible de redéfinir la macro \texttt{frenchchapter}, par exemple: % % \begin{verbatim} % \let\frenchchapter\arabic % numérotation en chiffres arabes % \let\frenchchapter\babylonian % numérotation en chiffres babyloniens % \end{verbatim} % \fi % % % \ifenglish % \subsection{Widows and Orphans} % % It is recommended not to leave widows and orphans in a document. % For this reason, we recommend you use the |nowidow| package: % \else % \subsection{Lignes orphelines et veuves} % % Il est fortement recommandé de ne pas laisser de lignes orphelines % dans un document. Pour cela, nous vous conseillons d'utiliser le paquet % |nowidow|: % \fi % % \begin{verbatim} % \usepackage[all]{nowidow} % \end{verbatim} % % \ifenglish % See the package documentation for more options. % \else % Voir la documentation de ce paquet pour plus d'options. % \fi % % % \ifenglish % \subsection{Draft mode} % The \textsf{impnattypo} package features a draft mode % allowing to visualize the penalties (ties) inserted by the % \texttt{nosingleletter} and \texttt{lastparline} options, % as well as the information added by the \texttt{homeoarchy} % and \texttt{rivers} options. In draft mode, % places where ties were inserted are indicated % by colored squares. % % To activate the draft mode, use the \texttt{draft} option, % for example: % \else % \subsection{Mode brouillon} % % \DescribeMacro{draft} % Le paquet \textsf{impnattypo} dispose d'un mode brouillon % permettant de visualiser les pénalités (espaces insécables) % ajoutés par les options \texttt{nosingleletter} et % \texttt{lastparline}, ainsi que les informations % ajoutées par les options \texttt{homeoarchy} % et \texttt{rivers}. En mode brouillon, % les emplacements des espaces insécables insérés % sont marqués par des rectangles de couleur. % % Pour activer le mode brouillon, utilisez l'option \texttt{draft}, % par exemple: % \fi % % \begin{verbatim} % \usepackage[draft,lastparline]{impnattypo} % \end{verbatim} % % \ifenglish % This document is generated with the \texttt{draft} option on % in order to demonstrate its effects. % \else % Cet document est générée avec l'option \texttt{draft} afin d'en % montrer les effets. % \fi % % % \StopEventually{} % % \section{Implementation} % % \iffalse %<*package> % \fi % % \begin{macrocode} \ProvidesPackage{impnattypo} \RequirePackage{ifluatex} \RequirePackage{kvoptions} \SetupKeyvalOptions{ family=impnattypo, prefix=int, } \DeclareBoolOption{draft} \DeclareBoolOption{frenchchapters} \DeclareBoolOption{hyphenation} \DeclareBoolOption{nosingleletter} \DeclareBoolOption{parindent} \DeclareBoolOption{lastparline} \DeclareBoolOption{homeoarchy} \DeclareBoolOption{rivers} \DeclareStringOption[red]{homeoarchywordcolor} \DeclareStringOption[orange]{homeoarchycharcolor} \DeclareStringOption[brown]{nosinglelettercolor} \DeclareStringOption[teal]{lastparlinecolor} \DeclareStringOption[lime]{riverscolor} \DeclareStringOption[1]{homeoarchymaxwords} \DeclareStringOption[3]{homeoarchymaxchars} \ProcessKeyvalOptions* \RequirePackage{xcolor} \def\usecolor#1{\csname\string\color@#1\endcsname\space} % \end{macrocode} % % \marginpar{No page finishes with an hyphenated word} % % \begin{macrocode} \ifinthyphenation \brokenpenalty=10000 % \end{macrocode} % \marginpar{Discourage hyphenation on two lines in a row} % \begin{macrocode} \doublehyphendemerits=1000000000 \fi % \end{macrocode} % % \marginpar{Number chapters} % % \begin{macrocode} \ifintfrenchchapters \let\frenchchapter\Roman \renewcommand{\thechapter}{% \ifnum\value{chapter}=1 premier% \else \frenchchapter{chapter}% \fi } \fi % \end{macrocode} % % \marginpar{No single letter} % % \begin{macrocode} \ifintnosingleletter \ifluatex \RequirePackage{luatexbase,luacode} \begin{luacode} local glyph_id = node.id "glyph" local glue_id = node.id "glue" local hlist_id = node.id "hlist" local prevent_single_letter = function (head) while head do if head.id == glyph_id then -- glyph if unicode.utf8.match(unicode.utf8.char(head.char),"%a") then -- some kind of letter if head.prev.id == glue_id and head.next.id == glue_id then -- only if we are at a one letter word local p = node.new("penalty") p.penalty = 10000 \ifintdraft local w = node.new("whatsit","pdf_literal") w.data = "q \usecolor{\intnosinglelettercolor} 0 0 m 0 5 l 2 5 l 2 0 l b Q" node.insert_after(head,head,w) node.insert_after(head,w,p) \else node.insert_after(head,head,p) \fi end end end head = head.next end return true end luatexbase.add_to_callback("pre_linebreak_filter",prevent_single_letter,"~") \end{luacode} \else \PackageError{The nosingleletter option only works with LuaTeX} \fi \fi % \end{macrocode} % % \marginpar{Paragraph indentation} % % \begin{macrocode} \ifintparindent \setlength{\parindent}{1em} \fi % \end{macrocode} % % \marginpar{Last line of paragraph} % % \begin{macrocode} \ifintlastparline \ifluatex \RequirePackage{luatexbase,luacode} \begin{luacode} local glyph_id = node.id "glyph" local glue_id = node.id "glue" local hlist_id = node.id "hlist" last_line_twice_parindent = function (head) while head do local _w,_h,_d = node.dimensions(head) if head.id == glue_id and head.subtype ~= 15 and (_w < 2 * tex.parindent) then -- we are at a glue and have less then 2*\parindent to go local p = node.new("penalty") p.penalty = 10000 \ifintdraft local w = node.new("whatsit","pdf_literal") w.data = "q \usecolor{\intlastparlinecolor} 0 0 m 0 5 l 2 5 l 2 0 l b Q" node.insert_after(head,head.prev,w) node.insert_after(head,w,p) \else node.insert_after(head,head.prev,p) \fi end head = head.next end return true end luatexbase.add_to_callback("pre_linebreak_filter",last_line_twice_parindent,"lastparline") \end{luacode} \else \setlength{\parfillskip}{0pt plus\dimexpr\textwidth-2\parindent} \fi \fi % \end{macrocode} % % \marginpar{Detect homeoarchies} % % \begin{macrocode} \ifinthomeoarchy \ifintdraft \ifluatex \RequirePackage{luatexbase,luacode} \begin{luacode} local glyph_id = node.id "glyph" local glue_id = node.id "glue" local hlist_id = node.id "hlist" compare_lines = function (line1,line2) local head1 = line1.head local head2 = line2.head local char_count = 0 local word_count = 0 while head1 and head2 do if (head1.id == glyph_id and head2.id == glyph_id and head1.char == head2.char) -- identical glyph or (head1.id == glue_id and head2.id == glue_id) then -- glue if head1.id == glyph_id then -- glyph char_count = char_count + 1 elseif char_count > 0 and head1.id == glue_id then -- glue word_count = word_count + 1 end head1 = head1.next head2 = head2.next elseif (head1.id == 0 or head2.id == 0) then -- end of line break elseif (head1.id ~= glyph_id and head1.id ~= glue_id) then -- some other kind of node head1 = head1.next elseif (head2.id ~= glyph_id and head2.id ~= glue_id) then -- some other kind of node head2 = head2.next else -- no match, no special node break end end -- analyze last non-matching node, check for punctuation if ((head1 and head1.id == glyph_id and head1.char > 49) or (head2 and head2.id == glyph_id and head2.char > 49)) then -- not a word elseif char_count > 0 then word_count = word_count + 1 end return char_count,word_count,head1,head2 end compare_lines_reverse = function (line1,line2) local head1 = node.tail(line1.head) local head2 = node.tail(line2.head) local char_count = 0 local word_count = 0 while head1 and head2 do if (head1.id == glyph_id and head2.id == glyph_id and head1.char == head2.char) -- identical glyph or (head1.id == glue_id and head2.id == glue_id) then -- glue if head1.id == glyph_id then -- glyph char_count = char_count + 1 elseif char_count > 0 and head1.id == glue_id then -- glue word_count = word_count + 1 end head1 = head1.prev head2 = head2.prev elseif (head1.id == 0 or head2.id == 0) then -- start of line break elseif (head1.id ~= glyph_id and head1.id ~= glue_id) then -- some other kind of node head1 = head1.prev elseif (head2.id ~= glyph_id and head2.id ~= glue_id) then -- some other kind of node head2 = head2.prev elseif (head1.id == glyph_id and head1.char < 48) then -- punctuation head1 = head1.prev elseif (head2.id == glyph_id and head2.char < 48) then -- punctuation head2 = head2.prev else -- no match, no special node break end end -- analyze last non-matching node, check for punctuation if ((head1 and head1.id == glyph_id and head1.char > 49) or (head2 and head2.id == glyph_id and head2.char > 49)) then -- not a word elseif char_count > 0 then word_count = word_count + 1 end return char_count,word_count,head1,head2 end highlight = function (line,nend,color) local n = node.new("whatsit","pdf_literal") -- get dimensions local w,h,d = node.dimensions(line.head,nend) local w_pts = w/65536 -- scaled points to points -- set data n.data = "q " .. color .. " 0 0 m 0 5 l " .. w_pts .. " 5 l " .. w_pts .. " 0 l b Q" -- insert node n.next = line.head line.head = n node.slide(line.head) end highlight_reverse = function (nstart,line,color) local n = node.new("whatsit","pdf_literal") -- get dimensions local w,h,d = node.dimensions(nstart,node.tail(line.head)) local w_pts = w/65536 -- scaled points to points -- set data n.data = "q " .. color .. " 0 0 m 0 5 l " .. w_pts .. " 5 l " .. w_pts .. " 0 l b Q" -- insert node node.insert_after(line.head,nstart,n) end homeoarchy = function (head) local cur_line = head local prev_line -- initiate prev_line local max_char = tonumber(\inthomeoarchymaxchars) local max_word = tonumber(\inthomeoarchymaxwords) while head do if head.id == hlist_id then -- new line prev_line = cur_line cur_line = head if prev_line.id == hlist_id then -- homeoarchy char_count,word_count,prev_head,cur_head = compare_lines(prev_line,cur_line) if char_count >= max_char or word_count >= max_word then local color if word_count >= max_word then color = "q \usecolor{\inthomeoarchywordcolor}" else color = "q \usecolor{\inthomeoarchycharcolor}" end -- highlight both lines highlight(prev_line,prev_head,color) highlight(cur_line,cur_head,color) end end end head = head.next end return true end luatexbase.add_to_callback("post_linebreak_filter",homeoarchy,"homeoarchy") homoioteleuton = function (head) local cur_line = head local prev_line -- initiate prev_line local max_char = tonumber(\inthomeoarchymaxchars) local max_word = tonumber(\inthomeoarchymaxwords) local linecounter = 0 while head do if head.id == hlist_id then -- new line linecounter = linecounter + 1 if linecounter > 1 then prev_line = cur_line cur_line = head if prev_line.id == hlist_id then -- homoioteleuton char_count,word_count,prev_head,cur_head = compare_lines_reverse(prev_line,cur_line) if char_count >= max_char or word_count >= max_word then local color if word_count >= max_word then color = "q \usecolor{\inthomeoarchywordcolor}" else color = "q \usecolor{\inthomeoarchycharcolor}" end -- highlight both lines highlight_reverse(prev_head,prev_line,color) highlight_reverse(cur_head,cur_line,color) end end end end head = head.next end return true end luatexbase.add_to_callback("post_linebreak_filter",homoioteleuton,"homoioteleuton") \end{luacode} \else \PackageError{The homeoarchy option only works with LuaTeX} \fi \fi \fi % \end{macrocode} % % \marginpar{Detect rivers} % % \begin{macrocode} \ifintrivers \ifintdraft \ifluatex \RequirePackage{luatexbase,luacode} \begin{luacode} local glyph_id = node.id "glyph" local glue_id = node.id "glue" local hlist_id = node.id "hlist" river_analyze_line = function(line,dim1,dim2,precision) local head = line.head while head do if head.id == glue_id then -- glue node local w1,h1,d1 = node.dimensions(line.glue_set,line.glue_sign,line.glue_order,line.head,head.prev) local w2,h2,d2 = node.dimensions(line.glue_set,line.glue_sign,line.glue_order,line.head,head) --print("dim1:"..dim1.."; dim2:"..dim2.."; w1:"..w1.."; w2:"..w2) if w1 > dim2 + precision then -- out of range return false,head elseif w1 < (dim2 + precision) and w2 > (dim1 - precision) then -- found return true,head end end head = head.next end return false,head end rivers = function (head) local prev_prev_line local prev_line local cur_line = head local cur_node local char_count local linecounter = 0 while head do if head.id == hlist_id then -- new line linecounter = linecounter + 1 prev_prev_line = prev_line prev_line = cur_line cur_line = head if linecounter > 2 then cur_node = cur_line.head char_count = 0 while cur_node do if cur_node.id == glyph_id then -- glyph char_count = char_count + 1 elseif cur_node.id == glue_id and char_count > 0 and cur_node.next then -- glue node -- prev_line local w1,h1,d1 = node.dimensions(head.glue_set,head.glue_sign,head.glue_order,head.head,cur_node.prev) local w2,h2,d2 = node.dimensions(head.glue_set,head.glue_sign,head.glue_order,head.head,cur_node) -- if we allow up to 45° diagonal rivers, then there can be up to + or - line height between spaces local w_p,h_p,d_p = node.dimensions(prev_line.head,cur_line.head) -- calculate line height found_p,head_p = river_analyze_line(prev_line,w1,w2,h_p) if found_p then -- prev_prev_line local w1,h1,d1 = node.dimensions(prev_line.glue_set,prev_line.glue_sign,prev_line.glue_order,prev_line.head,head_p.prev) local w2,h2,d2 = node.dimensions(prev_line.glue_set,prev_line.glue_sign,prev_line.glue_order,prev_line.head,head_p) -- if we allow up to 45° diagonal rivers, then there can be up to + or - line height between spaces local w_p,h_p,d_p = node.dimensions(prev_prev_line.head,prev_line.head) -- calculate line height found_pp,head_pp = river_analyze_line(prev_prev_line,w1,w2,h_p) if found_pp then local n_pp = node.new("whatsit","pdf_literal") n_pp.data = "q \usecolor{\intriverscolor} 0 0 m 0 5 l 5 5 l 5 0 l b Q" node.insert_after(prev_prev_line,head_pp.prev,n_pp) local n_p = node.new("whatsit","pdf_literal") n_p.data = "q \usecolor{\intriverscolor} 0 0 m 0 5 l 5 5 l 5 0 l b Q" node.insert_after(prev_line,head_p.prev,n_p) local n_c = node.new("whatsit","pdf_literal") n_c.data = "q \usecolor{\intriverscolor} 0 0 m 0 5 l 5 5 l 5 0 l b Q" node.insert_after(cur_line,cur_node.prev,n_c) end end end cur_node = cur_node.next end end end head = head.next end return true end luatexbase.add_to_callback("post_linebreak_filter",rivers,"rivers") \end{luacode} \else \PackageError{The homeoarchy option only works with LuaTeX} \fi \fi \fi % \end{macrocode} % % \iffalse % %<*fr> %% Set language to French \expandafter\newif\csname ifenglish\endcsname \englishfalse \input{impnattypo.dtx} % % \fi % % \Finale \endinput