1

I'm trying to write a script in Powershell to automatically print the contents of a text file. However, my tab characters are removed when printing making the final output hard to read.

I've provided an MWE to illustrate the issue.

Contents of MWE.txt (encoded in ANSI; a represents a tab character):

FOO→1,23→4,56

My powershell script:

$fname = "C:\MWE.txt"
Get-Content $fname | Out-Printer -name "Microsoft XPS Document Writer"

I'm using the XPS writer to avoid wasting paper, but in the end this will be changed to a real printer. The output on real paper is identical, I've ruled that out too.

Problem is that the in the print output, the tab characters are just erased, yielding:

FOO1,234,56

However, If I instead change the Out-Printer to Out-String, the console displays the file contents correctly. What on earth am I doing wrong? The file displays correctly with tabs in both notepad and notepad++, and I've tried UTF-8 encoding as well.

Edit 2018-07-12 10:20

Content structure of the file I'm trying to print, which is automatically generated by a system and put on a web server for which I have no access. For clarity, I've used to denote the tab character and · to denote a space:

MWE·-·TestFile·:·

0)→Time
1)→Temperature·1
2)→Humidity
3)→Pressure·A-B
4)→Pressure·A-C
5)→Pressure·D-C

0)···················→1)→2)→3)→4)→5)→
2018-07-04 12:00:00→22,03→5,34→18,93→23,10→10,10
2018-07-04 13:00:00→21,99→5,22→18,75→23,56→10,00
2018-07-04 14:00:00→21,99→5,17→18,55→23,54→10,15
2018-07-04 15:00:00→22,03→5,25→18,73→23,41→8,33
mklement0
  • 382,024
  • 64
  • 607
  • 775
chrillof
  • 325
  • 2
  • 15

2 Answers2

2

You're close, and you stumbled onto a workaround yourself - the use of Out-String. You can add Out-String to the pipeline before sending the content to the printer:

Get-Content $fname | Out-String | Out-Printer -Name "Microsoft XPS Document Writer"

If the above isn't working for you you can try processing the tab character yourself:

( Get-Content $fname -Raw ).Split( "`t" ) -Join "`t"

If that displays the output as you're expecting, you can pipe that to Out-Printer (Out-String isn't necessary because -Join concatenates into a string):

( Get-Content $fname -Raw ).Split( "`t" ) -Join "`t" |
  Out-Printer -Name "Microsoft XPS Document Writer"

