diff --git a/.gitattributes b/.gitattributes index 1c33c01..44ef177 100644 --- a/.gitattributes +++ b/.gitattributes @@ -12,6 +12,6 @@ docs/** linguist-documentation licences/** linguist-documentation *.lvtext linguist-language=tex -*.mkxl linguist-language=tex -linguist-documentation +*.mkxl linguist-language=tex *.opm linguist-language=tex *.tltext linguist-generated linguist-language=text diff --git a/.github/workflows/build-test-bundle.yml b/.github/workflows/build-test-bundle.yml index 0409335..940ac25 100644 --- a/.github/workflows/build-test-bundle.yml +++ b/.github/workflows/build-test-bundle.yml @@ -54,10 +54,14 @@ jobs: context l3build blindtext + beebe kantlipsum linebreaker luatexbase optex + path + pgfplots + tugboat - name: Install ConTeXt LMTX run: | @@ -82,13 +86,12 @@ jobs: - name: Build Documentation run: | cd "$GITHUB_WORKSPACE" - l3build doc "$lmtx_context" + l3build doc - name: Test the package run: | # Temporary workaround; see latex3/l3build#232 sed -i '/TEXMFCNF/,+2d' "$(kpsewhich l3build-check.lua)" - export TEXMFHOME="$GITHUB_WORKSPACE/texmf" l3build check --show-log-on-error @@ -101,7 +104,7 @@ jobs: if: "! ${{github.base_ref}}" # not a PR with: prerelease: true - artifacts: ./lua-widow-control.tds.zip, ./lua-widow-control.ctan.zip, ./texmf/doc/luatex/lua-widow-control/lua-widow-control.pdf + artifacts: ./lua-widow-control.tds.zip, ./lua-widow-control.ctan.zip, ./texmf/doc/luatex/lua-widow-control/lua-widow-control.pdf, ./texmf/doc/luatex/lua-widow-control/tb133chernoff-widows.pdf tag: release-${{github.sha}} commit: ${{github.sha}} name: Prerelease ${{github.sha}} diff --git a/.gitignore b/.gitignore index fb5e2a7..5dce72d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ # SPDX-License-Identifier: MPL-2.0+ # SPDX-FileCopyrightText: 2022 Max Chernoff *.aux +*.bbl +*.blg *.log *.out *.pdf @@ -14,3 +16,5 @@ *.zip build/ nogit-** +tugboat.bib +tugboat.def diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000..f4af41d --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,31 @@ +# lua-widow-control +# https://github.com/gucci-on-fleek/lua-widow-control +# SPDX-License-Identifier: MPL-2.0+ +# SPDX-FileCopyrightText: 2022 Max Chernoff + +cff-version: 1.2.0 +title: lua-widow-control +authors: + - family-names: Chernoff + given-names: Max +type: software +repository-code: "https://github.com/gucci-on-fleek/lua-widow-control" +license: + - MPL-2.0 + - CC-BY-SA-4.0 + +message: There is a TUGboat article available for this package. +preferred-citation: + type: article + title: Automatically removing widows and orphans with lua-widow-control + authors: + - family-names: Chernoff + given-names: Max + doi: 10.47397/tb/43-1/tb133chernoff-widows + volume: 43 + issue: 1 + year: 2022 + month: 5 + start: 28 + end: 39 + journal: TUGboat diff --git a/build.lua b/build.lua index 59316a3..2226095 100644 --- a/build.lua +++ b/build.lua @@ -8,7 +8,6 @@ module = "lua-widow-control" local orig_targets = target_list -local orig_options = option_list target_list = {} -- Tagging @@ -75,26 +74,20 @@ end target_list.doc = {} target_list.doc.desc = "Builds the documentation" --- option_list["lmtx-path"] = { --- type = "string", --- desc = "Path to the ConTeXt LMTX executable", --- } - -function target_list.doc.func(args) - options["lmtx-path"] = args and args[1] - local context = options["lmtx-path"] or "context" - - mkdir("./docs/tmp") - run("./docs", - os_setenv .. - " TEXMFHOME=" .. - abspath("./texmf") .. - os_concat .. - context .. - " lwc-documentation" - ) +function target_list.doc.func() + local context = os.getenv("lmtx_context") or "context" + local error = 0 - return 0 + mkdir("./docs/manual/tmp") + error = error + run("./docs/manual", context .. " lwc-manual") + + error = error + run("./docs/TUGboat", context .. " tb133chernoff-widows-figure.ctx") + error = error + run("./docs/TUGboat", "lualatex tb133chernoff-widows.ltx") + error = error + run("./docs/TUGboat", "bibtex tb133chernoff-widows") + error = error + run("./docs/TUGboat", "lualatex tb133chernoff-widows.ltx") + error = error + run("./docs/TUGboat", "lualatex tb133chernoff-widows.ltx") + + return error end -- Tests diff --git a/docs/TUGboat/tb133chernoff-widows-figure.ctx b/docs/TUGboat/tb133chernoff-widows-figure.ctx new file mode 100644 index 0000000..e61637e --- /dev/null +++ b/docs/TUGboat/tb133chernoff-widows-figure.ctx @@ -0,0 +1,166 @@ +% lua-widow-control +% https://github.com/gucci-on-fleek/lua-widow-control +% SPDX-License-Identifier: MPL-2.0+ OR CC-BY-SA-4.0+ +% SPDX-FileCopyrightText: 2022 Max Chernoff + +\startbuffer[demo-text] + \definepapersize[smallpaper][ + width=6cm, + height=8.3cm + ] + \setuppapersize[smallpaper] + + \def\lwc/{\sans{lua-\allowbreak widow-\allowbreak control}} + \def\Lwc/{\sans{lua-\allowbreak widow-\allowbreak control}} + + \setupbodyfont[9pt] + \setupindenting[yes, 2em] + \setupalign[tolerant] + + \definecolor[midlightgray][s=0.75] + \definepalet[layout][grid=midlightgray] + \showgrid[nonumber, none, lines] + + \definefontfeature[default][default][expansion=quality,protrusion=quality] + + \usetypescript[modern-base] + \setupbodyfont[reset,modern] + + \setupalign[hz,hanging,tolerant] + + \setuplanguage[en][spacing=packed] + + \setuplayout[ + topspace=0.1cm, + backspace=0.1cm, + width=middle, + height=\dimexpr 21\baselineskip + 1.1pt, + header=0pt, + footer=0pt, + ] + + \starttext + \Lwc/ can remove most widows and orphans from a document, \emph{without} + stretching any glue or shortening any pages. + + \ifx\uselooseness\undefined\else\uselooseness\fi It does so by automatically lengthening a paragraph on a page where a + widow or orphan would otherwise occur. While \TeX{} breaks paragraphs + into their natural length, \lwc/ is breaking the paragraph 1~line + longer than its natural length. \TeX{}'s paragraph is output to the + page, but \lwc/'s paragraph is just stored for later. When a widow or + orphan occurs, \lwc/ can take over. It selects the previously-saved + paragraph with the least badness; then, it replaces \TeX{}'s paragraph + with its saved paragraph. This increases the text block height of the + page by 1~line. + + Now, the last line of the current page can be pushed to the top of the + next page. This removes the widow or the orphan without creating any + additional work. + \stoptext + \stoptext +\stopbuffer +\savebuffer[list=demo-text] + +\startbuffer[shorten] + \parskip=0pt + \input tb133chernoff-widows-figure-demo-text.tmp +\stopbuffer + +\startbuffer[shorten-code] + \parskip=0pt + \clubpenalty=10000 + \widowpenalty=10000 +\stopbuffer + +\startbuffer[stretch] + \parskip=0pt plus 1fill + \input tb133chernoff-widows-figure-demo-text.tmp +\stopbuffer + +\startbuffer[stretch-code] + \parskip=0pt plus 1fill + \clubpenalty=10000 + \widowpenalty=10000 +\stopbuffer + +\startbuffer[ignore] + \startsetups[*default] + \clubpenalty=0 + \widowpenalty=0 + \displaywidowpenalty=0 + \interlinepenalty=0 + \brokenpenalty=0 + \stopsetups + + \setups[*default] + + \input tb133chernoff-widows-figure-demo-text.tmp +\stopbuffer + +\startbuffer[ignore-code] + \parskip=0pt + \clubpenalty=0 + \widowpenalty=0 +\stopbuffer + +\startbuffer[lwc] + \def\uselooseness{\looseness=1} + \input tb133chernoff-widows-figure-demo-text.tmp +\stopbuffer + +% We're pretending that we're using LaTeX for the demo since that's what +% 99% of users will be using. +\startbuffer[lwc-code] + \usepackage{lua-widow-control} +\stopbuffer + +\setupbodyfont[10pt] + +\setupbackend[format=PDF/A-1b:2005] % Force PDF version <1.5 + +\startTEXpage[ + align=normal, + width=39pc, + offset=0pt, +] + \veryraggedcenter + \setupTABLE[row][1, 5][style=\bfa, align=middle, offset=0pt] + \setupTABLE[row][5][toffset=2ex] + \setupTABLE[frame=off, distance=5em] + \startTABLE + \NC Ignore + \NC Shorten + \NC\NR + + \NC \typesetbuffer[ignore][frame=on, page=1, scale=925] + \NC \typesetbuffer[shorten][frame=on, page=1, scale=925] + \NC\NR + + \NC \clip[height=1cm]{\typesetbuffer[ignore] + [frame=on, page=2, scale=925]} + \NC \clip[height=1cm]{\typesetbuffer[shorten] + [frame=on, page=2, scale=925]} + \NC\NR + + \NC \typebuffer[ignore-code] + \NC \typebuffer[shorten-code] + \NC\NR + + \NC Stretch + \NC \sans{lua-widow-control} \NC\NR + + \NC \typesetbuffer[stretch][frame=on, page=1, scale=925] + \NC \typesetbuffer[lwc][frame=on, page=1, scale=925] + \NC\NR + + \NC \clip[height=1cm]{\typesetbuffer[stretch] + [frame=on, page=2, scale=925]} + \NC \clip[height=1cm]{\typesetbuffer[lwc] + [frame=on, page=2, scale=925]} + \NC\NR + + \NC \typebuffer[stretch-code] + \NC \typebuffer[lwc-code] + \NC\NR + \stopTABLE +\stopTEXpage diff --git a/docs/TUGboat/tb133chernoff-widows-plot.dat b/docs/TUGboat/tb133chernoff-widows-plot.dat new file mode 100644 index 0000000..e6705bd --- /dev/null +++ b/docs/TUGboat/tb133chernoff-widows-plot.dat @@ -0,0 +1,1575 @@ +% lua-widow-control +% https://github.com/gucci-on-fleek/lua-widow-control +% SPDX-License-Identifier: MPL-2.0+ OR CC-BY-SA-4.0+ +% SPDX-FileCopyrightText: 2022 Max Chernoff + +Percentile Natural Long +0.063734863 nan nan +0.127469726 nan nan +0.191204589 nan nan +0.254939452 nan nan +0.318674315 nan nan +0.382409178 nan nan +0.446144041 100 nan +0.509878904 100 nan +0.573613767 100 nan +0.63734863 100 nan +0.701083493 100 nan +0.764818356 100 nan +0.828553219 100 nan +0.892288082 100 nan +0.956022945 100 nan +1.019757808 100 nan +1.08349267 100 nan +1.147227533 100 nan +1.210962396 100 nan +1.274697259 100 nan +1.338432122 100 nan +1.402166985 100 nan +1.465901848 100 nan +1.529636711 100 nan +1.593371574 100 nan +1.657106437 100 nan +1.7208413 100 nan +1.784576163 100 nan +1.848311026 100 nan +1.912045889 100 nan +1.975780752 100 nan +2.039515615 100 nan +2.103250478 100 nan +2.166985341 100 nan +2.230720204 100 nan +2.294455067 100 nan +2.35818993 100 nan +2.421924793 100 nan +2.485659656 100 nan +2.549394519 100 nan +2.613129382 100 nan +2.676864245 100 nan +2.740599108 100 nan +2.804333971 100 nan +2.868068834 100 nan +2.931803697 100 nan +2.99553856 100 nan +3.059273423 100 nan +3.123008286 100 nan +3.186743149 100 nan +3.250478011 100 nan +3.314212874 100 nan +3.377947737 100 nan +3.4416826 100 nan +3.505417463 100 nan +3.569152326 100 nan +3.632887189 100 nan +3.696622052 100 nan +3.760356915 100 nan +3.824091778 100 nan +3.887826641 100 nan +3.951561504 100 nan +4.015296367 100 nan +4.07903123 100 nan +4.142766093 100 nan +4.206500956 100 nan +4.270235819 100 nan +4.333970682 100 nan +4.397705545 100 nan +4.461440408 100 nan +4.525175271 100 nan +4.588910134 100 nan +4.652644997 100 nan +4.71637986 100 nan +4.780114723 100 nan +4.843849586 100 nan +4.907584449 100 nan +4.971319312 100 nan +5.035054175 100 nan +5.098789038 100 nan +5.162523901 100 nan +5.226258764 100 nan +5.289993627 100 nan +5.353728489 100 nan +5.417463352 100 nan +5.481198215 100 nan +5.544933078 100 nan +5.608667941 100 nan +5.672402804 100 nan +5.736137667 100 nan +5.79987253 100 nan +5.863607393 100 nan +5.927342256 100 nan +5.991077119 100 nan +6.054811982 100 nan +6.118546845 100 nan +6.182281708 100 nan +6.246016571 100 nan +6.309751434 100 nan +6.373486297 100 nan +6.43722116 100 nan +6.500956023 100 nan +6.564690886 100 nan +6.628425749 100 nan +6.692160612 100 nan +6.755895475 100 nan +6.819630338 100 nan +6.883365201 100 nan +6.947100064 100 nan +7.010834927 100 nan +7.07456979 100 nan +7.138304653 100 nan +7.202039516 100 nan +7.265774379 100 nan +7.329509242 100 nan +7.393244105 100 nan +7.456978967 100 nan +7.52071383 100 nan +7.584448693 100 nan +7.648183556 100 nan +7.711918419 100 nan +7.775653282 100 nan +7.839388145 100 nan +7.903123008 100 nan +7.966857871 100 nan +8.030592734 100 nan +8.094327597 100 nan +8.15806246 100 nan +8.221797323 100 nan +8.285532186 100 nan +8.349267049 100 nan +8.413001912 100 nan +8.476736775 100 nan +8.540471638 100 nan +8.604206501 100 nan +8.667941364 100 nan +8.731676227 100 nan +8.79541109 100 nan +8.859145953 100 nan +8.922880816 100 nan +8.986615679 100 nan +9.050350542 100 nan +9.114085405 100 nan +9.177820268 100 nan +9.241555131 100 nan +9.305289994 100 nan +9.369024857 100 nan +9.43275972 100 nan +9.496494583 100 nan +9.560229446 100 nan +9.623964308 100 nan +9.687699171 100 nan +9.751434034 100 nan +9.815168897 100 nan +9.87890376 100 nan +9.942638623 100 nan +10.00637349 100 nan +10.07010835 100 nan +10.13384321 100 nan +10.19757808 100 nan +10.26131294 100 nan +10.3250478 100 nan +10.38878266 100 nan +10.45251753 100 nan +10.51625239 100 nan +10.57998725 100 nan +10.64372212 100 nan +10.70745698 100 nan +10.77119184 100 nan +10.8349267 100 nan +10.89866157 100 nan +10.96239643 100 nan +11.02613129 100 nan +11.08986616 100 nan +11.15360102 100 nan +11.21733588 100 nan +11.28107075 100 nan +11.34480561 100 nan +11.40854047 100 nan +11.47227533 100 nan +11.5360102 100 nan +11.59974506 100 nan +11.66347992 100 nan +11.72721479 100 nan +11.79094965 100 nan +11.85468451 100 nan +11.91841938 100 nan +11.98215424 100 nan +12.0458891 100 nan +12.10962396 100 nan +12.17335883 100 nan +12.23709369 100 nan +12.30082855 100 nan +12.36456342 100 nan +12.42829828 100 nan +12.49203314 100 nan +12.55576801 100 nan +12.61950287 100 nan +12.68323773 100 nan +12.74697259 100 nan +12.81070746 100 nan +12.87444232 100 nan +12.93817718 100 nan +13.00191205 100 nan +13.06564691 100 nan +13.12938177 100 nan +13.19311663 100 nan +13.2568515 100 nan +13.32058636 100 nan +13.38432122 100 nan +13.44805609 100 nan +13.51179095 100 nan +13.57552581 100 nan +13.63926068 100 nan +13.70299554 100 nan +13.7667304 100 nan +13.83046526 100 nan +13.89420013 100 nan +13.95793499 100 nan +14.02166985 100 nan +14.08540472 100 nan +14.14913958 100 nan +14.21287444 100 nan +14.27660931 100 nan +14.34034417 100 nan +14.40407903 100 nan +14.46781389 100 nan +14.53154876 100 nan +14.59528362 100 nan +14.65901848 100 nan +14.72275335 100 nan +14.78648821 100 nan +14.85022307 100 nan +14.91395793 100 nan +14.9776928 100 nan +15.04142766 100 nan +15.10516252 100 nan +15.16889739 100 nan +15.23263225 100 nan +15.29636711 100 nan +15.36010198 100 nan +15.42383684 100 nan +15.4875717 100 nan +15.55130656 100 nan +15.61504143 100 nan +15.67877629 100 nan +15.74251115 100 nan +15.80624602 100 nan +15.86998088 100 nan +15.93371574 100 nan +15.99745061 100 nan +16.06118547 100 nan +16.12492033 100 nan +16.18865519 100 nan +16.25239006 100 nan +16.31612492 100 nan +16.37985978 100 nan +16.44359465 100 nan +16.50732951 100 nan +16.57106437 100 nan +16.63479924 100 nan +16.6985341 100 nan +16.76226896 100 nan +16.82600382 100 nan +16.88973869 100 nan +16.95347355 100 nan +17.01720841 100 nan +17.08094328 100 nan +17.14467814 100 nan +17.208413 100 nan +17.27214786 100 nan +17.33588273 100 nan +17.39961759 100 nan +17.46335245 100 nan +17.52708732 100 nan +17.59082218 100 nan +17.65455704 100 nan +17.71829191 100 nan +17.78202677 100 nan +17.84576163 100 nan +17.90949649 100 nan +17.97323136 100 nan +18.03696622 100 nan +18.10070108 100 nan +18.16443595 100 nan +18.22817081 100 nan +18.29190567 100 nan +18.35564054 100 nan +18.4193754 100 nan +18.48311026 100 nan +18.54684512 100 nan +18.61057999 100 nan +18.67431485 100 nan +18.73804971 100 nan +18.80178458 100 nan +18.86551944 100 nan +18.9292543 100 nan +18.99298917 100 nan +19.05672403 100 nan +19.12045889 100 nan +19.18419375 100 nan +19.24792862 100 nan +19.31166348 100 nan +19.37539834 100 nan +19.43913321 100 nan +19.50286807 100 nan +19.56660293 100 nan +19.63033779 100 nan +19.69407266 100 nan +19.75780752 100 nan +19.82154238 100 nan +19.88527725 100 nan +19.94901211 100 nan +20.01274697 100 nan +20.07648184 100 nan +20.1402167 100 nan +20.20395156 100 nan +20.26768642 100 nan +20.33142129 100 nan +20.39515615 100 nan +20.45889101 100 nan +20.52262588 100 nan +20.58636074 100 nan +20.6500956 100 nan +20.71383047 100 nan +20.77756533 100 nan +20.84130019 100 nan +20.90503505 100 nan +20.96876992 100 nan +21.03250478 100 nan +21.09623964 100 nan +21.15997451 100 nan +21.22370937 100 nan +21.28744423 100 nan +21.35117909 100 nan +21.41491396 100 nan +21.47864882 100 nan +21.54238368 100 nan +21.60611855 100 nan +21.66985341 100 nan +21.73358827 100 nan +21.79732314 100 nan +21.861058 100 nan +21.92479286 100 nan +21.98852772 100 nan +22.05226259 100 nan +22.11599745 100 nan +22.17973231 100 nan +22.24346718 100 nan +22.30720204 100 nan +22.3709369 100 nan +22.43467177 100 nan +22.49840663 100 nan +22.56214149 100 nan +22.62587635 100 nan +22.68961122 100 nan +22.75334608 100 nan +22.81708094 100 nan +22.88081581 100 nan +22.94455067 100 nan +23.00828553 100 nan +23.0720204 100 nan +23.13575526 100 nan +23.19949012 100 nan +23.26322498 100 nan +23.32695985 100 nan +23.39069471 100 nan +23.45442957 100 nan +23.51816444 100 nan +23.5818993 100 nan +23.64563416 100 nan +23.70936902 100 nan +23.77310389 100 nan +23.83683875 100 nan +23.90057361 100 nan +23.96430848 100 nan +24.02804334 100 nan +24.0917782 100 nan +24.15551307 100 nan +24.21924793 100 nan +24.28298279 100 nan +24.34671765 100 nan +24.41045252 100 nan +24.47418738 100 nan +24.53792224 100 nan +24.60165711 100 nan +24.66539197 100 nan +24.72912683 100 nan +24.7928617 100 nan +24.85659656 100 nan +24.92033142 100 nan +24.98406628 100 nan +25.04780115 100 nan +25.11153601 100 nan +25.17527087 100 nan +25.23900574 100 nan +25.3027406 100 nan +25.36647546 100 nan +25.43021033 100 nan +25.49394519 100 nan +25.55768005 100 nan +25.62141491 100 nan +25.68514978 100 nan +25.74888464 100 nan +25.8126195 100 nan +25.87635437 100 nan +25.94008923 100 nan +26.00382409 100 nan +26.06755895 100 nan +26.13129382 100 nan +26.19502868 100 nan +26.25876354 100 nan +26.32249841 100 nan +26.38623327 100 nan +26.44996813 100 nan +26.513703 100 nan +26.57743786 100 nan +26.64117272 100 nan +26.70490758 100 nan +26.76864245 100 nan +26.83237731 100 nan +26.89611217 100 nan +26.95984704 100 nan +27.0235819 100 nan +27.08731676 100 nan +27.15105163 100 nan +27.21478649 100 nan +27.27852135 100 nan +27.34225621 100 nan +27.40599108 100 nan +27.46972594 100 nan +27.5334608 100 nan +27.59719567 100 nan +27.66093053 100 nan +27.72466539 100 nan +27.78840025 100 nan +27.85213512 100 nan +27.91586998 100 nan +27.97960484 121 nan +28.04333971 141.4213562 nan +28.10707457 141.4213562 nan +28.17080943 141.4213562 nan +28.2345443 141.4213562 nan +28.29827916 141.4213562 nan +28.36201402 141.4213562 nan +28.42574888 141.4213562 nan +28.48948375 141.4213562 nan +28.55321861 141.4213562 nan +28.61695347 141.4213562 nan +28.68068834 141.4213562 nan +28.7444232 141.4213562 nan +28.80815806 141.4213562 nan +28.87189293 141.4213562 nan +28.93562779 141.4213562 140.8734657 +28.99936265 141.4213562 141.4213562 +29.06309751 141.4213562 141.4213562 +29.12683238 141.4213562 141.4213562 +29.19056724 141.4213562 141.4213562 +29.2543021 141.4213562 155.3072224 +29.31803697 141.4213562 170.8956797 +29.38177183 141.4213562 172.5340546 +29.44550669 141.4213562 172.5340546 +29.50924156 141.4213562 172.5340546 +29.57297642 141.4213562 187.383297 +29.63671128 141.4213562 187.383297 +29.70044614 141.4213562 190.2117241 +29.76418101 141.4213562 190.2117241 +29.82791587 141.4213562 190.2117241 +29.89165073 141.4213562 190.2117241 +29.9553856 141.4213562 190.2117241 +30.01912046 141.4213562 205.0609665 +30.08285532 141.4213562 205.0609665 +30.14659018 141.4213562 209.3036072 +30.21032505 141.4213562 209.3036072 +30.27405991 141.4213562 209.3036072 +30.33779477 141.4213562 209.3036072 +30.40152964 141.4213562 213.0422493 +30.4652645 141.4213562 221.3244225 +30.52899936 141.4213562 224.1528496 +30.59273423 141.4213562 229.8097039 +30.65646909 141.4213562 229.8097039 +30.72020395 141.4213562 229.8097039 +30.78393881 141.4213562 229.8097039 +30.84767368 141.4213562 233.5 +30.91140854 141.4213562 240.4163056 +30.9751434 141.4213562 244.6589463 +31.03887827 141.4213562 244.6589463 +31.10261313 141.4213562 251.7300141 +31.16634799 141.4213562 251.7300141 +31.23008286 141.4213562 251.7300141 +31.29381772 141.4213562 251.7300141 +31.35755258 141.4213562 260.9224023 +31.42128744 141.4213562 266.1584741 +31.48502231 141.4213562 266.5792565 +31.54875717 141.4213562 275.0645379 +31.61249203 141.4213562 275.0645379 +31.6762269 141.4213562 275.0645379 +31.73996176 141.4213562 278.6000718 +31.80369662 141.4213562 280.5922308 +31.86743149 141.4213562 282.8427125 +31.93116635 141.4213562 288.6751346 +31.99490121 141.4213562 289.9137803 +32.05863607 141.4213562 297.3353886 +32.12237094 141.4213562 299.8132752 +32.1861058 141.4213562 299.8132752 +32.24984066 141.4213562 300.520382 +32.31357553 141.4213562 306.1772363 +32.37731039 141.4213562 314.6558967 +32.44104525 141.4213562 314.6625176 +32.50478011 141.4213562 319.6122651 +32.56851498 141.4213562 325.9762261 +32.63224984 141.4213562 325.9762261 +32.6959847 141.4213562 325.9762261 +32.75971957 141.4213562 330.9259736 +32.82345443 141.4213562 340.0593086 +32.88718929 141.4213562 340.1183618 +32.95092416 141.4213562 346.4101615 +33.01465902 141.4213562 349.2969129 +33.07839388 141.4213562 353.5533906 +33.14212874 141.4213562 357.0889245 +33.20586361 141.4213562 363.4528855 +33.26959847 141.4213562 368.402633 +33.33333333 141.4213562 372.3909236 +33.3970682 141.4213562 379.3191269 +33.46080306 141.4213562 382.5447686 +33.52453792 141.4213562 382.5447686 +33.58827279 141.4213562 384.666089 +33.65200765 141.4213562 391.5 +33.71574251 141.4213562 396.0622847 +33.77947737 141.4213562 404.7225387 +33.84321224 141.4213562 409.9186911 +33.9069471 141.4213562 412.9503602 +33.97068196 141.4213562 412.9503602 +34.03441683 141.4213562 419.1562954 +34.09815169 141.4213562 433.0127019 +34.16188655 141.4213562 437.631504 +34.22562141 141.4213562 444.7701654 +34.28935628 141.4213562 444.7701654 +34.35309114 141.4213562 448.5 +34.416826 141.4213562 459.6194078 +34.48056087 141.4213562 461.7407281 +34.54429573 141.4213562 467.653718 +34.60803059 141.4213562 473.4272207 +34.67176546 141.4213562 476.3139721 +34.73550032 141.4213562 478.0041841 +34.79923518 141.4213562 480.8326112 +34.86297004 141.4213562 484.9742261 +34.92670491 141.4213562 492.8534265 +34.99043977 141.4213562 497.0985818 +35.05417463 141.4213562 508.6455872 +35.1179095 141.4213562 512.6524164 +35.18164436 141.4213562 516.1511407 +35.24537922 141.4213562 520.5 +35.30911409 141.4213562 527.5016588 +35.37284895 141.4213562 536.3584001 +35.43658381 141.4213562 543.2866033 +35.50031867 141.4213562 545.8864351 +35.56405354 141.4213562 548.7148622 +35.6277884 141.4213562 550.7921568 +35.69152326 141.4213562 557.72036 +35.75525813 141.4213562 563.5641046 +35.81899299 141.4213562 568.1126649 +35.88272785 141.4213562 577.3502692 +35.94646272 141.4213562 582.5 +36.01019758 141.4213562 586.1915216 +36.07393244 141.4213562 586.5878735 +36.1376673 141.4213562 594.093427 +36.20140217 141.4213562 600.5 +36.26513703 141.4213562 601.040764 +36.32887189 141.4213562 605.6404324 +36.39260676 141.4213562 612.5686356 +36.45634162 141.4213562 617.30422 +36.52007648 141.4213562 625.0823946 +36.58381134 141.4213562 625.2703415 +36.64754621 141.4213562 634.9818895 +36.71128107 141.4213562 639.931637 +36.77501593 141.4213562 647.2096518 +36.8387508 141.4213562 656.1950929 +36.90248566 141.4213562 661.0660582 +36.96622052 141.4213562 665.3874811 +37.02995539 141.4213562 669 +37.09369025 141.4213562 673.8727625 +37.15742511 141.4213562 680.2367235 +37.22115997 141.4213562 692.9646456 +37.28489484 141.4213562 698.0164755 +37.3486297 141.4213562 707.1067812 +37.41236456 141.4213562 713.0275824 +37.47609943 141.4213562 716.4916841 +37.53983429 141.4213562 721.6878365 +37.60356915 141.4213562 727.4613392 +37.66730402 141.4213562 728.6160397 +37.73103888 141.4213562 733.5 +37.79477374 141.4213562 742.4724462 +37.8585086 141.4213562 746.5138981 +37.92224347 141.4213562 750.5 +37.98597833 141.4213562 756.3288526 +38.04971319 141.4213562 764.9891067 +38.11344806 141.4213562 769.6079088 +38.17718292 141.4213562 774.9890322 +38.24091778 141.4213562 775.4683746 +38.30465264 141.4213562 781.3529932 +38.36838751 141.4213562 787.9903553 +38.43212237 141.4213562 794.7880221 +38.49585723 141.4213562 795 +38.5595921 141.4213562 799.6301228 +38.62332696 141.4213562 808.2903769 +38.68706182 141.4213562 809.6372645 +38.75079669 141.4213562 816.3732806 +38.81453155 141.4213562 822.7241336 +38.87826641 141.4213562 825.9007204 +38.94200127 141.4213562 836.2894236 +39.00573614 141.4213562 840.7499628 +39.069471 141.4213562 843.57839 +39.13320586 141.4213562 849 +39.19694073 141.4213562 853.5 +39.26067559 141.4213562 856.7877995 +39.32441045 141.4213562 864.9110937 +39.38814532 141.4213562 871.8626612 +39.45188018 141.4213562 877 +39.51561504 141.4213562 888.1261172 +39.5793499 141.4213562 893.1608664 +39.64308477 141.4213562 903.5 +39.70681963 141.4213562 906.5108935 +39.77055449 141.4213562 917.986928 +39.83428936 141.4213562 922 +39.89802422 141.4213562 930.1112837 +39.96175908 141.4213562 936.9164851 +40.02549395 141.4213562 936.9164851 +40.08922881 141.4213562 945.6997409 +40.15296367 141.4213562 952.0505939 +40.21669853 141.4213562 961.0620167 +40.2804334 141.4213562 972.8352036 +40.34416826 141.4213562 976.267279 +40.40790312 141.4213562 987.1210665 +40.47163799 141.4213562 998.434775 +40.53537285 141.4213562 1001.970309 +40.59910771 141.4213562 1008.05357 +40.66284257 141.4213562 1019.023225 +40.72657744 141.4213562 1025 +40.7903123 141.4213562 1035.911434 +40.85404716 141.4213562 1039.230485 +40.91778203 141.4213562 1044.426637 +40.98151689 141.4213562 1053.589104 +41.04525175 141.4213562 1058.283043 +41.10898662 141.4213562 1062.5 +41.17272148 141.4213562 1069.85256 +41.23645634 141.4213562 1079.067653 +41.3001912 141.4213562 1084.5 +41.36392607 141.4213562 1091.77287 +41.42766093 141.4213562 1097.542862 +41.49139579 141.4213562 1103.5 +41.55513066 141.4213562 1112.728594 +41.61886552 141.4213562 1124.294979 +41.68260038 141.4213562 1127.244654 +41.74633525 141.4213562 1133.239251 +41.81007011 141.4213562 1141.263435 +41.87380497 141.4213562 1147.5 +41.93753983 141.4213562 1156.941572 +42.0012747 141.4213562 1161.069335 +42.06500956 141.4213562 1172.383043 +42.12874442 141.4213562 1177.332791 +42.19247929 141.4213562 1189.341555 +42.25621415 141.4213562 1199.733859 +42.31994901 141.4213562 1202.081528 +42.38368388 141.4213562 1202.081528 +42.44741874 141.4213562 1209 +42.5111536 141.4213562 1217.050468 +42.57488846 141.4213562 1225.416052 +42.63862333 141.4213562 1233.5 +42.70235819 141.4213562 1245.344531 +42.76609305 141.4213562 1252.272734 +42.82982792 141.4213562 1257.5 +42.89356278 141.4213562 1259.357177 +42.95729764 141.4213562 1262.665039 +43.0210325 141.4213562 1274.20642 +43.08476737 141.4213562 1279.985547 +43.14850223 141.4213562 1290.469876 +43.21223709 141.4213562 1296.025 +43.27597196 141.4213562 1308.147545 +43.33970682 141.4213562 1318.04704 +43.40344168 141.4213562 1323.703894 +43.46717655 141.4213562 1332.896283 +43.53091141 141.4213562 1343 +43.59464627 141.4213562 1354.5 +43.65838113 141.4213562 1363.301874 +43.722116 141.4213562 1373 +43.78585086 141.4213562 1378.151117 +43.84958572 141.4213562 1387.343505 +43.91332059 141.4213562 1399.47514 +43.97705545 141.4213562 1409.312007 +44.04079031 141.4213562 1423 +44.10452518 141.4213562 1438.686137 +44.16826004 141.4213562 1442.798323 +44.2319949 141.4213562 1446.033368 +44.29572976 141.4213562 1454.518649 +44.35946463 141.4213562 1473 +44.42319949 141.4213562 1481.618642 +44.48693435 141.4213562 1500.128436 +44.55066922 141.4213562 1508.616253 +44.61440408 141.4213562 1517.853858 +44.67813894 141.4213562 1540.947868 +44.7418738 141.4213562 1548.453422 +44.80560867 141.4213562 1555.6703 +44.86934353 141.4213562 1566.948627 +44.93307839 141.4213562 1572 +44.99681326 141.4213562 1581.79787 +45.06054812 141.4213562 1596.4 +45.12428298 141.4213562 1600.360317 +45.18801785 141.4213562 1612.539302 +45.25175271 156.2705986 1618.567422 +45.31548757 156.2705986 1627.5 +45.37922243 156.2705986 1632.709558 +45.4429573 156.2705986 1639.5 +45.50669216 156.2705986 1646.025617 +45.57042702 156.2705986 1654.5 +45.63416189 156.2705986 1668.402565 +45.69789675 156.2705986 1683.553385 +45.76163161 156.2705986 1690.760295 +45.82536648 156.2705986 1699.884702 +45.88910134 156.2705986 1701 +45.9528362 156.2705986 1707.799336 +46.01657106 156.2705986 1714.733944 +46.08030593 156.2705986 1730.006613 +46.14404079 156.2705986 1743.941617 +46.20777565 156.2705986 1751.503497 +46.27151052 156.2705986 1768.47406 +46.33524538 156.2705986 1789.318306 +46.39898024 156.2705986 1801.708078 +46.46271511 156.2705986 1815.5 +46.52644997 156.2705986 1831.355054 +46.59018483 156.2705986 1839.914186 +46.65391969 156.2705986 1850.479855 +46.71765456 169 1868.176116 +46.78138942 172.5340546 1879.467576 +46.84512428 172.5340546 1899.482386 +46.90885915 172.5340546 1911 +46.97259401 172.5340546 1924.744658 +47.03632887 172.5340546 1934.123402 +47.10006373 172.5340546 1943 +47.1637986 172.5340546 1969.292386 +47.22753346 172.5340546 1982.727414 +47.29126832 172.5340546 1995.5 +47.35500319 172.5340546 2006.597404 +47.41873805 172.5340546 2020.204074 +47.48247291 173.2050808 2032.789485 +47.54620778 173.2050808 2050.5 +47.60994264 173.2050808 2056.973626 +47.6736775 173.2050808 2071.115762 +47.73741236 173.2050808 2087.966061 +47.80114723 173.2050808 2098.692927 +47.86488209 173.2050808 2111.420849 +47.92861695 173.2050808 2120.680042 +47.99235182 173.2050808 2132.731894 +48.05608668 173.2050808 2151.584955 +48.11982154 173.2050808 2174.801328 +48.18355641 173.2050808 2187.163294 +48.24729127 173.2050808 2209.708691 +48.31102613 173.2050808 2227.222493 +48.37476099 173.2050808 2258.499059 +48.43849586 173.2050808 2269.105661 +48.50223072 173.2050808 2286.609368 +48.56596558 173.2050808 2298.44668 +48.62970045 173.2050808 2314.597229 +48.69343531 173.2050808 2330.381182 +48.75717017 173.2050808 2346.333333 +48.82090504 173.2050808 2359.629096 +48.8846399 173.2050808 2379.068009 +48.94837476 173.2050808 2388.498064 +49.01210962 173.2050808 2398.506202 +49.07584449 173.2050808 2414.059089 +49.13957935 173.2050808 2421.840726 +49.20331421 173.2050808 2455.470695 +49.26704908 173.2050808 2467.0177 +49.33078394 173.2050808 2492.421112 +49.3945188 173.2050808 2508.58692 +49.45825366 173.2050808 2532.149383 +49.52198853 173.2050808 2541.495885 +49.58572339 173.2050808 2558.665888 +49.64945825 173.2050808 2566.899297 +49.71319312 173.2050808 2586.410889 +49.77692798 173.2050808 2600.031634 +49.84066284 173.2050808 2616.29509 +49.90439771 173.2050808 2631.144333 +49.96813257 173.2050808 2647.407789 +50.03186743 173.2050808 2665.085458 +50.09560229 173.2050808 2687.619783 +50.15933716 173.2050808 2704.683438 +50.22307202 173.2050808 2728.557372 +50.28680688 173.2050808 2741.666667 +50.35054175 173.2050808 2756.302233 +50.41427661 173.2050808 2775.900094 +50.47801147 173.2050808 2790.243359 +50.54174634 173.2050808 2812.163669 +50.6054812 173.2050808 2828.427125 +50.66921606 173.2050808 2838.253923 +50.73295092 173.2050808 2855.5 +50.79668579 173.2050808 2877.217493 +50.86042065 173.2050808 2886.409881 +50.92415551 173.2050808 2895.44235 +50.98789038 173.2050808 2919.6439 +51.05162524 173.2050808 2938.571195 +51.1153601 173.2050808 2945.099744 +51.17909496 173.2050808 2956.610729 +51.24282983 173.2050808 2973.384015 +51.30656469 173.2050808 2999.740179 +51.37029955 173.2050808 3021.467276 +51.43403442 173.2050808 3048.333333 +51.49776928 173.2050808 3059.114969 +51.56150414 173.2050808 3089.349527 +51.62523901 173.2050808 3117.691454 +51.68897387 173.2050808 3140.261215 +51.75270873 173.2050808 3168.545486 +51.81644359 173.2050808 3189.642188 +51.88017846 173.2050808 3218.750068 +51.94391332 173.2050808 3242.791699 +52.00764818 173.2050808 3269.661756 +52.07138305 173.2050808 3312.79527 +52.13511791 173.2050808 3334.305565 +52.19885277 173.2050808 3340.372434 +52.26258764 173.2050808 3352.94321 +52.3263225 173.2050808 3355.221677 +52.39005736 173.2050808 3366.675674 +52.45379222 173.2050808 3387.195772 +52.51752709 173.2050808 3401.183618 +52.58126195 173.2050808 3413.362506 +52.64499681 173.2050808 3436.677477 +52.70873168 173.2050808 3457.045053 +52.77246654 173.2050808 3468.358762 +52.8362014 173.2050808 3486.036431 +52.89993627 173.2050808 3506.248185 +52.96367113 173.2050808 3508.989028 +53.02740599 173.2050808 3525.634411 +53.09114085 173.2050808 3541.177876 +53.15487572 173.2050808 3560.282643 +53.21861058 173.2050808 3576.677808 +53.28234544 173.2050808 3595.442694 +53.34608031 173.2050808 3617.676787 +53.40981517 173.2050808 3626.043574 +53.47355003 173.2050808 3652.206525 +53.53728489 173.2050808 3677.721215 +53.60101976 173.2050808 3713.724815 +53.66475462 173.2050808 3734.230911 +53.72848948 173.2050808 3752.969241 +53.79222435 173.2050808 3773.828891 +53.85595921 173.2050808 3804.630531 +53.91969407 173.2050808 3825.447686 +53.98342894 173.2050808 3845.788513 +54.0471638 173.2050808 3867.958148 +54.11089866 173.2050808 3892.331277 +54.17463352 173.2050808 3906.764966 +54.23836839 173.2050808 3927.271063 +54.30210325 185.3294364 3947.460956 +54.36583811 185.3294364 3975.113604 +54.42957298 185.3294364 4002.178607 +54.49330784 185.3294364 4022.730478 +54.5570427 185.3294364 4042 +54.62077757 185.3294364 4057.838952 +54.68451243 185.3294364 4087.876331 +54.74824729 185.3294364 4101.219331 +54.81198215 185.3294364 4125.191208 +54.87571702 185.3294364 4145 +54.93945188 185.3294364 4164.505388 +55.00318674 185.3294364 4180.061737 +55.06692161 185.3294364 4190.870077 +55.13065647 185.3294364 4194.665192 +55.19439133 185.3294364 4219.604393 +55.2581262 185.3294364 4232.741192 +55.32186106 185.3294364 4260.852919 +55.38559592 185.3294364 4279.234664 +55.44933078 185.3294364 4311.937152 +55.51306565 185.3294364 4327.847054 +55.57680051 190.2117241 4339.696985 +55.64053537 190.2117241 4362.84884 +55.70427024 190.2117241 4387.894598 +55.7680051 190.2117241 4411.939275 +55.83173996 190.2117241 4432.852411 +55.89547482 190.2117241 4461.136683 +55.95920969 190.2117241 4474.819237 +56.02294455 196 4476.233254 +56.08667941 197.4537921 4497.906235 +56.15041428 198.6084926 4520.861101 +56.21414914 198.6084926 4532.54996 +56.277884 198.6084926 4546.34305 +56.34161887 198.6084926 4571.564357 +56.40535373 198.6084926 4592.385019 +56.46908859 198.6084926 4596.194078 +56.53282345 198.6084926 4599.729612 +56.59655832 198.6084926 4622 +56.66029318 198.6084926 4633.670737 +56.72402804 200 4647.380992 +56.78776291 200 4667.838054 +56.85149777 200 4669.497093 +56.91523263 200 4698.246778 +56.9789675 200 4712.083085 +57.04270236 200 4745.630085 +57.10643722 200 4776.688414 +57.17017208 200 4806.39695 +57.23390695 200 4834.842616 +57.29764181 200 4850.666667 +57.36137667 200 4868.360864 +57.42511154 200 4882.5 +57.4888464 200 4912.026291 +57.55258126 200 4931.009139 +57.61631612 200 4950.213255 +57.68005099 200 4981.567273 +57.74378585 200 5013.387079 +57.80752071 200 5033.293173 +57.87125558 200 5053.546906 +57.93499044 200 5072.784048 +57.9987253 200 5104 +58.06246017 200 5119.453096 +58.12619503 200 5143.938071 +58.18992989 200 5169.412003 +58.25366475 200 5183.092706 +58.31739962 200 5199.039174 +58.38113448 200 5218.666667 +58.44486934 200 5232.590181 +58.50860421 200 5245.390956 +58.57233907 200 5264.409986 +58.63607393 200 5277.525187 +58.6998088 200 5308.957713 +58.76354366 200 5330.720572 +58.82727852 200 5367.666667 +58.89101338 200 5394.549403 +58.95474825 200 5429.620263 +59.01848311 200 5463.814098 +59.08221797 200 5483.70569 +59.14595284 200 5505.597152 +59.2096877 200 5538.293167 +59.27342256 200 5561.394834 +59.33715743 200 5585.697808 +59.40089229 200 5601.699921 +59.46462715 200 5625.499818 +59.52836201 209.3036072 5656.854249 +59.59209688 209.3036072 5684.20771 +59.65583174 209.3036072 5694 +59.7195666 209.3036072 5715 +59.78330147 209.3036072 5733.928889 +59.84703633 209.3036072 5754.131137 +59.91077119 210.5 5769.63569 +59.97450605 210.5 5784.13347 +60.03824092 210.5 5805.279683 +60.10197578 210.5 5830.095411 +60.16571064 210.5 5847.340264 +60.22944551 210.5 5866.506231 +60.29318037 210.5 5881.305994 +60.35691523 210.5 5905.5 +60.4206501 210.5 5919.333333 +60.48438496 210.5 5936.74664 +60.54811982 210.5 5951.722784 +60.61185468 210.5 5967.77351 +60.67558955 210.5 5988.48733 +60.73932441 210.5 6000.025125 +60.80305927 210.5 6011.80606 +60.86679414 210.5 6043.5 +60.930529 210.7328483 6071.5 +60.99426386 213.0422493 6101.480488 +61.05799873 213.0422493 6122.354122 +61.12173359 213.0422493 6145.361516 +61.18546845 213.0422493 6169.506666 +61.24920331 213.0422493 6180.820374 +61.31293818 213.0422493 6199.722074 +61.37667304 221 6217.465437 +61.4404079 221 6244 +61.50414277 222 6275.540017 +61.56787763 222 6309.885577 +61.63161249 222 6333.555439 +61.69534736 222 6350 +61.75908222 222 6366.430044 +61.82281708 222 6389.416875 +61.88655194 222 6411.864455 +61.95028681 222 6452.349378 +62.01402167 223.6067977 6468.050232 +62.07775653 223.6067977 6482.090712 +62.1414914 223.6067977 6502.333333 +62.20522626 223.6067977 6523.060056 +62.26896112 223.6067977 6538.026209 +62.33269598 223.6067977 6555 +62.39643085 223.6067977 6561 +62.46016571 223.6067977 6587.406774 +62.52390057 223.6067977 6598.720482 +62.58763544 223.6067977 6618.519472 +62.6513703 223.6067977 6642.347649 +62.71510516 223.6067977 6659.885219 +62.77884003 223.6067977 6687.91986 +62.84257489 223.6067977 6725.999703 +62.90630975 223.6067977 6725.999703 +62.97004461 223.6067977 6760.923084 +63.03377948 223.6067977 6786.457332 +63.09751434 223.6067977 6823.666667 +63.1612492 223.6067977 6840.197448 +63.22498407 223.6067977 6861 +63.28871893 223.6067977 6877.250672 +63.35245379 223.6067977 6893.350361 +63.41618866 223.6067977 6913 +63.47992352 225.166605 6949.610649 +63.54365838 228.6307066 6970.640016 +63.60739324 228.6307066 6995.407386 +63.67112811 228.6307066 6995.407386 +63.73486297 228.6307066 7027 +63.79859783 228.6307066 7052.329482 +63.8623327 228.6307066 7091 +63.92606756 228.6307066 7125 +63.98980242 229.8097039 7147.333333 +64.05353728 229.8097039 7182.458881 +64.11727215 229.8097039 7204.052206 +64.18100701 232.5 7216.878365 +64.24474187 232.9982833 7242.859127 +64.30847674 232.9982833 7281.333333 +64.3722116 232.9982833 7302.5 +64.43594646 232.9982833 7332.132812 +64.49968133 232.9982833 7350.275107 +64.56341619 232.9982833 7367.656898 +64.62715105 232.9982833 7385.285316 +64.69088591 232.9982833 7414.666667 +64.75462078 232.9982833 7442.62232 +64.81835564 234.5 7462.451415 +64.8820905 234.5 7492 +64.94582537 234.5 7509.979852 +65.00956023 234.5 7546.729424 +65.07329509 242.3897688 7572.40652 +65.13702996 242.3897688 7597.929543 +65.20076482 243.284196 7629.240332 +65.26449968 243.284196 7640.288771 +65.32823454 243.284196 7659.985176 +65.39196941 243.284196 7682.715178 +65.45570427 244.9489743 7686.841484 +65.51943913 244.9489743 7721.959604 +65.583174 244.9489743 7750.5 +65.64690886 244.9489743 7771.32753 +65.71064372 244.9489743 7796.651149 +65.77437859 244.9489743 7825.754414 +65.83811345 244.9489743 7861.489431 +65.90184831 244.9489743 7884.666667 +65.96558317 244.9489743 7915.353309 +66.02931804 244.9489743 7962 +66.0930529 244.9489743 7964.5 +66.15678776 244.9489743 7988.602548 +66.22052263 244.9489743 8041.5 +66.28425749 244.9489743 8072.205399 +66.34799235 244.9489743 8097 +66.41172721 245 8137.031285 +66.47546208 245.3738644 8181.225458 +66.53919694 245.3738644 8203 +66.6029318 245.3738644 8238.450502 +66.66666667 248 8240.231717 +66.73040153 248 8305.129986 +66.79413639 251.7300141 8333.253416 +66.85787126 251.7300141 8376 +66.92160612 251.7300141 8411.666667 +66.98534098 251.7300141 8430.5 +67.04907584 252.6756815 8466.5 +67.11281071 253.5221884 8495.534423 +67.17654557 253.5221884 8521.073383 +67.24028043 253.5221884 8536.900169 +67.3040153 253.5221884 8550.557487 +67.36775016 253.5221884 8589.455184 +67.43148502 253.5221884 8616.096129 +67.49521989 253.5221884 8639.758571 +67.55895475 253.5221884 8664 +67.62268961 254.0341184 8688.652417 +67.68642447 254.4645358 8728.753426 +67.75015934 254.4645358 8764.996129 +67.8138942 254.4645358 8789.088792 +67.87762906 256.5 8831.895841 +67.94136393 261.1727398 8859.175647 +68.00509879 262.0954025 8877.637084 +68.06883365 262.5 8916.5 +68.13256851 262.5 8929 +68.19630338 262.5 8946.331096 +68.26003824 262.9118991 8975.890588 +68.3237731 262.9118991 9010.272637 +68.38750797 262.9118991 9051.115263 +68.45124283 262.9118991 9076.199921 +68.51497769 263.2717228 9123.306449 +68.57871256 263.2717228 9153.311168 +68.64244742 263.2717228 9193.786722 +68.70618228 263.8560213 9227.389941 +68.76991714 263.8560213 9276 +68.83365201 264.5751311 9295.339334 +68.89738687 264.5751311 9318.36371 +68.96112173 264.5751311 9353 +69.0248566 264.5751311 9384.14057 +69.08859146 264.5751311 9411.964088 +69.15232632 264.5751311 9455.537221 +69.21606119 266.5393029 9478.971019 +69.27979605 266.5393029 9514.469244 +69.34353091 266.5393029 9539 +69.40726577 270.6686166 9557 +69.47100064 271.4851132 9588.342093 +69.5347355 271.4851132 9624.453448 +69.59847036 272.512385 9664.843506 +69.66220523 272.512385 9685.666667 +69.72594009 272.512385 9704.913422 +69.78967495 272.512385 9730.333333 +69.85340982 273 9759.949953 +69.91714468 273.1181063 9809.630217 +69.98087954 273.1181063 9843.432332 +70.0446144 275.0645379 9869.225502 +70.10834927 275.0645379 9923.222471 +70.17208413 275.0645379 9943 +70.23581899 275.9307884 9966.5 +70.29955386 278 9997.075672 +70.36328872 278 10015.1069 +70.42702358 278 10073.6075 +70.49075844 279.5084972 10117.76038 +70.55449331 280.449639 10155.59124 +70.61822817 280.449639 10182.5 +70.68196303 281.2055679 10233.38811 +70.7456979 281.2055679 10255.5 +70.80943276 281.6913204 10281.38959 +70.87316762 282.3242816 10315.20956 +70.93690249 282.3242816 10353.44195 +71.00063735 282.5 10384.5 +71.06437221 282.8427125 10412.92123 +71.12810707 282.8427125 10434.5676 +71.19184194 282.8427125 10454.65867 +71.2555768 284.1408102 10496.22789 +71.31931166 284.1408102 10497.33333 +71.38304653 284.5 10525.28444 +71.44678139 288.3868929 10567.35729 +71.51051625 289 10600.94486 +71.57425112 289.1428219 10621.75 +71.63798598 290.2645345 10671.41082 +71.70172084 290.2673337 10708.42509 +71.7654557 290.2673337 10744.77404 +71.82919057 290.2673337 10784.10864 +71.89292543 290.6546797 10818.5797 +71.95666029 290.6546797 10845.11584 +72.02039516 291.0810311 10894.51388 +72.08413002 293.3721186 10937.95012 +72.14786488 293.3721186 10961.66667 +72.21159975 294.5 11021.06607 +72.27533461 294.5 11057.35615 +72.33906947 295 11101.5 +72.40280433 296 11131.27495 +72.4665392 297.0800758 11159.60335 +72.53027406 297.6919549 11211.19763 +72.59400892 298.2914682 11246.53335 +72.65774379 298.3990617 11261 +72.72147865 298.5919337 11289.66687 +72.78521351 299.8132752 11327.92037 +72.84894837 299.8132752 11370.98415 +72.91268324 300 11383.5 +72.9764181 300 11413.39319 +73.04015296 300 11443.33333 +73.10388783 300.8597205 11473.5 +73.16762269 302.5315411 11507.29456 +73.23135755 302.5315411 11514.30971 +73.29509242 302.5315411 11542.40392 +73.35882728 305.0173297 11544.11863 +73.42256214 306.5291876 11570.9813 +73.486297 307 11602.91517 +73.55003187 307 11629.2761 +73.61376673 307.2378964 11660.19082 +73.67750159 307.7276935 11719.66667 +73.74123646 308.1301673 11738.10832 +73.80497132 308.6357076 11756.79821 +73.86870618 308.6357076 11779.80051 +73.93244105 311.8206902 11796.33435 +73.99617591 312 11829.40246 +74.05991077 312.5 11872.5 +74.12364563 313.2483041 11896.71804 +74.1873805 314 11931.10542 +74.25111536 314.6666667 11968.47108 +74.31485022 316.227766 11988.28837 +74.37858509 316.783838 12026.20611 +74.44231995 317.2089217 12051.95919 +74.50605481 319.7579442 12075.96961 +74.56978967 321.380032 12104.72574 +74.63352454 322.0871388 12143.98556 +74.6972594 322.1614502 12174.96456 +74.76099426 322.8685491 12210.38084 +74.82472913 322.8685491 12229.4764 +74.88846399 323 12249.18038 +74.95219885 323.7826431 12268.72679 +75.01593372 323.893501 12271.5 +75.07966858 323.893501 12313.13192 +75.14340344 324.2084592 12342.57057 +75.2071383 325.9762261 12365.45592 +75.27087317 327.0368863 12397 +75.33460803 328.6666667 12431.19631 +75.39834289 330.1417877 12462.59556 +75.46207776 330.5 12493 +75.52581262 330.6811153 12528.51795 +75.58954748 331.662479 12570 +75.65328235 333.1741286 12610.08175 +75.71701721 335.4101966 12650.5 +75.78075207 336.1501153 12678.50543 +75.84448693 336.3965913 12718.30744 +75.9082218 336.7825708 12753 +75.97195666 337.6666667 12798.63274 +76.03569152 337.6666667 12816.95508 +76.09942639 338.0474819 12836.80589 +76.16316125 340.3295462 12856.46155 +76.22689611 341 12882.27481 +76.29063098 342.4358125 12905.21897 +76.35436584 344.3259555 12949 +76.4181007 344.9289782 12979.65208 +76.48183556 346.4101615 13002.50541 +76.54557043 346.4101615 13034.45285 +76.60930529 346.4101615 13076.05891 +76.67304015 348.1052796 13113.47739 +76.73677502 349.7210317 13158.23734 +76.80050988 350 13201.86467 +76.86424474 351.2607164 13256.50264 +76.9279796 351.5017781 13304.6508 +76.99171447 352 13372.38306 +77.05544933 352.8462838 13408.33333 +77.11918419 353.5533906 13465.60136 +77.18291906 353.5533906 13529.5 +77.24665392 353.5533906 13559.0975 +77.31038878 355.7562368 13575.74309 +77.37412365 357.176427 13634.70396 +77.43785851 357.7708764 13662.99412 +77.50159337 358.6653036 13679.73728 +77.56532823 360.0069444 13709.08367 +77.6290631 360.9780118 13763.89283 +77.69279796 362.038672 13793.47528 +77.75653282 363.2238586 13832.74378 +77.82026769 365 13861.60261 +77.88400255 366.3794796 13894.03198 +77.94773741 367.1623619 13933.9224 +78.01147228 369.1097398 13959.17481 +78.07520714 370.0815226 14006.67644 +78.138942 370.5 14018.39194 +78.20267686 370.5 14040.31225 +78.26641173 370.8589538 14100.04827 +78.33014659 372.2000806 14133.66667 +78.39388145 372.7585319 14163.34883 +78.45761632 374.413675 14199.35252 +78.52135118 375.9948138 14255.93285 +78.58508604 376.1066338 14318.91232 +78.64882091 377.4482746 14400.2296 +78.71255577 379.0304517 14443.00956 +78.77629063 379.6922843 14492.40378 +78.84002549 381 14526.71012 +78.90376036 382.2058782 14557.30969 +78.96749522 382.5447686 14630.80223 +79.03123008 384.1616413 14693.52315 +79.09496495 385.6666667 14708.98971 +79.15869981 387 14731.43965 +79.22243467 387.7435891 14759.43984 +79.28616953 389.3034072 14778.17326 +79.3499044 391.5377194 14812.5 +79.41363926 392 14866.21297 +79.47737412 393.4610164 14914.4781 +79.54110899 394.6666667 14943.5 +79.60484385 394.9075841 15015.14845 +79.66857871 395.3368184 15057 +79.73231358 396.3333509 15096.97453 +79.79604844 398.6590858 15104.06039 +79.8597833 400.969762 15145.05226 +79.92351816 401.7163178 15173.5 +79.98725303 403.4044187 15211.50423 +80.05098789 404.7283039 15233.38685 +80.11472275 406.3526793 15275.5 +80.17845762 407.3013626 15328.0723 +80.24219248 409.6666667 15362.71331 +80.30592734 411 15402.55048 +80.36966221 412.3307734 15440.4966 +80.43339707 412.9503602 15479 +80.49713193 414.2490624 15518.59789 +80.56086679 415.4614302 15545.5 +80.62460166 416.3333333 15627.05986 +80.68833652 419.039139 15673.49488 +80.75207138 420.3749814 15733.79372 +80.81580625 421.3805881 15817.05045 +80.87954111 423.3534772 15872.69353 +80.94327597 426 15902.09079 +81.00701083 427.6738513 15951.61059 +81.0707457 428.6666667 15990.26904 +81.13448056 430.6280297 16043.78774 +81.19821542 433.0127019 16086.67927 +81.26195029 434.2995181 16139 +81.32568515 435.5 16198.07643 +81.38942001 436.4804692 16279.54554 +81.45315488 438 16329.05315 +81.51688974 439.2751605 16391 +81.5806246 440.5053916 16443.06109 +81.64435946 442.2503062 16474.88138 +81.70809433 442.9201652 16524.37837 +81.77182919 444.4862203 16589.00528 +81.83556405 444.7701654 16638.22256 +81.89929892 444.8483824 16685.59872 +81.96303378 446.2917581 16748.53122 +82.02676864 448.0238089 16801.5 +82.09050351 448.6592527 16849.5 +82.15423837 451.5732499 16918.17114 +82.21797323 453.6666667 16994.15159 +82.28170809 455.3333333 17048.5761 +82.34544296 457.6666667 17099.06776 +82.40917782 459.2793268 17134.98891 +82.47291268 461.3008238 17186.23032 +82.53664755 462.5 17225.5 +82.60038241 465.6666667 17270 +82.66411727 467.044029 17327 +82.72785214 468.6759465 17373.98821 +82.791587 472.6666667 17422.69907 +82.85532186 474.0254213 17483.21516 +82.91905672 476.0974319 17533.01597 +82.98279159 478.0041841 17628.17205 +83.04652645 478.0041841 17680.41696 +83.11026131 478.0041841 17749.96972 +83.17399618 480.0337488 17829.95884 +83.23773104 481.2018288 17866.68143 +83.3014659 482.2473432 17909.60055 +83.36520076 484 17970.5 +83.42893563 486.9499828 17999.47199 +83.49267049 487.8609775 18029.66129 +83.55640535 489.0860281 18085.67014 +83.62014022 491.868885 18126.79816 +83.68387508 497.5492605 18159.5 +83.74760994 499.9847998 18232.42808 +83.81134481 501.7173839 18235.22639 +83.87507967 502.9618939 18284.2757 +83.93881453 504.5 18306.55948 +84.00254939 505.7588358 18345.32139 +84.06628426 506.5 18391.2119 +84.13001912 509.5116126 18448.6505 +84.19375398 509.6666667 18502.63275 +84.25748885 511.3632404 18567.5 +84.32122371 512.6524164 18613 +84.38495857 514.0739895 18614.36669 +84.44869344 516.25 18651.2314 +84.5124283 519.7543651 18699.79787 +84.57616316 523.9453693 18753.88605 +84.63989802 525.0073015 18815.26792 +84.70363289 527.25 18879.93115 +84.76736775 528.7328248 18943.52069 +84.83110261 529.6229791 19017.99043 +84.89483748 532.6666667 19076.34313 +84.95857234 536.3584001 19121.53343 +85.0223072 536.3933125 19194.5 +85.08604207 538.5450095 19251 +85.14977693 540.1170244 19318.14001 +85.21351179 542 19399.55051 +85.27724665 543.5807646 19445.68257 +85.34098152 544.1186681 19530.5 +85.40471638 545.7355337 19606.5 +85.46845124 548 19658.27562 +85.53218611 548.7148622 19730.94545 +85.59592097 550.1290758 19774.78465 +85.65965583 553.390233 19825.15282 +85.72339069 556.9724281 19919.78797 +85.78712556 560.7356775 19975.41302 +85.85086042 563.25 20107.93406 +85.91459528 565.7942001 20176.5 +85.97833015 568.1126649 20239.20948 +86.04206501 569.3997514 20284.04701 +86.10579987 571.5476066 20316 +86.16953474 573.5012674 20389.67146 +86.2332696 577.0589586 20441 +86.29700446 581.3776741 20540.39053 +86.36073932 584.0702013 20605 +86.42447419 586.1915216 20630.41037 +86.48820905 588.4998726 20663.05697 +86.55194391 590.7584713 20711.35603 +86.61567878 590.7691597 20796 +86.67941364 594.8818829 20895.5 +86.7431485 598.9324431 20985.51505 +86.80688337 601.0216302 21080.96848 +86.87061823 602.1181551 21146.02829 +86.93435309 604.5 21200.87923 +86.99808795 608 21310.57579 +87.06182282 613.1459859 21405.27437 +87.12555768 616.25 21473.67357 +87.18929254 619.6666667 21573.47266 +87.25302741 622.6075208 21673.5 +87.31676227 625 21731.66667 +87.38049713 625.6666667 21827.5 +87.44423199 628.9770266 21884.55786 +87.50796686 631.3325194 21976.5233 +87.57170172 632.5 22089.99865 +87.63543658 635.0852961 22224.31101 +87.69917145 636.6286615 22288.60714 +87.76290631 638 22318.0462 +87.82664117 639.8070732 22396.50121 +87.89037604 643.7455501 22535.13571 +87.9541109 648.063365 22566.31262 +88.01784576 648.6666667 22648.00919 +88.08158062 650.723082 22725.6613 +88.14531549 654.5 22795.00131 +88.20905035 660.424507 22887.63229 +88.27278521 665.3874811 22957.43437 +88.33652008 665.3874811 23012.01499 +88.40025494 669.0238479 23097.24057 +88.4639898 674.7116733 23186.03136 +88.52772467 676.9368919 23305.28941 +88.59145953 680.3360514 23429.18951 +88.65519439 687.3863542 23491.23574 +88.71892925 694.1199464 23595.5 +88.78266412 698 23655.36313 +88.84639898 705.6925676 23719.31468 +88.91013384 707.1067812 23808.28532 +88.97386871 713.2189606 23899.99174 +89.03760357 719.7417361 23992.13309 +89.10133843 723.0077224 24107.87805 +89.1650733 726.7220927 24214.64764 +89.22880816 729 24296.05403 +89.29254302 733.8683596 24405.78755 +89.35627788 740.2187276 24460.23777 +89.42001275 747.8786666 24593.38942 +89.48374761 754.3041345 24698.5 +89.54748247 762.5 24736 +89.61121734 768.162181 24806.7201 +89.6749522 770.3091525 24848 +89.73868706 779.0665202 24903 +89.80242192 782.886965 25003.29578 +89.86615679 787.9848016 25048.34143 +89.92989165 794.7880221 25140.74622 +89.99362651 798 25278.36032 +90.05736138 803 25365.5 +90.12109624 811 25494.75265 +90.1848311 818.2930841 25583.0504 +90.24856597 822.7241336 25706.28468 +90.31230083 824.6618701 25708.76549 +90.37603569 828.7291476 25829.80153 +90.43977055 834.5 25896.84044 +90.50350542 840.7499628 25933.99674 +90.56724028 841.810623 26039.20722 +90.63097514 849.4709465 26235 +90.69471001 857.6666667 26425.32182 +90.75844487 863.7160027 26579.4368 +90.82217973 878.1497594 26681.26808 +90.8859146 881.3267839 26791.56883 +90.94964946 886.7046537 26949.55587 +91.01338432 890.3895215 27078.70086 +91.07711918 901.4452681 27147.95065 +91.14085405 907.8832983 27213.82002 +91.20458891 917.4095777 27328.29764 +91.26832377 926 27441.452 +91.33205864 934.7322989 27552.41573 +91.3957935 937.7002816 27644.33333 +91.45952836 943.9662713 27776.56858 +91.52326322 949.1638425 27881.97883 +91.58699809 954.6862461 27980.21533 +91.65073295 960.7876797 28115.22606 +91.71446781 970.3333333 28291.34232 +91.77820268 982.1713191 28454.5 +91.84193754 987.1210665 28597 +91.9056724 990.5 28743.5 +91.96940727 994.1921343 28882.66667 +92.03314213 1001 29009.0557 +92.09687699 1005.744169 29146.66667 +92.16061185 1011.597153 29258.66439 +92.22434672 1015.174862 29286.24156 +92.28808158 1029.19392 29467 +92.35181644 1038.062504 29575.72001 +92.41555131 1038.739862 29745 +92.47928617 1039.230485 29950.21482 +92.54302103 1053 30146.0834 +92.6067559 1069.261494 30338.41645 +92.67049076 1076.142891 30381 +92.73422562 1085.513966 30546.03021 +92.79796048 1087.883783 30626.69973 +92.86169535 1092.759387 30714.18253 +92.92543021 1102.5 30748.53838 +92.98916507 1118.842551 30914.56118 +93.05289994 1133.915929 31042.96927 +93.1166348 1146.220092 31152.2023 +93.18036966 1152.376359 31274.67679 +93.24410453 1162.5 31405.44058 +93.30783939 1171 31538.33333 +93.37157425 1181.950827 31733.53813 +93.43530911 1188.333333 31927.99249 +93.49904398 1202.081528 32131.27453 +93.56277884 1209.265562 32335.9931 +93.6265137 1217.423568 32433.28544 +93.69024857 1225.425946 32581.67147 +93.75398343 1245.344531 32661.5 +93.81771829 1259.357177 32813.74165 +93.88145315 1265.5697 32904.86651 +93.94518802 1265.5697 33088.52128 +94.00892288 1275 33278.3594 +94.07265774 1276.333333 33381.80403 +94.13639261 1284.604349 33477 +94.20012747 1293.738832 33657.49997 +94.26386233 1304.761536 33791.33333 +94.3275972 1317 33890.32904 +94.39133206 1318.04704 34035.19069 +94.45506692 1323.864167 34257.90306 +94.51880178 1338.908361 34513.71872 +94.58253665 1345.553524 34651.76781 +94.64627151 1356.666667 34668.66667 +94.71000637 1372.498525 34790.91931 +94.77374124 1381.89001 34948.40085 +94.8374761 1390.836798 35111.26795 +94.90121096 1428.717047 35111.80247 +94.96494583 1439.669406 35212.6398 +95.02868069 1445.685074 35365.5 +95.09241555 1450 35642.47635 +95.15615041 1450 35903.20187 +95.21988528 1460.5 36057.49609 +95.28362014 1468.469101 36262.79306 +95.347355 1486.432026 36445.52442 +95.41108987 1501.68805 36637.07938 +95.47482473 1518.502742 36806.5 +95.53855959 1546.144021 37014.81677 +95.60229446 1566.948627 37158.46135 +95.66602932 1587.219022 37256.33074 +95.72976418 1603 37372.27325 +95.79349904 1612.988996 37550.90561 +95.85723391 1616.580754 37679.98149 +95.92096877 1616.580754 37858.49706 +95.98470363 1628.705109 38188.01613 +96.0484385 1631.88241 38396.56821 +96.11217336 1662.5 38557.11856 +96.17590822 1683.639348 38697.77503 +96.23964308 1684.840695 38923.53736 +96.30337795 1702.570292 39073.49905 +96.36711281 1737.24696 39219 +96.43084767 1768.47406 39418.01228 +96.49458254 1818.364673 39598.68685 +96.5583174 1838.477631 39743.34916 +96.62205226 1863.666667 39889.21665 +96.68578713 1900 40089.47064 +96.74952199 1930.947975 40439.28937 +96.81325685 1982.453698 40778.27728 +96.87699171 1984.5 41378.69379 +96.94072658 2005.001278 41600.5 +97.00446144 2054.713646 41807.52426 +97.0681963 2057.676359 41973 +97.13193117 2106.076922 42156.64546 +97.19566603 2141 42378.5 +97.25940089 2147.794256 42546.61502 +97.32313576 2186.427268 42719.5 +97.38687062 2220 42943.5 +97.45060548 2269.67666 43233.67291 +97.51434034 2296.5 43516.04449 +97.57807521 2370.232056 43516.5 +97.64181007 2442.680659 43691.5 +97.70554493 2462 43978 +97.7692798 2554.774941 44300.23984 +97.83301466 2591.365002 44461.5 +97.89674952 2624.628259 44746.95526 +97.96048438 2690.358311 45179.49893 +98.02421925 2801.91062 45461.7149 +98.08795411 2877.217493 45573.03205 +98.15168897 2979.336973 45824.64586 +98.21542384 3064.913912 46076.49208 +98.2791587 3169.606147 46409.53937 +98.34289356 3283.132752 46662.71377 +98.40662843 3386.483146 46949.54654 +98.47036329 3462.5 47461.7548 +98.53409815 3642.107522 47929.69381 +98.59783301 3714.108911 48113.47467 +98.66156788 3739.580496 48526.84129 +98.72530274 3838.882715 48891.62633 +98.7890376 3942.827412 49484.69157 +98.85277247 4260.793504 49846.07833 +98.91650733 4483.7641 50164.98348 +98.98024219 4520.53332 50480.35311 +99.04397706 4762 51305.5 +99.10771192 5060.056126 51933 +99.17144678 5376.986352 52460.31357 +99.23518164 5444.722215 52979.97558 +99.29891651 5444.722215 53372.60612 +99.36265137 5444.722215 53969.54846 +99.42638623 5444.722215 54938.34222 +99.4901211 5444.722215 55449.5 +99.55385596 5459.571458 56188.84521 +99.61759082 5748.071024 56830.8964 +99.68132569 6122.222254 57893.14158 +99.74506055 6614.5 59304.59616 +99.80879541 7690.305586 60478.13589 +99.87253027 8976.666667 62969.57313 +99.93626514 10304.69567 64989.04536 +100 14746.74475 70892.72389 diff --git a/docs/TUGboat/tb133chernoff-widows.bib b/docs/TUGboat/tb133chernoff-widows.bib new file mode 100644 index 0000000..b18838d --- /dev/null +++ b/docs/TUGboat/tb133chernoff-widows.bib @@ -0,0 +1,219 @@ +% lua-widow-control +% https://github.com/gucci-on-fleek/lua-widow-control +% SPDX-License-Identifier: MPL-2.0+ OR CC-BY-SA-4.0+ +% SPDX-FileCopyrightText: 2022 Max Chernoff + +@book{texbook, + author = {Donald E. Knuth}, + title = {The \TeX{}book}, + publisher = {Addison--Wesley}, + xurl = {https://ctan.org/pkg/texbook}, + year = {2021}, +} + +@misc{widows-and-orphans, + urlnewline = 1, + author = {Frank Mittelbach}, + year = {2021}, + month = {March}, + title = {The \textsf{widows-and-orphans} package}, + url = {https://ctan.org/pkg/widows-and-orphans}, +} + +@misc{widow-assist, + author = {jeremie}, + title = {Paragraph callback to help with widows\slash orphans hand tuning}, + url = {https://tex.stackexchange.com/q/372062}, + year = {2017}, + month = {August}, +} + +@book{elements, + title = {The Elements of Typographic Style}, + author = {Robert Bringhurst}, + edition = {3rd}, + publisher = {Hartley \& Marks}, + year = {2004}, +} + +@misc{oed-club, + title={club, n.}, + journal={OED Online}, + publisher={Oxford University Press}, + month=sep, + year={2021}, + url={www.oed.com/view/Entry/34788}, + author={{Oxford English Dictionary}} +} + +@misc{oed-line, + title={line at end of paragraph}, + journal={OED Online}, + publisher={Oxford University Press}, + urlnewline=1, + url={https://www.oed.com/view/th/class/195380}, + author={{Oxford English Dictionary}} +} + +@misc{oed-widow, + title={widow, n.}, + journal={OED Online}, + publisher={Oxford University Press}, + month=dec, + year={2021}, + url={www.oed.com/view/Entry/228912}, + author={{Oxford English Dictionary}} +} + +@article{widowhistory, + title={The Typographical Widow}, + subtitle={Who is she? What is she?}, + author={Karl Brown}, + journal={Bulletin of the {New York} Public Library}, + publisher={The {New York} Public Library}, + location={New York}, + volume={52}, + number={1}, + year={1948}, + month=jan, + pages={3--25}, + url={https://hdl.handle.net/2027/uc1.b3310084} +} + +@article{widowhistory2, + title={The Typographical Widow: Encore}, + subtitle={Encore}, + author={Karl Brown}, + journal={Bulletin of the {New York} Public Library}, + publisher={The {New York} Public Library}, + location={New York}, + volume={52}, + number={9}, + year={1948}, + month=sep, + pages={458-466}, + urlnewline=1, + url={https://hdl.handle.net/2027/uc1.b3310084} +} + +@book{old, + title={Mechanick exercises}, + subtitle={The doctrine of handy-works applied to the art of printing}, + volume={\unskip~2,\gobble}, + author={Moxon, Joseph}, + year={1683}, + location={London}, + pages={394}, + publisher={\hspace*{-.66em}\gobble}, + url={https://archive.org/details/mechanickexercis00moxo_0} +} + +@article{global, + url = {https://doi.org/10.1111/coin.12165}, + year = {2018}, + month = mar, + publisher = {Wiley}, + volume = {35}, + number = {2}, + pages = {242--284}, + author = {Frank Mittelbach}, + title = {A general framework for globally optimized pagination}, + journal = {Computational Intelligence} +} + +@book{backwards1, + title={The Layout Book}, + author={Ambrose, G. and Harris, P.}, + isbn={9782940373536}, + series={Advanced Level Series}, + year={2007}, + publisher={Bloomsbury Academic} +} + +@book{backwards2, + title={Typography Essentials Revised and Updated}, + subtitle={100 Design Principles for Working with Type}, + author={Saltz, I.}, + isbn={9781631596483}, + year={2019}, + publisher={Rockport Publishers} +} + +@book{backwards3, + title={Advanced Typography: From Knowledge to Mastery}, + author={Hunt, R.}, + isbn={9781350055926}, + lccn={2020024110}, + year={2020}, + publisher={Bloomsbury Publishing} +} + +@article{gutenberg, + title={All Books (sorted by popularity)}, + url={https://www.gutenberg.org/ebooks/search/?sort_order=downloads}, + year={2022}, + month=mar, + journal={Project Gutenberg} +} + +@misc{etex, + title={The {\eTeX} manual}, + author={{The \NTS{} Team}}, + url={ctan.org/pkg/etex}, + year={1998}, + month=feb, +} + +@String{j-TUGboat = "TUGboat"} +@Article{xIsambert:TB31-1-12, +urlnewline = 1, + author = "Paul Isambert", + title = "Strategies against widows", + journal = j-TUGboat, + volume = "31", + number = "1", + pages = "12--17", + year = "2010", + ISSN = "0896-3207", + ISSN-L = "0896-3207", + bibdate = "Sun Nov 27 15:57:23 MST 2011", + bibsource = "http://www.math.utah.edu/pub/tex/bib/index-table-t.html#tugboat; + http://www.math.utah.edu/pub/tex/bib/tugboat.bib", + URL = "https://tug.org/TUGboat/tb31-1/tb97isambert.pdf", + acknowledgement = ack-bnb # " and " # ack-nhfb, + fjournal = "TUGboat", + issue = "97", + journal-URL = "https://tug.org/TUGboat/", +} + +@Article{xThanh:2000:MTE, +urlnewline = 1, + author = "\Thanh", + title = "Micro-typographic extensions to the {\TeX} typesetting + system", + journal = j-TUGboat, + volume = "21", + number = "4", + pages = "317--317", + month = dec, + year = "2000", + CODEN = "????", + ISSN = "0896-3207", + ISSN-L = "0896-3207", + bibdate = "Mon Aug 10 16:37:32 MDT 2020", + bibsource = "http://www.math.utah.edu/pub/tex/bib/tugboat.bib", + URL = "https://tug.org/TUGboat/tb21-4/tb69thanh.pdf", + acknowledgement = ack-nhfb, + fjournal = "TUGboat", + issue = "69", + journal-URL = "https://tug.org/TUGboat/", +} + +@phdthesis{plass, + title={Optimal pagination techniques for automatic typesetting systems}, + author={Plass, Michael Frederick}, + year={1981}, + school={Stanford University}, + urlnewline=1, + url={tug.org/docs/plass/plass-thesis.pdf} +} diff --git a/docs/TUGboat/tb133chernoff-widows.ltx b/docs/TUGboat/tb133chernoff-widows.ltx new file mode 100644 index 0000000..bedc81d --- /dev/null +++ b/docs/TUGboat/tb133chernoff-widows.ltx @@ -0,0 +1,1437 @@ +% lua-widow-control +% https://github.com/gucci-on-fleek/lua-widow-control +% SPDX-License-Identifier: MPL-2.0+ OR CC-BY-SA-4.0+ +% SPDX-FileCopyrightText: 2022 Max Chernoff + +\documentclass[final]{ltugboat} + +% This is the LaTeX source for the following article: +% @article{tb133chernoff-widows, +% title={Automatically removing widows and orphans with +% \texttt{lua-widow-control}}, +% author={Chernoff, Max}, +% journal={TUGboat}, +% volume={43}, +% number={1}, +% year={2022}, +% month=may, +% DOI={10.47397/tb/43-1/tb133chernoff-widows}, +% } +% Please refer to the PDF on tug.org for the authoritative version. + +% Compiling: +% context tb1333chernoff-widows-figure.ctx +% lualatex tb1333chernoff-widows.ltx +% bibtex tb1333chernoff-widows +% lualatex tb1333chernoff-widows.ltx +% lualatex tb1333chernoff-widows.ltx +% The original article was built with the final/frozen TeX Live 2021. + +% Set the publication info +\vol 43, 1. +\issyear 2022. +\issueseqno=133 +\setcounter{page}{28} +\PrelimDraftfalse + +% Load lwc +\usepackage[balanced]{lua-widow-control} + +% Table Stuff +\usepackage{tabularx} +\usepackage{hhline} +\usepackage{booktabs} +\AddToHook{env/tabularx/before}{\smallskip\noindent} +\AddToHook{env/tabularx/after}{\smallskip} +\AddToHook{env/tabular/before}{\smallskip\noindent} +\AddToHook{env/tabular/after}{\smallskip} +\renewcommand{\arraystretch}{1.15} + +\usepackage{graphicx} + +% Let the macro names in section headings be in boldface +\usepackage{lmodern} +\DeclareRobustCommand{\cs}[1]{\texttt{\textbackslash#1}} + +\makeatletter +\DeclareRobustCommand{\eTeX}{% + \ifx\f@series\bfseries@rm% + \ensuremath{\boldsymbol{\varepsilon}}\mbox{-}\kern-.125em\TeX% + \else% + \ensuremath{\varepsilon}\mbox{-}\kern-.125em\TeX% + \fi% +} +\let\goodeTeX=\eTeX +\makeatother + +\usepackage{mathtools} + +% Abbreviations +% Most of these are just typewriter commands with `\allowbreak`s added. +\def\lwc/{\textsf{lua-\allowbreak widow-\allowbreak control}} +\def\Lwc/{\textsf{lua-\allowbreak widow-\allowbreak control}} +\def\estretch/{% + \texorpdfstring{\cs{emergency}\-\mbox{\tt stretch}}{\textbackslash{}emergencystretch}% + } +\def\openalty/{\cs{output}\-\mbox{\tt penalty}} +\def\waos/{widows and orphans} +\def\wao/{widow and orphan} +\def\woo/{widow or orphan} +\def\woos/{widow or orphans} +\def\latexuse/{% + \cs{use\-package\{lua-\allowbreak widow-\allowbreak control\}} +} +\def\lsness/{\texorpdfstring{% + \cs{loose}\-\mbox{\tt ness}}{\textbackslash{}looseness}% +} +\def\plainop/{Plain~\TeX\slash\OpTeX{}} +\newcommand{\LuaMetaTeX}{Lua\-Meta\-\TeX{}} +\newcommand{\q}[1]{\texorpdfstring{``#1''}{“#1”}} + +\def\inlineurl[#1]#2{\href{https://#1}{#2}\footnote{\raggedright\tbsurl{#1}}} + +\def\longs/{\char"017F} +\def\endofline#1{\unskip\nobreak\hskip\fontdimen2\font plus 1fill\hbox{#1}} + +% Additional macros +\def\dots{\ensuremath{\mathellipsis}} +\def\ttbs{{\tt\char`\\}} + +\ifdefined\tubsentencespace\else + \def\tubsentencespace{\spacefactor=3000{}\space\ignorespaces} +\fi + +% Figures +\makeatletter +\renewcommand*{\fps@figure}{tb} +\renewcommand*{\fps@table}{tb} +\g@addto@macro\@floatboxreset\centering +\makeatother + +% pgfplots +\usepackage{pgfplots} +\usepackage{pgfplotstable} +\usetikzlibrary{patterns} + +\pgfkeys{ + /pgf/number format/.cd, + sci generic={% + mantissa sep={\times}, + exponent={10^{##1}} + }, + 1000 sep={\,}, +} + +\pgfplotsset{ + compat=1.18, + lua backend=true, + unbounded coords=discard, + filter discard warning=false, +} + +\pgfplotstableread{\jobname-plot.dat}{\plotdata} + +% Metadata +\title{Automatically removing \waos/ with \lwc/} +\author{Max Chernoff} +\address{Calgary, Alberta\\Canada} + +% Load last +\AddToHookNext{shipout/foreground}{% + \put(1in, \dimexpr-\baselineskip - 0.5ex){% + \fbox{\parbox[t]{\textwidth}{% + This document is a part of the \lwc/ documentation. Some commands + may have changed since publication, so check the package manual for + the current syntax. For the authoritative version of the article, + please see + \tbsurl{https://tug.org/TUGboat/tb43-1/tb133chernoff-widows.html}. + }% + }}% +} + +\let\nameref=\undefined % latex3/hyperref#234 +\usepackage[hidelinks,pdfa]{hyperref} + +\begin{document} + \maketitle + + \begin{abstract} + The \textsf{lua-widow-control} package, for + plain~Lua\TeX\slash{}\LuaLaTeX\slash{}\ConTeXt\slash{}\OpTeX{}, + removes widows and orphans without any user intervention. + Using the power of Lua\TeX{}, it does so \emph{without} stretching any glue + or shortening any pages or columns. Instead, \textsf{lua-widow-control} + automatically lengthens a paragraph on a page or column where a widow or + orphan would otherwise occur. + + To use \textsf{lua-widow-control}, all that most users need do is + place \verb|\usepackage{lua-widow-control}| in their preamble. No further + changes are required. + \end{abstract} + + \section{Motivation} + + \TeX{} provides top-notch typesetting: even 40 years after its first + release, no other program produces higher quality mathematical + typesetting, and its paragraph-breaking algorithm is still + state-of-the-art. However, its page breaking is not quite as sophisticated + as its paragraph breaking and thus suffers from some minor issues. + + Unmodified \TeX{} has only two familiar ways of dealing with \waos/: it can + either shorten a page by one line, or it can stretch vertical + whitespace. \TeX{} was designed for mathematical and scientific typesetting, + where a typical page has multiple section headings, tables, figures, and + equations. For this style of document, \TeX's default behaviour works quite + well, since the slight stretching of whitespace between the various document + elements is nearly imperceptible; however, for prose or other documents + composed almost entirely of paragraphs, there is little vertical whitespace + to stretch. + + Since no ready-made and fully-automated solution to remove + \waos/ from all types of documents was available, I decided to + create \lwc/. + + \section{What are \waos/?} + + \subsection{Widows} + + A \q{widow} occurs when the majority of a paragraph is on one page + or column, + but the last line is on the following page or column. It not only looks + quite odd for a lone line to be at the start of the page, but it makes a + paragraph harder to read since the separation of a paragraph and + its last line disconnects the two, causing the reader to lose context for + the widowed line. + + \subsection{Orphans} + + An \q{orphan} occurs when the first line of a paragraph is at the end + of the page or column preceding the remainder of the paragraph. They are not + as distracting for the reader, but they are still not ideal. + Visually, \waos/ are about equally disruptive; however, orphans tend not to + decrease the legibility of a text as much as widows, so many authors choose + to ignore them. + + See figure~\ref{tab:widow} for a visual reference. + + \begin{figure} + \def\firstpage#1{% + \parfillskip=0pt% + \spaceskip=0.2em plus 1fill% + \hskip 3em% + #1% + } + + \def\lastpage#1{% + \parfillskip=3em% + \spaceskip=0.2em plus 1fill% + #1% + } + + \renewcommand{\arraystretch}{1} + \renewcommand{\doublerulesep}{0.5em} + \begin{tabularx}{\linewidth}{|X|@{\hskip\doublerulesep}|X|} + \multicolumn1c{\bfseries Widow} & + \multicolumn1c{\bfseries Orphan} + \\ \hhline{-||-} + \firstpage{A widow occurs when the last line of a paragraph is + placed on a page separate from} + & \vskip2.25\baselineskip\relax\firstpage{An orphan is} + \\ \hhline{-||-} + \lastpage{where it begins.} + & \lastpage{when the first line of a paragraph occurs on the + page before all of the other lines.} \\ \hhline{-||-} + \end{tabularx} + \caption{The difference between \waos/. If we imagine that each box is a + different page, then this roughly simulates how \waos/ appear.} + \label{tab:widow} + \end{figure} + + \subsection{Broken hyphens} + + \q{Broken} hyphens occur whenever a page break occurs in a + hyphenated word. These are not related to \waos/; however, + breaking a word across two pages is at least as disruptive for the reader + as \waos/. \TeX{} identifies broken hyphens in the same ways as \waos/, so + \lwc/ treats broken hyphens in the same way. + + \section{History and etymology} + + The concept of \waos/ is nearly as old as printing itself. In \cite{old}, + a printers manual from 1683, we have: + \begin{quote} + Nor do good \emph{Compo\longs/iters} account it good Workman\longs/hip + to begin a \emph{Page} with a \emph{Break-line}, unle\longs/s it be a + very \longs/hort \emph{Break}, and cannot be gotten in the foregoing + \emph{Page}\,; but if it be a long \emph{Break}, he will let it be the + \emph{Direction-line} of the fore-going \emph{Page}, and \emph{Set} his + \emph{Direction} at the end of it. \endofline{(p.~226)} + \end{quote} + + \subsection{Widows} + + However, the terms \q{widow} and \q{orphan} are much newer. The earliest + published source that I could find referencing \q{widows} in typography is + \textsl{Webster's New International Dictionary} from~1934. However, no + one \Dash not even the editors of the dictionary~\cite{widowhistory} \Dash + seems to know how it got there. Even then, the definition is somewhat + different than it is now: + \begin{quote} + widow, n.\ c.\ \emph{Print}\@. A short line or single word carried over from + the foot of one column or page to the head of a succeeding column or + page. \endofline{\cite{widowhistory}} + \end{quote} + Contrast this with the modern definition: + \begin{quote} + \emph{Typography}\@. A short line of text (usually one consisting of one + word or part of a word) which falls undesirably at the end of a + paragraph, esp.\ one set at the top of a page or column. + \endofline{\cite{oed-widow}} + \end{quote} + which includes a single lone line of any length. + + \subsection{Orphans} + + The term \q{orphan} is even more confusing. Its initial usage seems to have + occurred some time after \q{widow}~\cite{widowhistory}, and it is given many + contradictory definitions. Most sources define an orphan as a first line at + the bottom of the page and a widow as the last line at the + top~\cite{elements, widowhistory, widowhistory2, xIsambert:TB31-1-12, + texbook, widows-and-orphans, oed-line, oed-widow}; however, some sources + define these two terms as \emph{exact opposites} of each other, with a widow + as a first line at the bottom of the page and an orphan as the last + line!~\cite{backwards1, widowhistory, backwards3, oed-line, + backwards2}\tubsentencespace + This usage is plain wrong; nevertheless, it is sufficiently common that you + need to be careful when you see the terms \q{widow} and \q{orphan}. + + \subsection{Clubs} + + \textsl{The \TeX{}book} never refers to \q{orphans} as such; rather, it + refers to them as \q{clubs}. This term is remarkably rare: I could only find + a \emph{single} source published before \textsl{The \TeX{}book} \Dash a + compilation article about the definition of \q{widow} \Dash that mentions + a \q{club line}: + \begin{quote} + The Dictionary staff informs me that they have no example of the use of + the word widow in the typographical sense.~[\dots] + + Mr. Watson of the technical staff says that the Edinburgh printing + houses referred to it as a ``clubline''. + \endofline{\cite[p.~4]{widowhistory}} + \end{quote}\medskip + \begin{quote} + To my knowledge, a `widow', or `widow-line,' is a short line, forming + the end of a paragraph, which is carried over from the foot of a page or + column to the top of the succeeding one.~[\dots] + + To my personal knowledge, in typographical parlance in Edinburgh, + Scotland, the `widow' is called a `club-line.' + \endofline{\cite[p.~23]{widowhistory}} + \end{quote} + + Both quotes above are from separate authors, and they each define a \q{club} + like we define \q{widow}, not an \q{orphan}. In addition, they both mention + that the term is only used in Scotland. Even the extensive + \acro{OED}\Dash which lists 17~full definitions and + 103~subdefinitions for the noun \q{club}\Dash doesn't recognize the + phrase.~\cite{oed-club} + + I spent a few hours searching through Google Books and my university library + catalogue, but I could not find a single additional source. If anyone has + any more information on the definition of a \q{club line} or why Knuth chose + to use this archaic Scottish term in \TeX{}, please let me know! + + \section{Pagination in \TeX} + + Let's move on to looking at how \TeX{} treats these \waos/. + + \subsection{Algorithm} + + It is tricky to understand how \lwc/ works if you aren't familiar with how + \TeX{} breaks pages and columns. For a full description, you should + consult Chapter~15 of \textit{\TB}~\cite{texbook} (\q{How \TeX{} Makes Lines + into Pages}); however, this goes into much more detail than most users + require, so here is a \emph{very} simplified summary of \TeX{}'s page + breaking algorithm: + + \TeX{} fills the page with lines and other objects until the next object + will no longer fit. Once no more objects will fit, \TeX{} will align the + bottom of the last line with the bottom of the page by stretching any + available vertical spaces if (in \LaTeX) \cs{flushbottom} is set; + otherwise, it will + break the page and leave the bottom empty. + + However, some objects have penalties attached. Penalties encourage or + discourage page breaks from occurring at specific places. For example, + \LaTeX{} sets a negative penalty before section headings to encourage a + page break there; conversely, it sets a positive penalty after section + headings to discourage breaking. + + To reduce \waos/, \TeX{} sets weakly\hyph positive penalties between the + first and second lines of a paragraph to prevent orphans, and between the + penultimate and final lines to prevent widows. + + One important note: once \TeX{} begins breaking a page, it never goes + back to modify any content on the page. Page breaking is a localized + algorithm, without any backtracking. + + \subsection{Behaviour} + + Merely describing the algorithm doesn't allow us to intuitively + understand how + \TeX{} deals with \waos/. + + Due to the penalties attached to \waos/, \TeX{} tries to avoid + them. Widows and orphans with small penalties attached \Dash like + \LaTeX's default values of 150 \Dash are only lightly coupled to the rest + of the paragraph, while \waos/ with large penalties \Dash values of + 10\,000 or more \Dash are treated as infinitely bad and are thus + unbreakable. Intermediate values behave just as you would expect, + discouraging page breaks proportional to their value. + + When \TeX{} goes to break a page, it tries to avoid breaking at a + location with a high penalty. How it does so depends on a few settings: + + \subsubsection{\cs{flushbottom} and \cs{normalbottom}} + + With the settings \cs{normalbottom} (Plain \TeX{}) or + \cs{flushbottom} (\LaTeX{}), \TeX{} is willing to stretch any glue on the + page by an amount roughly commensurate to the magnitude of the + penalty: for small \cs{clubpenalty} and \cs{widowpenalty} values, \TeX{} will + only slightly stretch the glue on the page before creating a \woo/; + for very large penalties, \TeX{} will stretch the glue by a + near-infinite amount. + + This corresponds to the \q{Stretch} column in + Figure~\ref{fig:demo}. It is + the default behaviour of Plain~\TeX{}, and of the standard \LaTeX{} classes + when the \verb|twocolumn| option is given. + + \subsubsection{\cs{raggedbottom}} + + When \cs{raggedbottom} is set, \TeX{} won't stretch any glue. Instead, + for sufficiently-high \cs{clubpenalty} and \cs{widowpenalty} values, \TeX{} will + shorten the page or column by one~line in order to prevent the \woo/ from + being created. + + This corresponds to the \q{Shorten} column in Figure~\ref{fig:demo} and is + the default behaviour of the \LaTeX{} classes when the + \verb|twocolumn| option is not given. + + \section{\lsness/}\label{sec:looseness} + Before we can continue further, we need to discuss one more \TeX{} + command: \lsness/. The following is excerpted from Chapter~14 of + \cite{texbook} (\q{How \TeX{} Breaks Paragraphs into Lines}): + + \begin{quote}\parskip=0pt + If you set \lsness/\verb|=1|, \TeX{} will try to make the current + paragraph one line longer than its optimum length, provided that + there is a way to choose such breakpoints without exceeding the + tolerance you have specified for the badnesses of individual lines. + Similarly, if you set \lsness/\verb|=2|, \TeX{} will try to make the + paragraph two lines longer; and \lsness/\verb|=-1| causes an attempt + to make it shorter.~[\dots] + + For example, you can set \lsness/\verb|=1| if you want to avoid a + lonely \q{club line} or \q{widow line} on some page that does not + have sufficiently flexible glue, or if you want the total number of + lines in some two-column document to come out to be an even number. + + It's usually best to choose a paragraph that is already pretty + \q{full}, i.e., one whose last line doesn't have much white space, + since such paragraphs can generally be loosened without much harm. + You might also want to insert a tie between the last two words of + that paragraph, so that the loosened version will not end with only + one \q{widow word} on the orphans line; this tie will cover your + tracks, so that people will find it hard to detect the fact that you + have tampered with the spacing. On the other hand, \TeX{} can take + almost any sufficiently long paragraph and stretch it a bit, without + substantial harm. + \end{quote} + + The \wao/ removal strategy suggested in the second paragraph works quite + well; however, it requires manual editing each and every time a page + or paragraph is rewritten or repositioned. + + \begin{figure*}[p] + % Note: this figure may appear incorrect on some systems. + \divide\abovecaptionskip by 2 + \includegraphics{\jobname-figure} + \caption{A visual comparison of various automated widow-handling + techniques. + }\label{fig:demo} + \end{figure*} + + \section{Alternate removal strategies} + + \looseness=1 There have been a few previous attempts to improve upon \TeX's + previously-discussed \wao/-handling abilities; however, none of these + have been able to automatically remove \waos/ without stretching any glue + or shortening any pages. + + The articles \q{Strategies against + widows} by Paul Isambert~\cite{xIsambert:TB31-1-12} and + \q{Managing forlorn paragraph lines} by Frank + Mittelbach~\cite{Mittelbach:2018:MFP} both + begin with comprehensive discussions of the methods of preventing \waos/. + They agree that \waos/ are bad and ought to be avoided; however, they + differ in their solutions. \textsl{Strategies}~proposes an output routine + that reduces the length of facing pages by one line when necessary to + remove \waos/, while \textsl{Managing}~proposes that the author manually + rewrites or adjusts \lsness/ when needed. + + \looseness=1 The post \q{Paragraph callback \dots} by + jeremie~\cite{widow-assist} contains a file + \verb|widow-assist.lua| that + automatically detects which paragraphs can be safely shortened or + lengthened by one line. Mittelbach's \textsf{widows-and-orphans} + package~\cite{widows-and-orphans} alerts the author to + the pages that contain widows or orphans. Combined, these packages make + it simple for the author to quickly remove \waos/ by adjusting the + values of \lsness/; however, it still requires the author to make manual + source changes after each revision. + + Another article by Mittelbach~\cite{global} suggests an fully-automated + solution to remove \waos/. This would seem to offer a complete solution; + however, it requires multiple passes, an external tool, and has not yet been + publicly released. + + \pagebreak + + \Lwc/ is essentially a combination of + \verb|widow-assist.lua|~\cite{widow-assist} and + \textsf{widows-and-orphans}~\cite{widows-and-orphans} (although its + implementation is independent of both): when the \openalty/ + value indicates + that a \woo/ has occurred, Lua is used to find a stretchable paragraph. What + \lwc/ mainly adds on top of these packages is automation: it eliminates the + requirement for any manual adjustments or changes to your document's + source. + + \section{Visual comparison} + + Although \TeX{}'s page breaking algorithm is reasonably + straightforward, it can lead to + complex behaviour when \waos/ are involved. The usual + choices, when rewriting is not possible, are to ignore them, + stretch some glue, or shorten the + page. Figure~\ref{fig:demo} has a visual comparison of these + options, which we'll discuss in the following: + + \subsection{\q{Ignore}} + + As you can see, the last line of the page is on a separate page from the + rest of its paragraph, creating a widow. This is usually highly + distracting for the reader, so it is best avoided for the reasons previously + discussed. + + \subsection{\q{Shorten}} + + This page did not leave any widows, but it did shorten the previous page + by one line. Sometimes this is acceptable, but usually it looks bad because + pages will then have different text-block heights. This can make the pages + look quite uneven, especially when typesetting with columns or in a book + with facing pages. + + \subsection{\q{Stretch}} + + This page also has no widows and it has a flush bottom margin. However, + the space between each pair of paragraphs had to be stretched. + + If this page had many equations, headings, and other elements with + natural space between them, the stretched out space would be much less + noticeable. \TeX{} was designed for mathematical typesetting, so it makes + sense that this is its default behaviour. However, in a page with mostly + text, these paragraph gaps look unsightly. + + Also, this method is incompatible with grid typesetting, where + all glue stretching must be quantised to the height of a line. + + \subsection{\q{\lwc/}} + + \Lwc/ has none of these issues: it eliminates the widows in a document + while keeping a flush bottom margin and constant paragraph spacing. + + To do so, \lwc/ lengthened the second paragraph by one line. If you look + closely, you can see that this stretched the interword spaces. This + stretching is noticeable when typesetting in a narrow text block, but + is mostly imperceptible with larger widths. + + \Lwc/ automatically finds the \q{best} paragraph to stretch, so the + increase in interword spaces should almost always be minimal. + + \section{Installation and standard usage} + + The \lwc/ package was first released in + October~2021. It is available in the default installations of both + MiK\TeX{} and \TeX{}~Live, although you will need recent versions + of either. + + You may also download \lwc/ manually from either + \inlineurl[ctan.org/pkg/lua-widow-control]{\acro{CTAN},} + the \inlineurl% + [modules.contextgarden.net/cgi-bin/module.cgi/action=view/id=127]% + {\ConTeXt{} Garden,} or \inlineurl% + [github.com/gucci-on-fleek/lua-widow-control/releases/latest/]% + {GitHub,} although it is best if you can install it through your + \TeX~distribution. + + As its name may suggest, \lwc/ \textit{requires} \LuaTeX{}\footnote{Or + \LuaMetaTeX{} in the case of \ConTeXt{}.} regardless of the format used. + With that in mind, using \lwc/ is quite simple: + + \begin{tabularx}{\linewidth}{@{}l@{}>{\raggedleft\arraybackslash}X@{}} + Plain \TeX{} & + \cs{input lua-widow-control}\phantom{\tt]} \\ + \OpTeX {} & + \cs{load[lua-widow-control]} \\ + \LaTeX{} & + \latexuse/ \\ + \ConTeXt{} & + \cs{usemodule[lua-widow-control]} \\ + \end{tabularx} + + And that's usually enough. Most users won't need to do anything else since + \lwc/ comes preconfigured and ready-to-go. + + \section{Options} + + Nevertheless, \lwc/ does have a few options. + + \Lwc/ tries very hard to have a \q{natural} user interface with each + format, so how you set an option heavily depends on how you are running + \lwc/. Also note that not every option is available in every format. + + Some general guidelines: + \begin{description} + \item[\rm \plainop/\ ] Some options are set + by modifying a register, while others must be set manually using + \cs{directlua}. + + \item[\rm \LaTeX{}\ ] Options can be set either as package options + or at any point in the document with \cs{lwcsetup}. + + \item[\rm \ConTeXt{}\ ] Always use \cs{setuplwc}. + \end{description} + + \subsection{Disabling} + + You may want to disable \lwc/ for certain portions of your + document. You can do so with the following commands: + + \begin{tabularx}{\linewidth}{@{}Xl@{}} + \plainop/ & + \cs{lwcdisable} \\ + \LaTeX{} & + \cs{lwcsetup\{disable\}} \\ + \ConTeXt{} & + \cs{setuplwc[state=stop]}\hphantom{\texttt{a}} \\ + \end{tabularx} + + This prevents \lwc/ from stretching any paragraphs that follow. If a page + has earlier paragraphs where \lwc/ was still enabled and a \woo/ is + detected, \lwc/ will still attempt to remove the \woo/. + + \subsection{Enabling} + + \Lwc/ is enabled as soon as the package is loaded. If you + have previously disabled it, you will need to re-enable it to save new paragraphs. + + \begin{tabularx}{\linewidth}{@{}Xl@{}} + \plainop/ & + \cs{lwcenable} \\ + \LaTeX{} & + \cs{lwcsetup\{enable\}} \\ + \ConTeXt{} & + \cs{setuplwc[state=start]} \\ + \end{tabularx} + + \subsection{Automatically disabling} + + You may want to disable \lwc/ for certain commands where + stretching is undesirable such as section headings. Of course, manually + disabling and + then enabling \lwc/ multiple times + throughout a document would quickly become tedious, so \lwc/ provides + some options to do this automatically for you. + + \Lwc/ automatically patches the default \LaTeX{}, \ConTeXt{}, + Plain~\TeX{}, \OpTeX{}, \textsf{\mbox{memoir}}, \KOMAScript, and + \textsf{titlesec} section commands, so you don't need to patch these. + Any others, though, you'll need to patch yourself. + + \begin{tabularx}{\linewidth}{@{}Xl@{}} + \leavevmode\rlap{\plainop/} & \texttt{ }% + \cs{lwcdisablecmd\{\meta{\ttbs macro}\}} \\ + \LaTeX{} & \texttt{ }% + \cs{lwcsetup\{disablecmds=\{} \\ + & \hfill\texttt{\meta{csnameone}, + \meta{csnametwo}\}\}} \\ + \ConTeXt{} & + \cs{prependtoks\textbackslash{}lwc@patch@pre} \\ + & \hfill\cs{to\textbackslash{}everybefore\meta{hook}} \\ + & \cs{prependtoks\textbackslash{}lwc@patch@post} \\ + & \hfill\cs{to\textbackslash{}everyafter\meta{hook}} \\ + \end{tabularx} + + \subsection{\estretch/} + + \Lwc/ defaults to an \estretch/ value of 3~em for stretched paragraphs, + but you can configure this. + + \Lwc/ will only use the \estretch/ when it cannot lengthen a paragraph + in any other way, so it is fairly safe to set this to a large value. + \TeX{} accumulates badness when \estretch/ is + used~\cite{Knuth:TB10-3-325}, so it's pretty rare that a paragraph that + requires any \estretch/ will actually be used on the page. + + \begin{tabularx}{\linewidth}{@{}Xl@{}} + \leavevmode\rlap{\plainop/} & + \hskip2em\cs{lwcemergencystretch=} \\ + & \hfill\texttt{\meta{dimension}} \\ + \LaTeX{} & + \cs{lwcsetup\{emergencystretch=\hphantom{\}}}\\ + & \hfill\texttt{\meta{dimension}\}} \\ + \ConTeXt{} & + \cs{setuplwc[emergencystretch=\hphantom{]}} \\ + & \hfill\texttt{\meta{dimension}]} \\ + \end{tabularx} + + \subsection{Penalties} + + You can also manually adjust the penalties that \TeX{} assigns to \waos/. + Usually, the defaults are fine, but there are a few circumstances where you + may want to change them. + + \begin{tabular}{@{}p{.175\linewidth}@{}r@{\texttt{=\meta{integer}}}l@{}} + \leavevmode\rlap{\plainop/} & + \hskip6em\cs{widowpenalty} \\ + & \hskip6em\cs{clubpenalty} \\ + & \hskip6em\cs{brokenpenalty} \\ + \LaTeX{} & + \cs{lwcsetup\{ widowpenalty} & \texttt{\}} \\ + & \cs{lwcsetup\{orphanpenalty} & \texttt{\}} \\ + & \cs{lwcsetup\{brokenpenalty} & \texttt{\}} \\ + \leavevmode\hbox{\ConTeXt{}} & + \cs{setuplwc[ widowpenalty} & \texttt{]} \\ + & \cs{setuplwc[orphanpenalty} & \texttt{]} \\ + & \cs{setuplwc[brokenpenalty} & \texttt{]} \\ + \end{tabular} + + The value of these penalties determines how much \TeX{} should attempt + to stretch glue before passing the \woo/ to \lwc/. If you set the values to~1 + (default), \TeX{} will stretch nothing and immediately trigger \lwc/; + if you set the values to 10\,000, \TeX{} will stretch infinitely and + \lwc/ will never be triggered. If you set the value to some intermediate + number, \TeX{} will first attempt to stretch some glue to remove the \woo/; + only if it fails will \lwc/ come in and lengthen a paragraph. As a special + case, if you set the values to~0, both \TeX{} and \lwc/ will completely + ignore the \woo/. + + \subsection{\cs{nobreak} behaviour} + + When \lwc/ encounters an orphan, it removes it by moving the orphaned + line to the next page. The majority of the time, this is an appropriate + solution. However, if the orphan is immediately preceded by a section + heading (or \cs{nobreak}\slash\cs{penalty 10000}), \lwc/ would na\"ively separate a section heading from + the paragraph that follows. This is almost always undesirable, so \lwc/ + provides some options to configure this. + + \begin{tabularx}{\linewidth}{@{}Xr@{}} + \leavevmode\rlap{\plainop/} & + \cs{directlua\{lwc.}\hskip4em\null \\ + & \hfill\texttt{nobreak\_behaviour="\meta{value}"\}} \\ + \LaTeX{} & + \cs{lwcsetup\{nobreak=\meta{value}\}\hphantom{"}} \\ + \ConTeXt{} & + \cs{setuplwc[nobreak=\meta{value}]\hphantom{"}} \\ + \end{tabularx} + + The default value, \texttt{keep}, \emph{keep}s the section heading with + the orphan by moving both to the next page. The advantage to this option + is that it removes the orphan and retains any \cs{nobreak}s; the + disadvantage is that moving the section heading can create a large blank + space at the end of the page. + + The value \texttt{split} \emph{split}s up the section heading and the + orphan by moving the orphan to the next page while leaving the heading + behind. This is usually a bad idea, but exists for the sake of + flexibility. + + The value \texttt{warn} causes \lwc/ to give up on the page and do nothing, + leaving an orphaned line. \Lwc/ \emph{warn}s the user so that they can + manually remove the orphan. + + See figure~\ref{tab:nobreak} for a visual reference. + + \begin{figure} + \renewcommand{\arraystretch}{1} + \renewcommand{\doublerulesep}{0.5em} + \begin{tabularx}{\linewidth}{% + |X|@{\hskip\doublerulesep}|X|@{\hskip\doublerulesep}|X|% + } + \multicolumn1c{\ttfamily keep} & + \multicolumn1c{\ttfamily split} & + \multicolumn1c{\ttfamily warn} + \\ \hhline{-||-||-} + & + & + \textbf{Heading} \\ + & + \textbf{Heading} & + The\hfill first\hfill line + \\ \hhline{-||-||-} + \textbf{Heading} & + The\hfill first\hfill line & + text\hfill text\hfill text \\ + The\hfill first\hfill line & + text\hfill text\hfill text & + last line. \\ + text\hfill text\hfill text & + last line. & + % Nothing + \\ \hhline{-||-||-} + \end{tabularx} + \caption{A visual comparison of the \texttt{nobreak} option values.} + \label{tab:nobreak} + \end{figure} + + \subsection{Maximum cost} + + \Lwc/ ranks each paragraph on the page by how much it would \q{cost} to + lengthen that paragraph. By default, \lwc/ selects the paragraph on + the page with the lowest cost; however, you can configure it to only + select paragraphs below a selected cost. + + If there aren't any paragraphs below the set threshold, then \lwc/ won't + remove the \woo/ and will instead issue a warning. + + \begin{tabularx}{\linewidth}{@{}Xr@{\texttt{=\meta{integer}}}l@{}} + \leavevmode\rlap{\plainop/} & + \cs{lwcmaxcost} \\ + \LaTeX{} & + \cs{lwcsetup\{max-cost} & \texttt{\}} \\ + \leavevmode\hbox{\ConTeXt{}} & + \cs{setuplwc[maxcost} & \texttt{]} \\ + \end{tabularx} + + Based on my testing, \texttt{max-cost} values less than 1\,000 + cause completely imperceptible changes in interword spacing; values less + than 5\,000 are only noticeable if you are specifically trying to pick out the + expanded paragraph on the page; values less than 15\,000 are typically + acceptable; and larger values may become distracting. \Lwc/ defaults to an + infinite \texttt{max-cost}, although the \q{strict} and \q{balanced} modes + sets the values to~5\,000 and 10\,000 respectively. + + \section{Presets} + + As you can see, \lwc/ provides quite a few options. Luckily, there are a few + presets that you can use to set multiple options at once. These presets are + a good starting point for most documents, and you can always manually + override individual options. + + Currently, these presets are \LaTeX{}-only. + + \begin{tabular}{@{}rl@{}} + \LaTeX{} & + \cs{lwcsetup\{\meta{preset}\}} \\ + \end{tabular} + + \subsection{\texttt{default}} + + If you use \lwc/ without any options, it defaults to this preset. In default + mode, \lwc/ takes all possible measures to remove \waos/ and will not + attempt to stretch any vertical glue. This usually + removes~$\mathord{>}\,95\%$ of all + possible \waos/. The catch here is that this mode is quite aggressive, so + it often leaves behind some fairly \q{spacey} paragraphs. + + This mode is good if you want to remove (nearly) all \waos/ from your + document, without fine-tuning the results. + + \subsection{\texttt{strict}} + + \Lwc/ also offers a strict mode. This greatly restricts \lwc/'s tolerance + and makes it so that it will only lengthen paragraphs where the change will + be imperceptible. + + The caveat with strict mode is that\Dash depending on the document\Dash + \lwc/ will be able to remove less than a third of the \waos/. + For the \waos/ that can't be automatically removed, a warning will be + printed to your terminal and log file so that a human can manually fix the + situation. + + This mode is good if you want the best possible typesetting and are willing + to do some manual editing. + + \subsection{\texttt{balanced}} + + Balanced mode sits somewhere between default mode and strict mode. This mode + first lets \TeX{} stretch a little glue to remove the \woo/; only if that + fails will it then trigger \lwc/. Even then, the maximum paragraph cost is + capped. Here, \lwc/ can usually remove 90\% of a document's + potential \waos/, and it does so while making a minimal visual impact. + + This mode is recommended for most users who care about their document's + typography. This mode is not the default since it doesn't remove all + \waos/: it + still requires a little manual intervention. + + \begin{table} + \caption{\Lwc/ options set by each mode.}\label{tab:modes} + \ttfamily\setlength{\tabcolsep}{4pt} + \begin{tabular}{l*3r}\toprule + \textrm{Option} & default & balanced & strict \\ \midrule + max-cost & $\infty$ & 10000 & 5000 \\ + \rlap{emergencystretch} & 3em & 1em & 0pt \\ + nobreak & keep & keep & warn \\ + widowpenalty & 1 & 500 & 1 \\ + orphanpenalty & 1 & 500 & 1 \\ + brokenpenalty & 1 & 500 & 1 \\ + \bottomrule\end{tabular} + \end{table} + + \section{Compatibility} + + The \lwc/ implementation is almost entirely in Lua, with only a minimal + \TeX{} footprint. It + doesn't modify the output routine, inserts\slash floats, \cs{everypar}, and + it doesn't insert any whatsits. This means that it should be compatible with + nearly any \TeX{} package, class, and format. Most changes that \lwc/ makes + are not observable on the \TeX{} side. + + However, on the Lua side, \lwc/ modifies much of a page's internal + structure. + This should not affect any \TeX{} code; however, it may surprise + Lua code that modifies or depends on the page's low-level structure. This + does not matter for Plain~\TeX{} or \LaTeX{}, where even most Lua-based + packages don't depend on the node list structure; nevertheless, there are + a few issues with \ConTeXt{}. + + Simple \ConTeXt{} documents tend to be fine, but many advanced + \ConTeXt{} features rely heavily on Lua and can thus be disturbed by + \lwc/. This is not a huge issue\Dash the \lwc/ manual is + written in \ConTeXt{}\Dash but \lwc/ is inevitably more reliable + with Plain \TeX{} and \LaTeX{} than with \ConTeXt{}. + + Finally, keep in mind that adding \lwc/ to a document will almost certainly + change its page break locations. + + \subsection{Formats} + + \Lwc/ runs on all known \LuaTeX{}-based formats: Plain~\LuaTeX{}, + \LuaLaTeX{}, \CMkIV{}, \ConTeXt{} Mk\acro{XL}\slash\acro{LMTX}, + and~\OpTeX{}. Unless otherwise documented, all features should work + equally well in all formats. + + \subsection{Columns} + + Since \TeX{} and the formats implement column breaking and page + breaking through the + same internal mechanisms, \lwc/ removes \waos/ between columns just + as it does with \waos/ between pages. + + \Lwc/ is known to work with the \LaTeX{} class option \verb|twocolumn| + and the two-column output routine from Chapter~23 of \cite{texbook}. + + \subsection{Performance} + + \Lwc/ runs entirely in a single pass, without depending on any + \verb|.aux| files or the like. Thus, it shouldn't meaningfully + increase compile times. Although \lwc/ internally breaks each paragraph + twice, modern computers break paragraphs near-instantaneously, so you + are not likely to notice any slowdown. + + \subsection{\eTeX{} penalties} + + Knuth's original \TeX{} has three basic line penalties: + \cs{interlinepenalty}, which + is inserted between all lines; \cs{club\-penalty}, which is inserted after + the first line; and \cs{widow\-penalty}, which is inserted before the last + line. The \eTeX{} extensions~\cite{etex} generalize these commands with a + syntax similar to \cs{parshape}: with \cs{widow\-penalties} you can set the + penalty between the last, second last, and $n$th last lines of a paragraph; + \cs{inter\-line\-penalties} and \cs{club\-penalties} behave similarly. + + \Lwc/ makes no explicit attempts to support these new -\texttt{penalties} + commands. Specifically, If you give a line a penalty that matches either + \cs{widowpenalty} or \cs{clubpenalty}, \lwc/ will treat the lines + exactly as it would a \woo/. So while these commands won't break \lwc/, they + are likely to lead to some unexpected behaviour. + + \section{Short last lines} + + \looseness=1 + When lengthening a paragraph with \lsness/, it is common advice to insert + ties (\verb|~|) between the last few words of the paragraph to avoid + overly-short last lines \cite{texbook}. \Lwc/ does this automatically, + but instead of using ties or \cs{hbox}es, it uses the + \cs{par\allowbreak fill\allowbreak skip} + parameter~\cite{texbook, Wermuth:2018:ECP}. When lengthening a paragraph + (and only when lengthening a paragraph\Dash remember, \lwc/ doesn't + interfere with \TeX{}'s output unless it detects a \woo/), \lwc/ sets + \cs{parfillskip} to \verb|0pt plus 0.8\hsize|. + This normally makes the last line of a paragraph be at least + 20\% of the overall paragraph's width, thus preventing + ultra-short~lines. + + \section{How it works} + + \Lwc/ uses a fairly simple algorithm to eliminate \waos/, but there + are a few subtleties. + + \subsection{Setup} + + \Lwc/ sets the parameters \cs{clubpenalty}, \cs{widowpenalty}, and + \cs{brokenpenalty} to sentinel values of~1. This will signal to \lwc/ when + a \woo/ occurs, yet it is small enough that it won't stretch any glue. + + \Lwc/ also enables \LuaTeX{}'s micro\-typographic + extensions~\cite{xThanh:2000:MTE}. This isn't strictly necessary; + however, it significantly increases the number of paragraphs that can + be acceptably \q{loosened}. + + That is all that happens on the \TeX{} end. The rest of \lwc/ is pure Lua. + + \subsection{Paragraph breaking} + + First, \lwc/ hooks into the paragraph breaking process, before any output + routines or page breaking. + + Before a paragraph is broken by \TeX{}, \lwc/ grabs the unbroken + paragraph. Then \lwc/ breaks the paragraph one line longer than its natural + length and stores it for later. It does this in the background, + \emph{without} interfering with how \TeX{} breaks paragraphs into their + natural length. + + After \TeX{} has broken its paragraph into its natural length, \lwc/ + appears again. Before the broken paragraph is added to the main + vertical list, \lwc/ \q{tags} the first and last nodes of the paragraph + using a \LuaTeX{} attribute. These attributes associate the + previously-saved lengthened paragraph with the naturally-typeset + paragraph on the page. + + \subsection{Page breaking} + + \Lwc/ intercepts \cs{box255} (the \cs{vbox} output by \TeX) immediately + before the output routine runs, + after all the paragraphs have been typeset. + + First, \lwc/ looks at the \openalty/ of the page or column. If the page + was broken at a \woo/, the \openalty/ will be equal to either + the \cs{widowpenalty} or the \cs{clubpenalty}. If the \openalty/ does not + indicate a \woo/, \lwc/ will stop and return \cs{box255} unmodified to + the output~routine, and \TeX{} continues as normal. + + Otherwise, we assume that we have a \woo/ on the page, + meaning that we should lengthen the page by 1~line. We iterate through + the list of saved paragraphs to find the lengthened paragraph with the + least cost. After we've selected a good paragraph, we traverse + through the page to find the original version of this paragraph\Dash the + one that unmodified \TeX{} originally typeset. Having found the original + paragraph, we splice in the lengthened paragraph in place of the original. + + Since the page is now 1~line longer than it was before, we pull the last + line off the page to bring it back to its original length, and place + that line onto the top of \TeX's \q{recent contributions} list. When + the next page begins, this line will be inserted before all other + paragraphs, right at the top. Now, we can return the new, widow-free page + (updated \cs{box255}) to the output routine, which proceeds + as normal. + + \section{Choosing the \q{best} paragraph} + + As we discussed previously, \lwc/ lengthens the paragraph with the lowest + cost. However, assigning a cost to each paragraph is not quite as simple as + it sounds. Before we look at how \lwc/ assigns costs, let's look at how + \TeX{} scores paragraphs when breaking them naturally. + + \subsection{How \TeX{} scores paragraphs} + + All glue in \TeX{} has a certain natural size: the size that it would be + in an ideal scenario. However, most glue also has stretch and shrink + components so that the glue can change in size to adapt to its + surroundings. For each line, \TeX{} individually sums the total + stretch/shrink for the line and the stretch/shrink that was actually used. + We define the stretch/shrink ratio~$r$ as the quotient of the + stretch/shrink used and the stretch/shrink available. Then the badness~$b$ + of a line is approximately defined as + \begin{equation*} + b = 100r^3. + \end{equation*} + This is the badness referenced in the commonly-seen + \texttt{Underfull \cs{hbox} + (badness 1234)} warnings that \TeX{} produces. + + \TeX{} calculates the badness for each line individually; however, we also + need to assess the paragraph as a whole. To do so, \TeX{} defines the + demerits for a whole paragraph~$d$ as approximately\footnotemark{} the sum of + the squared badnesses for each line. The natural paragraph that \TeX{} + breaks is the one that minimizes~$d$. + + \footnotetext{We ignore any additional demerits or penalties that + \TeX{} may add.} + + One important thing to realize is that demerits grow incredibly fast: + demerits are proportional to the \emph{sixth} power of glue stretch. This + means that you can expect to see extremely large demerit values, even for + a relatively \q{good} paragraph. + + \subsection{Possible cost functions} + + Now, let's return to how \lwc/ assigns costs to each paragraph. This is + surprisingly more complicated than it sounds, so we'll go through a few + possible cost functions first. + + Here, we use $c$~for the cost of a paragraph, $d$~for the total demerits, + and $l$~for the number of lines (\cs{prevgraf}). + + \subsubsection{The original implementation} + + The original implementation of \lwc/ used the very simple cost function + \begin{equation*} + c = d. + \end{equation*} + This cost function works reasonably well, but has one major issue: it doesn't + take into account the number of lines in the paragraph. The demerits for a + paragraph is the sum of the demerits for each line. This means this cost + function will prefer using shorter paragraphs since they tend to have fewer + demerits. However, long paragraphs tend to have much more available glue + stretch, so this strategy can lead to suboptimal solutions. + + \subsubsection{Scaling by the number of lines} + + Once I realized this issue, I tried correcting it by dividing by the number + of lines in the paragraph to get the average demerits instead of the total + demerits: + \begin{equation*} + c = \frac{d}{l} + \end{equation*} + This works better than the previous function, but still has an issue. + If we have a fairly bad ten-line paragraph with total demerits $10d$ and an + almost-equally bad two-line paragraph with total demerits $2d + 1$, then by + this cost function, the ten-line paragraph will have a lower cost and will + be chosen. This means that our page now has ten bad lines instead of two bad + lines, which is not ideal. + + \subsubsection{Current implementation} + + Our first cost function, $c=dl^0$, doesn't consider the number of lines at + all, while our second cost function, $c=dl^{-1}$, considers the number of + lines too much. Splitting the difference between the two functions, we get + the current implementation: + \begin{equation*} + c = \frac{d}{\sqrt{l}} + \end{equation*} + + I didn't arrive at this function through any sort of scientific testing; + rather, I picked the simplest function that I could think of that satisfies + the following two properties: + \begin{itemize} + \item Given a long paragraph and a short paragraph with different + average badnesses per line, prefer the one with the least average + badness. + \item Given two paragraphs with equal average badnesses per line, + prefer the shorter one. + \end{itemize} + + \section{Quantitative analysis} + + \begin{figure} + \begin{tikzpicture}\begin{axis}[ + ybar interval, + xticklabels={1, ..., 15, + {$\,\ge\! 16$}}, + x tick label style={font=\small}, + y tick label style={font=\small}, + enlarge y limits=upper, + enlarge x limits={abs=1}, + grid=none, + scaled y ticks=base 10:-3, + ytick scale label code/.code={}, + xlabel={Paragraph length (lines)}, + ylabel={Count (thousands)} + ] + \addplot+ [ + draw=black, + fill=black!10, + semithick, + ] table { + Length Count + 1 4429 + 2 3704 + 3 2045 + 4 1320 + 5 894 + 6 717 + 7 498 + 8 406 + 9 379 + 10 251 + 11 175 + 12 152 + 13 111 + 14 95 + 15 79 + 16 437 + 18 0 + }; + + \filldraw [fill=black!25, draw=black] (16, 0) rectangle (18, 437); + \end{axis}\end{tikzpicture} + \caption{Histogram of natural paragraph lengths in the sample text.} + \label{fig:hist} + \end{figure} + + Let's look at some statistics for \lwc/. For testing, I + downloaded the top~ten books on \textsl{Project Gutenberg},\footnotemark{} + converted them to \LaTeX{} using \textsf{pandoc}, concatenated them into a + single \textsf{article} file, and compiled twice. This gives us a \acro{PDF} + with 1\,381~pages, 15\,692~paragraphs, 61\,865~lines, and 399~\waos/ + (if they aren't removed). + \footnotetext{\textsl{Frankenstein}, + \textsl{Pride and Prejudice}, + \textsl{Alice's Adventures in Wonderland}, + \textsl{The Great Gatsby}, + \textsl{The Adventures of Sherlock Holmes}, + \textsl{Simple Sabotage Field Manual}, + \textsl{A Tale of Two Cities}, + \textsl{The Picture of Dorian Gray}, + \textsl{Moby Dick}, + and \textsl{A Doll's House}. + } + + This is a fairly challenging test: almost every third page has a \woo/, over + half of the paragraphs have two lines or fewer, and the text block is set to + the fairly wide \textsf{article} defaults. An average document is + much less challenging for \lwc/, so we can consider this to be a + worst-case scenario. + + \subsection{Widows and orphans removed} + + \begin{figure} + \begin{tikzpicture}\begin{axis}[ + ybar=0pt, + bar width=0.8, + xtick=data, + ylabel={Widows and orphans removed}, + width=\linewidth, + height=0.8\linewidth, + xticklabels={ + \shortstack[c]{\hfill Maximum\\\hfill possible}, + \textsf{lwc} \texttt{default}, + \texttt{balanced}, + \LaTeX{}, + \texttt{strict}, + }, + x tick label style={ + font=\small, + rotate=45, + anchor=east, + }, + enlarge x limits=0.2, + ] + \addplot+ [ + draw=black, + fill=black!10, + semithick, + ] table [x expr=\coordindex, y index=0] { + 399 + 392 + 348 + 179 + 52 + }; + + \filldraw [fill=black!25, draw=black] + (-0.4, 0) rectangle (0.4, 399); + \end{axis}\end{tikzpicture} + \divide\abovecaptionskip by 2 + \caption{The number of \waos/ removed by each method.}\label{fig:modes} + \end{figure} + + When we run \LaTeX{} with its default settings on the file, 179~(47\%) of + the \waos/ are removed. When we add \lwc/ with default settings, we remove + 392~(98\%). Switching to strict mode, we can only remove 52~(13\%) of the + \waos/. In balanced mode, we remove 348~(87\%). See figure~\ref{fig:modes} + for a visual comparison. + + \subsection{Paragraph costs} + + \begin{figure} + \begin{tikzpicture}\begin{axis}[ + width=\linewidth, + height=0.8\linewidth, + xlabel={Percentile}, + ylabel={Cost}, + ymode=log, + legend entries={Natural, Long}, + legend pos=north west, + cycle list={ + {black, thick}, + {black!35, thick}, + }, + ] + \addplot+ table [x=Percentile, y=Long] {\plotdata}; + \addplot+ table [x=Percentile, y=Natural] {\plotdata}; + + \end{axis}\end{tikzpicture} + \divide\abovecaptionskip by 2 + \caption{Paragraph costs by percentile rank for naturally-broken and + one-line lengthened paragraphs.}\label{fig:costs} + \end{figure} + + The last section showed us that \lwc/ is quite effective at removing \waos/, + so now let's look at the paragraphs that \lwc/ expands. As \TeX{} + processes a document, \lwc/ is recording the costs for the naturally-broken + and expanded versions of each paragraph in the document. Costs don't + mean that much on their own, but a lower cost is always better. + + As you can see in figure~\ref{fig:costs}, the lengthened paragraphs tend to + have \emph{much} higher costs than the naturally-broken paragraphs. This + is not surprising, since (as we've seen) a paragraph's demerits + scale with the sixth + power of glue stretch, so even a small amount of glue stretch can cause a + huge increase in demerits. + + The empty space on the left of the \q{long} line is from the paragraphs + that \lwc/ was unable to lengthen at any cost. \LuaTeX{} assigns these + paragraphs zero~demerits, so they disappear on a logarithmic plot. + + \subsection{Lengthening vs.\ shortening paragraphs} + \begin{figure} + \begin{tikzpicture}\begin{axis}[ + xbar stacked, + height=0.2\linewidth, + width=\dimexpr\linewidth-1em, + scale only axis, + bar width=1, + enlargelimits=false, + xmin=0, + ymin=-0.5, + ymax=1, + ymajorticks=false, + xtick style={draw=none}, + xlabel={Paragraphs (thousands)}, + scaled x ticks=base 10:-3, + xtick scale label code/.code={}, + legend style={at={(0.5,1)}, anchor=north}, + legend columns=5, + legend cell align=left, + legend style={ + /tikz/every even column/.append style={column sep=1em}, + draw=none, + fill=none, + }, + legend entries={ + {$n=1$}, + {$n$}, + {$n+1$}, + {$n\pm1$}, + {$n-1$} + }, + ] + \addplot [fill=black!10 ] coordinates {(4429, 0)}; % One + \addplot [fill=white ] coordinates {(4474, 0)}; % None + \addplot [pattern=north east lines] coordinates {(5457, 0)}; % Long + \addplot [pattern=crosshatch ] coordinates {( 482, 0)}; % Both + \addplot [pattern=north west lines] coordinates {( 850, 0)}; % Short + \end{axis}\end{tikzpicture} + \divide\abovecaptionskip by 2 + \caption{The number of paragraphs in the test sample that + (respectively) have exactly + one line, cannot be stretched or shrunk, can be only stretched + by one~line, can be either stretched or shrunk, and can be + only shrunk.} + \label{fig:stretchshrink} + \end{figure} + + Figure~\ref{fig:stretchshrink} shows the number of paragraphs that \lwc/ + could potentially stretch or shrink. The one-line paragraphs are broken out + separately since this test sample has an anomalous number of them. + Otherwise, we can see that \lwc/ is capable of stretching the majority of + paragraphs. + + We can also see that of non-single-line paragraphs, only about 8\% + of paragraphs can only be shrunk (the last segment of + figure~\ref{fig:stretchshrink}), and this is in a document where 13\% + of paragraphs have at least eight~lines. Most documents rarely have + such long paragraphs, and it is these long paragraphs that are the + easiest to shrink. + + Because of this, \lwc/ doesn't even attempt to shrink paragraphs; it + only stretches them. + + \section{Known issues} + + \Lwc/ is quite stable these days, a few issues remain: + + \begin{itemize} + \item When a three-line paragraph is at the end of a page forming a + widow, \lwc/ will remove the widow; however, it will leave an orphan. + This issue is inherent to any process that removes widows through + paragraph expansion and is thus unavoidable. Orphans are considered + to be better than widows~\cite{elements}, so this is still an + improvement. + + \item Sometimes a \woo/ cannot be eliminated because no paragraph has + enough stretch. Sometimes this can be remediated by + increasing \lwc/'s \estretch/; however, some pages just don't have + any suitable paragraph. + + Long paragraphs with short words tend to be stretchier than short + paragraphs with long words since these long paragraphs have more + interword glue. Narrow columns also stretch more easily than wide columns + since you need to expand a paragraph by less to make a new line. + + \item When running under \LuaMetaTeX{} (\ConTeXt{}), the log may + contain many lines like \q{\texttt{% + \spaceskip=\fontdimen2\font plus1.25pt minus1.25pt + luatex warning > tex: left parfill skip is gone}}. These messages + are completely harmless (although admittedly quite annoying). + + \item \TeX{} may warn about overfull \cs{vbox}es on pages where + \lwc/ removed a \woo/. This happens due to the way that \lwc/ + corrects for the \cs{prevdepth} when replacing paragraphs. It + does not actually produce an overfull \texttt{vbox}, but there + is a warning nevertheless. You can set \cs{vfuzz=2.5pt} to + hide the + warning. + + \item \Lwc/ only attempts to expand paragraphs on a page with + a \woo/. A global system like in~\cite{global} would solve this; + however, this is both \acro{NP}-complete~\cite{plass} and + impossible to solve in a single pass. Very rarely would such a + system remove \woos/ that \lwc/ cannot. + \end{itemize} + + \section{Conclusion} + + All this probably makes \lwc/ look quite complicated, and this is true to + some extent. However, this complexity is hidden from the end~user: + as stated at the outset, most + users merely need to place \latexuse/ in their \LaTeX{} document + preamble, and \lwc/ will remove all the troublesome \waos/, without needing + any manual intervention. + + Should you have any issues, questions, or suggestions for \lwc/, please + visit the project's GitHub page: + \href{https://github.com/gucci-on-fleek/lua-widow-control} + {\tt github.com/gucci-on-fleek/lua-widow-control}. + Any feedback is greatly appreciated! + + \bibliographystyle{tugboat} + \AddToHook{env/thebibliography/begin}{% + \let\eTeX=\goodeTeX% + \let\tubeTeX=\goodeTeX% + } + \let\macro=\cs +\SetBibJustification{\raggedright \advance\itemsep by 1pt plus1pt minus1pt +\def\url{\tbsurl} +} +\smallskip + \bibliography{\jobname.bib, tugboat.bib} + + \makesignature +\end{document} diff --git a/docs/lwc-documentation.bib b/docs/manual/lwc-manual.bib similarity index 100% rename from docs/lwc-documentation.bib rename to docs/manual/lwc-manual.bib diff --git a/docs/lwc-documentation.mkxl b/docs/manual/lwc-manual.mkxl similarity index 99% rename from docs/lwc-documentation.mkxl rename to docs/manual/lwc-manual.mkxl index 8a0a0fe..8109a53 100644 --- a/docs/lwc-documentation.mkxl +++ b/docs/manual/lwc-manual.mkxl @@ -3,7 +3,7 @@ % SPDX-License-Identifier: MPL-2.0+ % SPDX-FileCopyrightText: 2022 Max Chernoff -\startenvironment[lwc-documentation] +\startenvironment[lwc-manual] \unprotect \mainlanguage[en] diff --git a/docs/lwc-documentation.tex b/docs/manual/lwc-manual.tex similarity index 99% rename from docs/lwc-documentation.tex rename to docs/manual/lwc-manual.tex index 23b13c0..63e46e6 100644 --- a/docs/lwc-documentation.tex +++ b/docs/manual/lwc-manual.tex @@ -1,5 +1,3 @@ -%!TeX program = context - % lua-widow-control % https://github.com/gucci-on-fleek/lua-widow-control % SPDX-License-Identifier: MPL-2.0+ OR CC-BY-SA-4.0+ @@ -10,10 +8,10 @@ \errmessage{Fatal error, exiting.} } -\environment lwc-documentation +\environment lwc-manual \usemodule[lua-widow-control] -\usebtxdataset[lwc-documentation.bib] +\usebtxdataset[\jobname.bib] \useURL[projecturl][https://github.com/gucci-on-fleek/lua-widow-control] \useURL[download][https://github.com/gucci-on-fleek/lua-widow-control/releases/latest] @@ -518,6 +516,6 @@ \subsection{lua-widow-control.opm} \subsection{Demo from \in{Table}[tab:demo]} -\typeTEXfile{lwc-documentation-demo-text.tmp} +\typeTEXfile{\jobname-demo-text.tmp} \stopdocument diff --git a/docs/lwc-sample.tex b/docs/manual/lwc-sample.tex similarity index 95% rename from docs/lwc-sample.tex rename to docs/manual/lwc-sample.tex index 8df0599..192814a 100644 --- a/docs/lwc-sample.tex +++ b/docs/manual/lwc-sample.tex @@ -48,7 +48,7 @@ \startbuffer[shorten] \parskip=0pt - \input lwc-documentation-demo-text.tmp + \input lwc-manual-demo-text.tmp \stopbuffer \startbuffer[shorten-code] @@ -60,7 +60,7 @@ \startbuffer[stretch] \parskip=0pt plus 1fill - \input lwc-documentation-demo-text.tmp + \input lwc-manual-demo-text.tmp \stopbuffer \startbuffer[stretch-code] @@ -81,7 +81,7 @@ \setups[*default] - \input lwc-documentation-demo-text.tmp + \input lwc-manual-demo-text.tmp \stopbuffer \startbuffer[ignore-code] @@ -93,7 +93,7 @@ \startbuffer[lwc] \usemodule[lua-widow-control] - \input lwc-documentation-demo-text.tmp + \input lwc-manual-demo-text.tmp \stopbuffer \startbuffer[lwc-code] diff --git a/texmf/doc/luatex/lua-widow-control/lua-widow-control.pdf b/texmf/doc/luatex/lua-widow-control/lua-widow-control.pdf index daa5bf5..c6c9f0f 120000 --- a/texmf/doc/luatex/lua-widow-control/lua-widow-control.pdf +++ b/texmf/doc/luatex/lua-widow-control/lua-widow-control.pdf @@ -1 +1 @@ -../../../../docs/lwc-documentation.pdf \ No newline at end of file +../../../../docs/manual/lwc-manual.pdf \ No newline at end of file diff --git a/texmf/doc/luatex/lua-widow-control/tb133chernoff-widows.pdf b/texmf/doc/luatex/lua-widow-control/tb133chernoff-widows.pdf new file mode 120000 index 0000000..084ec66 --- /dev/null +++ b/texmf/doc/luatex/lua-widow-control/tb133chernoff-widows.pdf @@ -0,0 +1 @@ +../../../../docs/TUGboat/tb133chernoff-widows.pdf \ No newline at end of file diff --git a/texmf/source/luatex/lua-widow-control/lwc-documentation.bib b/texmf/source/luatex/lua-widow-control/lwc-documentation.bib deleted file mode 120000 index 14b133d..0000000 --- a/texmf/source/luatex/lua-widow-control/lwc-documentation.bib +++ /dev/null @@ -1 +0,0 @@ -../../../../docs/lwc-documentation.bib \ No newline at end of file diff --git a/texmf/source/luatex/lua-widow-control/lwc-documentation.mkxl b/texmf/source/luatex/lua-widow-control/lwc-documentation.mkxl deleted file mode 120000 index e7c31c9..0000000 --- a/texmf/source/luatex/lua-widow-control/lwc-documentation.mkxl +++ /dev/null @@ -1 +0,0 @@ -../../../../docs/lwc-documentation.mkxl \ No newline at end of file diff --git a/texmf/source/luatex/lua-widow-control/lwc-documentation.tex b/texmf/source/luatex/lua-widow-control/lwc-documentation.tex deleted file mode 120000 index a1287af..0000000 --- a/texmf/source/luatex/lua-widow-control/lwc-documentation.tex +++ /dev/null @@ -1 +0,0 @@ -../../../../docs/lwc-documentation.tex \ No newline at end of file diff --git a/texmf/source/luatex/lua-widow-control/lwc-manual.bib b/texmf/source/luatex/lua-widow-control/lwc-manual.bib new file mode 120000 index 0000000..96679c0 --- /dev/null +++ b/texmf/source/luatex/lua-widow-control/lwc-manual.bib @@ -0,0 +1 @@ +../../../../docs/manual/lwc-manual.bib \ No newline at end of file diff --git a/texmf/source/luatex/lua-widow-control/lwc-manual.mkxl b/texmf/source/luatex/lua-widow-control/lwc-manual.mkxl new file mode 120000 index 0000000..e9c6aad --- /dev/null +++ b/texmf/source/luatex/lua-widow-control/lwc-manual.mkxl @@ -0,0 +1 @@ +../../../../docs/manual/lwc-manual.mkxl \ No newline at end of file diff --git a/texmf/source/luatex/lua-widow-control/lwc-manual.tex b/texmf/source/luatex/lua-widow-control/lwc-manual.tex new file mode 120000 index 0000000..1be8693 --- /dev/null +++ b/texmf/source/luatex/lua-widow-control/lwc-manual.tex @@ -0,0 +1 @@ +../../../../docs/manual/lwc-manual.tex \ No newline at end of file diff --git a/texmf/source/luatex/lua-widow-control/lwc-sample.tex b/texmf/source/luatex/lua-widow-control/lwc-sample.tex index fbf178a..a6b1e2d 120000 --- a/texmf/source/luatex/lua-widow-control/lwc-sample.tex +++ b/texmf/source/luatex/lua-widow-control/lwc-sample.tex @@ -1 +1 @@ -../../../../docs/lwc-sample.tex \ No newline at end of file +../../../../docs/manual/lwc-sample.tex \ No newline at end of file diff --git a/texmf/source/luatex/lua-widow-control/tb133chernoff-widows-figure.ctx b/texmf/source/luatex/lua-widow-control/tb133chernoff-widows-figure.ctx new file mode 120000 index 0000000..7beacd0 --- /dev/null +++ b/texmf/source/luatex/lua-widow-control/tb133chernoff-widows-figure.ctx @@ -0,0 +1 @@ +../../../../docs/TUGboat/tb133chernoff-widows-figure.ctx \ No newline at end of file diff --git a/texmf/source/luatex/lua-widow-control/tb133chernoff-widows-plot.dat b/texmf/source/luatex/lua-widow-control/tb133chernoff-widows-plot.dat new file mode 120000 index 0000000..a409030 --- /dev/null +++ b/texmf/source/luatex/lua-widow-control/tb133chernoff-widows-plot.dat @@ -0,0 +1 @@ +../../../../docs/TUGboat/tb133chernoff-widows-plot.dat \ No newline at end of file diff --git a/texmf/source/luatex/lua-widow-control/tb133chernoff-widows.bib b/texmf/source/luatex/lua-widow-control/tb133chernoff-widows.bib new file mode 120000 index 0000000..aa00968 --- /dev/null +++ b/texmf/source/luatex/lua-widow-control/tb133chernoff-widows.bib @@ -0,0 +1 @@ +../../../../docs/TUGboat/tb133chernoff-widows.bib \ No newline at end of file diff --git a/texmf/source/luatex/lua-widow-control/tb133chernoff-widows.ltx b/texmf/source/luatex/lua-widow-control/tb133chernoff-widows.ltx new file mode 120000 index 0000000..99fd5e0 --- /dev/null +++ b/texmf/source/luatex/lua-widow-control/tb133chernoff-widows.ltx @@ -0,0 +1 @@ +../../../../docs/TUGboat/tb133chernoff-widows.ltx \ No newline at end of file