%% %% This is file `impnattypo.sty', %% generated with the docstrip utility. %% %% The original source files were: %% %% impnattypo.dtx (with options: `package') %% %% This is a generated file. %% %% 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. %% \NeedsTeXFormat{LaTeX2e}[1999/12/01] \ProvidesPackage{impnattypo} [2019/03/04 1.5 Typographic utilities inspired by the French Imprimerie Nationale] \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} \ifinthyphenation \brokenpenalty=10000 \doublehyphendemerits=1000000000 \fi \ifintfrenchchapters \let\frenchchapter\Roman \renewcommand{\thechapter}{% \ifnum\value{chapter}=1 premier% \else \frenchchapter{chapter}% \fi } \fi \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 \ifintparindent \setlength{\parindent}{1em} \fi \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 \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 \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 \endinput %% %% End of file `impnattypo.sty'.