From 856ff1f6f5b3f90f70a7026ffa5076cc55a120f5 Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Tue, 28 Dec 2021 22:52:08 +0000 Subject: latexdiff (28dec21) git-svn-id: svn://tug.org/texlive/trunk@61434 c570f23f-e606-0410-a88d-b1316a301751 --- .../texmf-dist/scripts/latexdiff/latexdiff-vc.pl | 71 +++++++- Master/texmf-dist/scripts/latexdiff/latexdiff.pl | 191 +++++++++++++++------ Master/texmf-dist/scripts/latexdiff/latexrevise.pl | 44 ++++- 3 files changed, 239 insertions(+), 67 deletions(-) (limited to 'Master/texmf-dist/scripts') diff --git a/Master/texmf-dist/scripts/latexdiff/latexdiff-vc.pl b/Master/texmf-dist/scripts/latexdiff/latexdiff-vc.pl index b50a52722ff..849ad1de21e 100755 --- a/Master/texmf-dist/scripts/latexdiff/latexdiff-vc.pl +++ b/Master/texmf-dist/scripts/latexdiff/latexdiff-vc.pl @@ -26,6 +26,13 @@ # Detailed usage information at the end of the file # # TODO/IDEAS: - option to call external pre-processing codes +# version 1.3.2: +# - bug fix: when setting config variables with the command lines it is now possible to use quotes to includes spaces in the value, e.g. "-c LATEX=pdflatex --shell-escape' +# - bug fix: --only-changes is not compatible with graphics markup. --graphics-markup=none is now set automatically +# - when --pdf or --postscript is select, then replace tildes in version names with underscores in output file (e.g. diffHEAD_1 instead of diff HEAD~1. This is necessary because pdflatex strips ~n strings at the end of the filename, when generating output file names + +# version 1.3.1: +# - bug fix, import File::Path explicitly as otherwise mkpath is not found # version 1.3.0 ((7 October 2018) # - option --only-changes with hyperref will suppress hyperrefs (pull request jprotze)_ # - option --only-changes now moves (rather than copies) file with only changes @@ -66,13 +73,14 @@ use Pod::Usage qw/pod2usage/ ; use File::Temp qw/tempdir/ ; use File::Basename qw/dirname/; use File::Copy; +use File::Path; use strict ; use warnings ; my $versionstring=< choose directory name ($dir=$append) =~ s/^-//; @@ -532,7 +547,7 @@ foreach $diff ( @difffiles ) { #print STDERR "Generated postscript file $ps\n"; } elsif ( $run ) { if ( $onlychanges ) { - my @pages=findchangedpages("$diffbase.aux"); + my @pages=compresspages(findchangedpages("$diffbase.aux")); my $gs = `which gs`; $gs =~ s/^\s+|\s+$//g; my $qpdf = `which qpdf`; @@ -540,7 +555,7 @@ foreach $diff ( @difffiles ) { my $pdftk = `which pdftk`; $pdftk =~ s/^\s+|\s+$//g; my $command; - if (-x $gs && `gs --version` >= 9.20) { + if (-x $gs && `$gs --version` >= 9.20) { $command="gs -sDEVICE=pdfwrite -dNOPAUSE -dBATCH -dSAFER -sPageList=" . join(",", @pages) . " -sOutputFile=\"$diffbase-changedpage.pdf\" \"$diffbase.pdf\""; } elsif (-x $pdftk) { $command="pdftk \"$diffbase.pdf\" cat " . join(" ",@pages) . " output \"$diffbase-changedpage.pdf\""; @@ -588,13 +603,52 @@ sub findchangedpages { return(sort {$a <=> $b} keys(%pages)); } +# inspired by a python version posted at https://stackoverflow.com/a/54714846/8136338 +# compresspages(@pages) +# @pages: sorted array of unique integers (pages) +# return a list of pages and page ranges. 3 or more consecutive numbers are merged into a "begin-end" string +# +# example: join(",", compresspages([1,3,4,5,7,8])) -> join(",", [1,"3-5",7,8]) -> "1,3-5,7,8" +sub compresspages { + my (@pages) = @_; + my @res; + my $begin=$pages[0]; + my $end =$pages[0]; + my $page; + foreach $page ( @pages ) { + next if ($page == $begin); + if ($page == $end+1) { # handle continuous pages + $end = $page; + next; + } elsif ($begin == $end) { # push single continuous page + push @res, $begin; + $begin = $end = $page; + } elsif ($begin + 1 == $end) { # push two continuous pages + push @res, $begin, $end; + $begin = $end = $page; + } else { # push multiple continuous pages + push @res, "$begin-$end"; + $begin = $end = $page; + } + } + # Finally just check in the same manner for the end element + if ($begin == $end) { + push @res, $begin; + } elsif ($begin + 1 == $end) { + push @res, $begin, $end; + } else { + push @res, "$begin-$end"; + } + return @res; +} + # checkout_dir(rev,dirname) # checks out revision rev and stores it in dirname # uses global variables: $vc, $rootdir sub checkout_dir { my ($rev,$dirname)=@_; - unless (-e $dirname) { mkdir $dirname or die "Cannot mkdir $dirname ." ;} + unless (-e $dirname) { mkpath([ $dirname ]) or die "Cannot mkdir $dirname ." ;} if ( $vc eq "SVN" ) { system("svn checkout -r $rev $rootdir $dirname")==0 or die "Something went wrong in executing: svn checkout -r $rev $rootdir $dirname"; } elsif ( $vc eq "GIT" ) { @@ -717,7 +771,8 @@ With C<--flatten=keep-intermediate>, the intermediate revision snapshots are kep Post-process the output such that only pages with changes on them are displayed. This requires the use of subtype ZLABEL in latexdiff, which will be set automatically, but any manually set -s option will be overruled (also requires zref package to -be installed). (note that this option must be combined with --ps or --pdf to make sense) +be installed). This option also disables internal links (as implemented by hyperref package) and graphics markup. +(note that this option must be combined with --ps or --pdf to make sense) =item B<--force> diff --git a/Master/texmf-dist/scripts/latexdiff/latexdiff.pl b/Master/texmf-dist/scripts/latexdiff/latexdiff.pl index 45e5188bcc7..6cba4316e3c 100755 --- a/Master/texmf-dist/scripts/latexdiff/latexdiff.pl +++ b/Master/texmf-dist/scripts/latexdiff/latexdiff.pl @@ -22,7 +22,23 @@ # # Detailed usage information at the end of the file # - +# +# Version 1.3.2 +# API adaptions: +# - latexdiff now completes with exit code 0 after --help or --version command (see issue #248) +# New features / feature extensions +# - extend CUSTOMDIFCMD related postprocessing to deal properly with multiline commands, or a sequence of several commands in the same line (see github issue #204) +# - Support for additional macros from import package (\import, \inputfrom, \includefrom, \subimport,\subinputfrom, \subincludefrom). Provided by janniklasrose in PR #243 (fixes #239) +# - replace default driver dvips->pdftex +# Bug fixes: +# - fix issue #206 affecting proper markup of text commands which are not safe cmd's at the same time and have multiple arguments +# - fix issue #210 by adding \eqref (amsmath package) to the list of safe commands +# - fix bug reported in issue #168 mangled verbatim line environment +# - fix bug reported in issue #218 by replacing \hspace{0pt} after \mbox{..} auxiliary commands with \hskip0pt. +# - more ways to process \frac correctly with atomic arguments (committed by julianuu PR #246 +# - fix a bug in biblatex mode, which prevented proper processing of modified \textcite (see: https://tex.stackexchange.com/questions/555157/latexdiff-and-biblatex-citation-commands) +# - -h string fix: add -driver option +# # Version 1.3.1.1 # - remove spurious \n to fix error: Unknown regexp modifier "/n" at .../latexdiff line 1974, near "=~ " (see github issue #201) # @@ -660,8 +676,8 @@ my ($algodiffversion)=split(/ /,$Algorithm::Diff::VERSION); my ($versionstring)=<|\['.$brat_n.'\]|\{'. $pat_n . '\}|\(' . $coords .'\))'.$extraspace.')*'; +# standard $cmdoptseq (default: no intrevening spaces, controlled by extraspcae) - a final open parentheses is merged to the commend if it exists to deal properly with multi-argument text command + my $cmdoptseq='\\\\[\w\d@\*]+'.$extraspace.'(?:(?:<'.$abrat0.'>|\['.$brat_n.'\]|\{'. $pat_n . '\}|\(' . $coords .'\))'.$extraspace.')*\{?'; +# Handle tex \def macro: \def\MAKRONAME#1[#2]#3{DEFINITION} my $defseq='\\\\def\\\\[\w\d@\*]+(?:#\d+|\[#\d+\])+(?:\{'. $pat_n . '\})?'; my $backslashnl='\\\\\n'; my $oneletcmd='\\\\.\*?(?:\['.$brat_n.'\]|\{'. $pat_n . '\})*'; @@ -1410,6 +1429,9 @@ $ulem = ($latexdiffpreamble =~ /\\RequirePackage(?:\[$brat_n\])?\{ulem\}/ || def # If listings is being used or can be found in the latexdiff search path, add to the preamble auxiliary code to enable line-by-line markup if ( defined($packages{"listings"}) or `kpsewhich listings.sty` ne "" ) { my @listingpreamble=extrapream("LISTINGS"); + if ($latexdiffpreamble =~ /\\RequirePackage(?:\[$brat_n\])?\{color\}/ ) { + @listingpreamble=extrapream("COLORLISTINGS"); + } my @listingDIFcode=(); my $replaced; # note that in case user supplies preamblefile the type might not reflect well the @@ -1572,6 +1594,7 @@ if ( defined $packages{"apacite"} ) { } elsif (defined $packages{"biblatex"}) { print STDERR "biblatex package detected.\n" if $verbose ; $citpat='(?:[cC]ites?|(?:[pP]aren|foot|[Tt]ext|[sS]mart|super)cites?\*?|footnotecitetex)'; + push(@TEXTCMDEXCL, qr/^textcite$/); } else { # citation command pattern for all other citation schemes $citpat='(?:cite\w*|nocite)'; @@ -1848,6 +1871,9 @@ sub add_safe_commands { my $should_be_safe = $2; print STDERR "DEBUG Checking new command: maybe_to_test, should_be_safe: $1 $2\n" if $debug; my $success = 0; + # skip custom diff commands + next if ($maybe_to_test =~ m/^(?:ADD|DEL)?${CUSTOMDIFCMD}$/); + ###print STDERR "DEBUG: really test it. \n"; # test if all latex commands inside it are safe $success = 1; if ($should_be_safe =~ m/\\\\/) { @@ -1890,6 +1916,7 @@ sub remove_endinput { # expands \input and \include commands within text # expands \bibliography command with corresponding bbl file if available # expands \subfile command (from subfiles package - not part of standard text distribution) +# expands \import etc commands (from import package - not part of standard text distribution) # preamble is scanned for includeonly commands # encoding is the encoding sub flatten { @@ -1917,15 +1944,16 @@ sub flatten { # Run through filter, to let filterscript have a pass if it was set $text = filter($text); - # Recursively replace \\import and \\subimport files - $text =~ s/(^(?:[^%\n]|\\%)*)(\\subimport\{(.*?)\}|\\import\{(.*?)\})(?:[\s]*)\{(.*?)\}/{ + # Recursively replace \\import, \\subimport, and related import commands + $text =~ s/(^(?:[^%\n]|\\%)*)(\\(sub)?(?:import|inputfrom|includefrom))\{(.*?)\}(?:[\s]*)\{(.*?)\}/{ + # (--------1-------)(--(=3=)-------------2-------------------) (-4-) (-5-) # $1 is begline - # $3 is directory if subimport - # $4 is directory if import + # $2 is the import macro name + # $3 is (optional) prefix "sub" + # $4 is directory # $5 is filename $begline = (defined($1)? $1 : ""); - $subdir = $3 if defined($3); - $subdir = $4 if defined($4); + $subdir = $4; $fname = $5; $fname .= ".tex" unless $fname =~ m|\.\w{3,4}$|; print STDERR "DEBUG begline:", $begline, "\n" if $debug; @@ -1940,12 +1968,13 @@ sub flatten { print STDERR "importing importfilepath:", $importfilepath,"\n" if $verbose; if ( -f $importfilepath ) { # If file exists, replace input or include command with expanded input + #TODO: need remove_endinput & newpage similar to other replacements inside flatten $replacement=flatten(read_file_with_encoding($importfilepath, $encoding), $preamble,$importfilepath,$encoding) or die "Could not open file ",$fullfile,": $!"; } else { # if file does not exist, do not expand include or input command (do not warn if fname contains #[0-9] as it is then likely part of a command definition # and is not meant to be expanded directly print STDERR "WARNING: Could not find included file ",$importfilepath,". I will continue but not expand |$2|\n"; - $replacement=(defined($3)? "\\subimport" : "\\import"); + $replacement = $2; $replacement .= "{$subdir}{$fname} % Processed"; } "$begline$replacement"; @@ -2615,9 +2644,10 @@ sub marktags { # split this block to split sequences joined in pass1 + ### print STDERR "DEBUG: marktags before splitlatex blocksplit ",join("|",@$block),"\n" if $debug; @$block=splitlatex(join "",@$block); ### print STDERR "DEBUG: marktags $openmark,$closemark,$open,$close,$opencmd,$closecmd,$comment\n" if $debug; - ### print STDERR "DEBUG: marktags blocksplit ",join("|",@$block),"\n" if $debug; + print STDERR "DEBUG: after splitlatex ",join("|",@$block),"\n" if $debug; # we redefine locally $extraspace (shadowing the global definition) to capture command sequences with intervening spaces no matter what the global setting # this is done so we can capture those commands with a predefined number of arguments without having to introduce them again explicitly here @@ -2673,6 +2703,7 @@ sub marktags { # $2 (the command) is in context2, just treat it as an ordinary command (i.e. comment it open with $opencmd) # Because we do not want to disable this command # here we do not use $opencmd and $closecmd($opencmd is empty) + print STDERR "DEBUG: Detected text |$word| but not safe command \$2: $2 \$3: $3\n." if $debug; if ($cmd==1) { push (@$retval,$closecmd) ; } elsif ($cmd==0) { @@ -2731,7 +2762,7 @@ sub marktags { if ( $word =~ /^\\(?!\()(\\|[`'^"~=.]|[\w*@]+)(.*?)(\s*)$/s && iscmd($1,\@MBOXCMDLIST,\@MBOXCMDEXCL)) { # $word is a safe command in MBOXCMDLIST ###print STDERR "DEBUG Mboxsafecmd detected:$word:\n" if $debug ; - push(@$retval,"\\mbox{$AUXCMD\n\\" . $1 . $2 . $3 ."}\\hspace{0pt}$AUXCMD\n" ); + push(@$retval,"\\mbox{$AUXCMD\n\\" . $1 . $2 . $3 ."}\\hskip0pt$AUXCMD\n" ); } else { # $word is a normal word or a safe command (not in MBOXCMDLIST) push (@$retval,$word); @@ -2751,22 +2782,22 @@ sub take_comments_and_newline_from_frac() { # some special magic for common usage of frac, which does not conform to the latexdiff requirements but can be made to fit # note that this is a rare exception to the general rule that the new tex can be reconstructed from the diff file - # \frac12 -> \frac{1}{2} - s/\\frac(\d)(\w)/\\frac\{$1\}\{$2\}/g; - - # \frac1{2b} -> \frac{1}{2b} - s/\\frac(\d)/\\frac\{$1\}/g; - - # delete space and comment characters between \frac arguments -# s/\\frac(?:\s*?%[^\n]*?)*?(\{$pat_n\})\s*(\{$pat_n\})/\\frac$1$2/g; - s/\\frac(?:\s|%[^\n]*?)*(\{$pat_n\})(?:\s|%[^\n]*?)*(\{$pat_n\})/\\frac$1$2/g; + # regex that matches space and comment characters + my $space = qr/\s|%[^\n]*?/; + # \frac {abc} -> \frac{abc} + # \frac1 -> \frac{1} + # \frac a -> \frac{a} + # \frac \lambda -> \frac{\lambda} + s/\\frac(?|${space}+\{($pat_n)\}|${space}*(\d)|${space}+(\w)|${space}*(\\[a-zA-Z]+))/\\frac\{$1\}/g; + # same as above for the second argument of frac + s/\\frac(\{$pat_n\})(?|${space}*\{($pat_n)\}|${space}*(\d)|${space}+(\w)|${space}*(\\[a-zA-Z]+))/\\frac$1\{$2\}/g; } # preprocess($string, ..) # carry out the following pre-processing steps for all arguments: # 1. Remove leading white-space # Change \{ to \QLEFTBRACE and \} to \QRIGHTBRACE and \& to \AMPERSAND -# #. Change {,} in comments to \CLEFTBRACE, \CRIGHTBRACE +# #. Change {,},\frac in comments to \CLEFTBRACE, \CRIGHTBRACE, \CFRAC # 2. mark all first empty line (in block of several) with \PAR tokens # 3. Convert all '\%' into '\PERCENTAGE ' and all '\$' into \DOLLAR to make parsing regular expressions easier # 4. Convert all \verb|some verbatim text| commands (where | can be an arbitrary character) @@ -2802,9 +2833,11 @@ sub preprocess { s/(? to file latexdiff.debug.