However, this seems like it may be a bug with Out-Printer, as comments and other answers have yielded varying success rates. The workarounds above are known to work on at least Windows 10.0.15063.0 (the version I'm currently running and tested with), but may have stopped working in later versions.

You may try converting the tabs to spaces as a workaround:

# This is set to 5 spaces here, but can be any number of
# spaces you want to represent a tab character
$replaceTabString = "     "
( Get-Content $fname -Raw ).Replace( "`t", $replaceTabString ) |
  Out-Printer -Name "Microsoft XPS Document Writer"
codewario
  • 19,553
  • 20
  • 90
  • 159
  • 1
    Doesn't `Get-Content` just return an array of strings by default? – Maximilian Burszley Jul 10 '18 at 14:13
  • 1
    Sorry, this didn't work. I haven't tried piping `Out-String` per se, but I actually tried piping through the `echo` function. – chrillof Jul 10 '18 at 14:14
  • I literally just tested this and it solves the missing `tab` problem in the generated xps document. – codewario Jul 10 '18 at 14:15
  • 1
    Try `Get-Content $fname -Raw | Out-Printer -name "Microsoft XPS Document Writer"`. p.s. Are you sure the encoding is ANSI? `Get-Content` does have a `-Encoding` switch. You could try different encodings if you still don't see the tabs. – Theo Jul 10 '18 at 14:27
  • I added an alternative method to process the tabs yourself – codewario Jul 10 '18 at 14:27
  • @Theo I actually tried adding `-Raw` to `Get-Content`first and it yielded the same result, removal of the tabs when sent to `Out-Printer`. – codewario Jul 10 '18 at 14:28
  • My own experiments tell me that the problem is not that the tabs don't _reach_ the printer - `Get-Content` does output them - it's that the printer _removes_ them. The only thing `Out-String` does is to output the file content as a _single_ string (similar to `Get-Content -Raw`), but with an extra trailing newline. In short, your workarounds are ineffective, at least with the Microsoft XPS Document Writer printer that comes with Windows 10 Version 1709 (OS Build 16299.492) – mklement0 Jul 10 '18 at 23:45
  • Well, I was able to reproduce the original issue on Windows 10.0.15063.0 - piping the contents of the text file to `Out-Printer` (I ensured tabs were used) yielded the tabs stripped from the XPS document. First piping the file to `Out-String`, or the workaround by splitting on and re-adding the tabs fixed the issue on my system. – codewario Jul 10 '18 at 23:53
  • Not sure why the downvotes, this seems to be a bug with `Out-Printer` (OP stated the issue persists when printing to a real printer), where my workarounds above stopped working on the latest version of Windows 10, but is valid on earlier builds. – codewario Jul 11 '18 at 14:22
  • 2
    I've tried all of the above but with no success. Of course, replacing the tab characters with spaces works as long as all columns are identical in character width (which is not true in my case), otherwise the output will be misaligned which is not an option. However, this fortifies the belief that there is a bug in `Out-Printer` regarding the tab chacacter. – chrillof Jul 12 '18 at 08:00
  • I would recommend you file an issue on the [Github project](https://github.com/PowerShell/PowerShell). – codewario Jul 13 '18 at 13:58
1

I have no explanation - the symptom sounds like a bug (that also affects the Microsoft Print To PDF printer) and it seems to be specific to tab characters, whereas spaces work fine.

However, I can offer a workaround - assuming that the end goal is to create aligned output columns and that it isn't necessary to use tabs specifically to achieve that:

Get-Content "C:\MWE.txt" | ForEach-Object {
  # Construct a custom object whose properties contain the
  # tab-separated tokens on the input line.
  $obj = New-Object PSCustomObject; $i = 0
  foreach ($val in $_ -split "`t") {
    Add-Member -InputObject $obj -NotePropertyName ('col' + ++$i) -NotePropertyValue $val 
  }
  $obj
} | Format-Table -Property (1..6 | % { 'col' + $_ }) -HideTableHeaders | 
      Out-Printer -Name "Microsoft XPS Document Writer"
  • The ForEach-Object script block splits each input line into tokens by tabs and constructs a custom object with properties containing the individual tokens.

  • Format-Table creates table output with columns that use spaces for alignment; -HideTableHeaders suppresses output of a table header, which would normally display the (autogenerated) property names.

    • Note that since the input has lines with varying column counts, -FormatTable has to be instructed explicitly as to which columns to display, which is what (1..6 | % { 'col' + $_ }) does: it creates an array of all the auto-generated column names based on the assumption that at most 6 columns can be present, which is true of your sample input - adjust that number as needed. (To be safe, you may also specify a higher number, say, 20, but that will slow processing down.
  • Piping Format-Table's output to Out-Printer -Name "Microsoft XPS Document Writer" seems to result in the desired tabular output with aligned columns.

    • If column values end up getting truncated (indicated by ...), you need to tweak the Format-Table call, such as with custom column widths - see this answer of mine.

Running this against your sample input yields the following 6-column layout in the console, and the output XPS document renders similarly:

MWE·-·TestFile·:·                                                                                                                                                                   

0)                    Time                                                                                                                                                          
1)                    Temperature·1                                                                                                                                                 
2)                    Humidity                                                                                                                                                      
3)                    Pressure·A-B                                                                                                                                                  
4)                    Pressure·A-C                                                                                                                                                  
5)                    Pressure·D-C                                                                                                                                                  

0)··················· 1)            2)   3)    4)    5)                                                                                                                             
2018-07-04 12:00:00   22,03         5,34 18,93 23,10 10,10                                                                                                                          
2018-07-04 13:00:00   21,99         5,22 18,75 23,56 10,00                                                                                                                          
2018-07-04 14:00:00   21,99         5,17 18,55 23,54 10,15                                                                                                                          
2018-07-04 15:00:00   22,03         5,25 18,73 23,41 8,33                                                                                                                           
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 1
    @mkelemnt0: Thanks! I'll consider this the accepted answer as it provides a nice workaround for the issue. However, there is a caveat when the header explanations (top rows which keys the column numbers to names) are considerably long (and using the `-AutoSize` option to `Format-Table`). The reason is that the tabular data do not actually start from row one, but is processed as such. I do not deem it necessary to put more effort into this, as I hope the supposed bug in `Out-Printer` would be fixed in coming versions. - Again, thanks for all help! – chrillof Jul 18 '18 at 08:22
  • @noffe: Thanks, and good point about needing to handle the legend differently. On a side note, there is an actual bug in `Format-Table` related to `-AutoSize`: https://github.com/PowerShell/PowerShell/issues/7309 – mklement0 Jul 18 '18 at 13:58
  • @noffe: Also, I've reported the original bug (tabs getting stripped) here: https://windowsserver.uservoice.com/forums/301869-powershell/suggestions/34863562-out-printer-appears-to-mishandle-tab-characters – mklement0 Jul 18 '18 at 14:55