├── .gitignore
├── diff-word.cmd
├── docs
└── ui-screenshot.png
├── diff-word-wrapper.cmd
├── start-gui.cmd
├── .editorconfig
├── Folder.DotSettings
├── License.md
├── Diff-Word.ps1
├── CHANGELOG.md
├── Readme.md
└── Gui-Diff-Word.ps1
/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea/
2 |
--------------------------------------------------------------------------------
/diff-word.cmd:
--------------------------------------------------------------------------------
1 | @echo off
2 | powershell.exe -File "%~dpn0.ps1" %*
3 |
--------------------------------------------------------------------------------
/docs/ui-screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ForNeVeR/ExtDiff/HEAD/docs/ui-screenshot.png
--------------------------------------------------------------------------------
/diff-word-wrapper.cmd:
--------------------------------------------------------------------------------
1 | @echo off
2 | rem Pass old-file new-file to script
3 | %~dp0/diff-word.cmd %2 %5
4 |
--------------------------------------------------------------------------------
/start-gui.cmd:
--------------------------------------------------------------------------------
1 | @echo off
2 | cmd /c start /min "" powershell -WindowStyle Hidden -ExecutionPolicy Bypass -File "Gui-Diff-Word.ps1"
3 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 4
7 | trim_trailing_whitespace = true
8 | insert_final_newline = true
9 |
--------------------------------------------------------------------------------
/Folder.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | True
3 | True
--------------------------------------------------------------------------------
/License.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | =====================
3 |
4 | Copyright (C) 2024 by ExtDiff contributors
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy of
7 | this software and associated documentation files (the "Software"), to deal in
8 | the Software without restriction, including without limitation the rights to
9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
10 | the Software, and to permit persons to whom the Software is furnished to do so,
11 | subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Diff-Word.ps1:
--------------------------------------------------------------------------------
1 | param(
2 | [string] $BaseFileName,
3 | [string] $ChangedFileName
4 | )
5 |
6 | $ErrorActionPreference = 'Stop'
7 |
8 | function resolve($relativePath) {
9 | (Resolve-Path $relativePath).Path
10 | }
11 |
12 | $BaseFileName = resolve $BaseFileName
13 | $ChangedFileName = resolve $ChangedFileName
14 |
15 | # Remove the readonly attribute because Word is unable to compare readonly
16 | # files:
17 | $baseFile = Get-ChildItem $BaseFileName
18 | if ($baseFile.IsReadOnly) {
19 | $baseFile.IsReadOnly = $false
20 | }
21 |
22 | # Constants
23 | $wdDoNotSaveChanges = 0
24 | $wdCompareTargetNew = 2
25 |
26 | try {
27 | $word = New-Object -ComObject Word.Application
28 | $word.Visible = $true
29 | $document = $word.Documents.Open($BaseFileName, $false, $false)
30 | $document.Compare($ChangedFileName, [ref]"Comparison", [ref]$wdCompareTargetNew, [ref]$true, [ref]$true)
31 |
32 | $word.ActiveDocument.Saved = 1
33 |
34 | # Now close the document so only compare results window persists:
35 | $document.Close([ref]$wdDoNotSaveChanges)
36 | } catch {
37 | Add-Type -AssemblyName System.Windows.Forms
38 | [System.Windows.Forms.MessageBox]::Show($_.Exception)
39 | }
40 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | Changelog
2 | =========
3 | All notable changes to this project will be documented in this file.
4 |
5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## [1.4.0] - 2024-10-19
8 | ### Added
9 | - Support for dropping of multiple files into either file box. Thanks to @coor for the contribution.
10 |
11 | ### Changed
12 | - The `start-gui.cmd` script no longer waits for the GUI process termination. Thanks to @coor for the contribution.
13 |
14 | ## [1.3.0] - 2024-01-15
15 | ### Added
16 | - A new UI form implementation. Thanks to @devegied-lamabpo for the contribution.
17 |
18 | ## [1.2.0] - 2017-11-13
19 | ### Added
20 | - A new wrapper batch file, `diff-word-wrapper.cmd`, for more convenient usage with Git. Thanks to @jonmseaman for the contribution.
21 |
22 | ## [1.1.0] - 2017-09-12
23 | ### Added
24 | - A new proxy batch file, `diff-word.cmd`, for more convenient execution in certain environments. Thanks to @jtpereyda for the contribution.
25 |
26 | ## [1.0.1] - 2017-07-21
27 | ### Added
28 | - The file paths are now resolved before passing them to Microsoft Word. This allows passing of the PowerShell paths, e.g., using `~` to denote the home directory.
29 |
30 | ## [1.0.0] - 2016-08-23
31 | The initial project release. Includes the only PowerShell script that calls Microsoft Word to compare two documents.
32 |
33 | [1.0.0]: https://github.com/ForNeVeR/ExtDiff/releases/tag/1.0
34 | [1.0.1]: https://github.com/ForNeVeR/ExtDiff/compare/1.0...1.0.1
35 | [1.1.0]: https://github.com/ForNeVeR/ExtDiff/compare/1.0.1...1.1.0
36 | [1.2.0]: https://github.com/ForNeVeR/ExtDiff/compare/1.1.0...1.2.0
37 | [1.3.0]: https://github.com/ForNeVeR/ExtDiff/compare/1.2.0...v1.3.0
38 | [1.4.0]: https://github.com/ForNeVeR/ExtDiff/compare/v1.3.0...v1.4.0
39 | [Unreleased]: https://github.com/ForNeVeR/ExtDiff/compare/v1.4.0...HEAD
40 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | ExtDiff [![Status Aquana][status-aquana]][andivionian-status-classifier]
2 | =======
3 |
4 | This is a small command line script that will compare two files using Microsoft
5 | Word file comparison tool. Microsoft Word will be started using COM automation.
6 |
7 | It is useful as a diff tool for Word-related file types.
8 |
9 | ## Using via command line
10 |
11 | To run the script, execute it through PowerShell like this:
12 |
13 | ```console
14 | $ powershell -File Diff-Word.ps1 oldfile.docx newfile.docx
15 | ```
16 |
17 | Or via the batch file:
18 |
19 | ```console
20 | $ diff-word.cmd oldfile.docx newfile.docx
21 | ```
22 |
23 | ## Using the GUI form
24 |
25 | ![UI Form Screenshot][docs.ui-form]
26 |
27 | To run the GUI form, run the script `start-gui.cmd`: it will open a form that stays on top of all the system windows,
28 | and you can drag files from Windows Explorer to two text fields
29 | (first for the old document, and second for the revised document).
30 | When you click the **Compare** button, it will start Word application with chosen documents as in command line usage,
31 | and the form will lose the "stay on top of all windows" behavior.
32 | It will regain this property again after the button **Clear** is clicked.
33 |
34 | If you drop multiple files into either box, then the first element will be placed in the box where files are dropped, and the second one into the other box.
35 |
36 | ## Using via Git Integration
37 |
38 | You can also use this tool with git, so that `git diff` will use Microsoft Word
39 | to diff `*.docx` files.
40 |
41 | To do this, you must configure your `.gitattributes` and `.gitconfig` to support
42 | a custom diff tool.
43 |
44 | ### `.gitattributes`
45 |
46 | To configure your `.gitattributes`, open or create a file called
47 | `.gitattributes` in your git repo's root directory. Add the following text to a
48 | new line in this file:
49 |
50 | ```
51 | *.docx diff=word
52 | ```
53 |
54 | It is also possible to create a global `.gitattributes` file that will be
55 | applied to every repository in a system. To do that, create a file
56 | `.gitattributes` in your home directory, and then perform the following command:
57 |
58 | ```console
59 | git config --global core.attributesfile ~/.gitattributes
60 | ```
61 |
62 | ### `.gitconfig`
63 |
64 | To configure your `.gitconfig`, open or create the file in your home directory.
65 | Then, add the following to your `.gitconfig`:
66 |
67 | ```ini
68 | [diff "word"]
69 | command = /diff-word-wrapper.cmd
70 | ```
71 |
72 | Replace `` with the path to this repo's
73 | location on disk.
74 |
75 | -------
76 |
77 | Idea taken from [TortoiseSVN diff-doc script][tortoisesvn-diff-doc].
78 |
79 | Additional Documentation
80 | ------------------------
81 | - [License (MIT)][docs.license]
82 | - [Changelog][docs.changelog]
83 |
84 | [andivionian-status-classifier]: https://github.com/ForNeVeR/andivionian-status-classifier#status-aquana-
85 | [docs.changelog]: CHANGELOG.md
86 | [docs.license]: License.md
87 | [docs.ui-form]: docs/ui-screenshot.png
88 | [status-aquana]: https://img.shields.io/badge/status-aquana-yellowgreen.svg
89 | [tortoisesvn-diff-doc]: https://sourceforge.net/p/tortoisesvn/code/27268/tree/trunk/contrib/diff-scripts/diff-doc.js
90 |
--------------------------------------------------------------------------------
/Gui-Diff-Word.ps1:
--------------------------------------------------------------------------------
1 | $ErrorActionPreference = 'Stop'
2 | Add-Type -AssemblyName System.Windows.Forms
3 |
4 | $Form = New-Object System.Windows.Forms.Form
5 | $Form.ClientSize = New-Object System.Drawing.Point(595, 162)
6 | $Form.text = 'Compare MS Word Documents (drop files here)'
7 | #Make a form topmost window - good for drag and drop operations
8 | $Form.TopMost = $true
9 | $Form.FormBorderStyle = [System.Windows.Forms.BorderStyle]::FixedSingle
10 |
11 | $olddoc = New-Object System.Windows.Forms.TextBox
12 | $olddoc.multiline = $true
13 | $olddoc.width = 580
14 | $olddoc.height = 50
15 | $olddoc.location = New-Object System.Drawing.Point(6, 8)
16 | $olddoc.AllowDrop = $true
17 |
18 | $newdoc = New-Object System.Windows.Forms.TextBox
19 | $newdoc.multiline = $true
20 | $newdoc.width = 580
21 | $newdoc.height = 50
22 | $newdoc.location = New-Object System.Drawing.Point(6, 65)
23 | $newdoc.AllowDrop = $true
24 |
25 | $cmpbtn = New-Object System.Windows.Forms.Button
26 | $cmpbtn.text = "Compare"
27 | $cmpbtn.width = 446
28 | $cmpbtn.height = 30
29 | $cmpbtn.location = New-Object System.Drawing.Point(140, 126)
30 |
31 | $clrbtn = New-Object System.Windows.Forms.Button
32 | $clrbtn.text = "Clear"
33 | $clrbtn.width = 120
34 | $clrbtn.height = 30
35 | $clrbtn.location = New-Object System.Drawing.Point(6, 126)
36 |
37 | $Form.controls.AddRange(@($olddoc, $newdoc, $cmpbtn, $clrbtn))
38 |
39 | $olddoc.Add_DragDrop( { DnD $this $_ })
40 | $olddoc.Add_DragOver( { DnO $this $_ })
41 | $newdoc.Add_DragDrop( { DnD $this $_ })
42 | $newdoc.Add_DragOver( { DnO $this $_ })
43 | $cmpbtn.Add_Click( { DoCompare $this $_ })
44 | $clrbtn.Add_Click( { ClearInputs $this $_ })
45 |
46 | function DnO ($evSource, $evtArgs) {
47 | if ($evtArgs.Data.GetDataPresent([Windows.Forms.DataFormats]::FileDrop)) {
48 | $evtArgs.Effect = [Windows.Forms.DragDropEffects]::Copy
49 | }
50 | else {
51 | $evtArgs.Effect = [Windows.Forms.DragDropEffects]::None
52 | }
53 | $Form.ActiveControl = $cmpbtn
54 | }
55 | function DnD ($evSource, $evtArgs) {
56 |
57 | $files = $evtArgs.Data.GetData([Windows.Forms.DataFormats]::FileDrop)
58 | if ($files.Count -eq 1)
59 | {
60 | $evSource.text = $files[0]
61 | }
62 | elseif ($files.Count -gt 1)
63 | {
64 | if ($evSource -eq $olddoc)
65 | {
66 | $olddoc.text = $files[0]
67 | $newdoc.text = $files[1]
68 | }
69 | else
70 | {
71 | $olddoc.text = $files[1]
72 | $newdoc.text = $files[0]
73 | }
74 | }
75 | }
76 | function DoCompare ($evSource, $evArgs) {
77 | $Form.TopMost = $false
78 | $BaseFileName = $olddoc.text
79 | $ChangedFileName = $newdoc.text
80 | # Remove the readonly attribute because Word is unable to compare readonly
81 | # files:
82 | $baseFile = Get-ChildItem $BaseFileName
83 | if ($baseFile.IsReadOnly) {
84 | $baseFile.IsReadOnly = $false
85 | }
86 | # Constants
87 | $wdDoNotSaveChanges = 0
88 | $wdCompareTargetNew = 2
89 | try {
90 | $word = New-Object -ComObject Word.Application
91 | $word.Visible = $true
92 | $document = $word.Documents.Open($BaseFileName, $false, $false)
93 | $document.Compare($ChangedFileName, [ref]"Comparison", [ref]$wdCompareTargetNew, [ref]$true, [ref]$true)
94 |
95 | $word.ActiveDocument.Saved = 1
96 |
97 | # Now close the document so only compare results window persists:
98 | $document.Close([ref]$wdDoNotSaveChanges)
99 | }
100 | catch {
101 | [System.Windows.Forms.MessageBox]::Show($_.Exception)
102 | }
103 | }
104 | function ClearInputs ($evSource, $evtArgs) {
105 | $olddoc.text = ""
106 | $newdoc.text = ""
107 | $Form.TopMost = $true
108 | }
109 |
110 | [void]$Form.ShowDialog()
111 |
--------------------------------------------------------------------------------