Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Powershell to Print a Folder of Text files to PDF (Retaining the Original Base name)

First time posting - but I think this is a good one as I've spent 2 days researching, talked with local experts, and still haven't found this done.

Individual print jobs must be regularly initiated on a large set of files (.txt files), and this must be converted through the print job to a local file (i.e. through a PDF printer) which retains the original base name for each file. Further, the script must be highly portable.

The objective will not be met if the file is simply converted (and not printed), the original base file name is not retained, or the print process requires manual interaction at each print.

After my research, this is what stands so far in PowerShell:

PROBLEM: This script does everything but actually print the contents of the file. It iterates through the files, and "prints" a .pdf while retaining the original file name base; but the .pdf is empty.

I know I'm missing something critical (i.e. maybe a stream use?); but after searching and searching have not been able to find it. Any help is greatly appreciated.

As mentioned in the code, the heart of the print function is gathered from this post:

# The heart of this script (ConvertTo-PDF) is largley taken and slightly modified from https://social.technet.microsoft.com/Forums/ie/en-US/04ddfe8c-a07f-4d9b-afd6-04b147f59e28/automating-printing-to-pdf?forum=winserverpowershell
# The $OutputFolder variable can be disregarded at the moment. It is an added bonus, and a work in progress, but not cirital to the objective.
function ConvertTo-PDF {
    param(
        $TextDocumentPath, $OutputFolder
    )

      Write-Host "TextDocumentPath = $TextDocumentPath"
      Write-Host "OutputFolder = $OutputFolder"
    Add-Type -AssemblyName System.Drawing
    $doc = New-Object System.Drawing.Printing.PrintDocument
    $doc.DocumentName = $TextDocumentPath
    $doc.PrinterSettings = new-Object System.Drawing.Printing.PrinterSettings
    $doc.PrinterSettings.PrinterName = 'Microsoft Print to PDF'
    $doc.PrinterSettings.PrintToFile = $true
    $file=[io.fileinfo]$TextDocumentPath
      Write-Host "file = $file"
    $pdf= [io.path]::Combine($file.DirectoryName, $file.BaseName) + '.pdf'
      Write-Host "pdf = $pdf"
    $doc.PrinterSettings.PrintFileName = $pdf
    $doc.Print()
      Write-Host "Attempted Print: $pdf" 
    $doc.Dispose()
}

# get the relative path of the TestFiles and OutpufFolder folders.

$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
  Write-Host "scriptPath = $scriptPath"
$TestFileFolder = "$scriptPath\TestFiles\"
  Write-Host "TestFileFolder = $TestFileFolder"
$OutputFolder = "$scriptPath\OutputFolder\"
  Write-Host "OutputFolder = $OutputFolder"

# initialize the files variable with content of the TestFiles folder (relative to the script location).

$files = Get-ChildItem -Path $TestFileFolder


# Send each test file to the print job
foreach ($testFile in $files)
{
            $testFile = "$TestFileFolder$testFile"
              Write-Host "Attempting Print from: $testFile" 
              Write-Host "Attemtping Print to  : $OutputFolder"
            ConvertTo-PDF $testFile $OutputFolder
}
like image 696
FunkMaster Avatar asked Apr 13 '18 22:04

FunkMaster


1 Answers

You are missing a handler that reads the text file and passes the text to the printer. It is defined as a scriptblock like this:

$PrintPageHandler =
{
    param([object]$sender, [System.Drawing.Printing.PrintPageEventArgs]$ev)

    # More code here - see below for details
}

and is added to the PrintDocument object like this:

$doc.add_PrintPage($PrintPageHandler)

The full code that you need is below:

$PrintPageHandler =
{
    param([object]$sender, [System.Drawing.Printing.PrintPageEventArgs]$ev)

    $linesPerPage = 0
    $yPos = 0
    $count = 0
    $leftMargin = $ev.MarginBounds.Left
    $topMargin = $ev.MarginBounds.Top
    $line = $null

    $printFont = New-Object System.Drawing.Font "Arial", 10

    # Calculate the number of lines per page.
    $linesPerPage = $ev.MarginBounds.Height / $printFont.GetHeight($ev.Graphics)

    # Print each line of the file.
    while ($count -lt $linesPerPage -and (($line = $streamToPrint.ReadLine()) -ne $null))
    {
        $yPos = $topMargin + ($count * $printFont.GetHeight($ev.Graphics))
        $ev.Graphics.DrawString($line, $printFont, [System.Drawing.Brushes]::Black, $leftMargin, $yPos, (New-Object System.Drawing.StringFormat))
        $count++
    }

    # If more lines exist, print another page.
    if ($line -ne $null) 
    {
        $ev.HasMorePages = $true
    }
    else
    {
        $ev.HasMorePages = $false
    }
}

function Out-Pdf
{
    param($InputDocument, $OutputFolder)

    Add-Type -AssemblyName System.Drawing

    $doc = New-Object System.Drawing.Printing.PrintDocument
    $doc.DocumentName = $InputDocument.FullName
    $doc.PrinterSettings = New-Object System.Drawing.Printing.PrinterSettings
    $doc.PrinterSettings.PrinterName = 'Microsoft Print to PDF'
    $doc.PrinterSettings.PrintToFile = $true

    $streamToPrint = New-Object System.IO.StreamReader $InputDocument.FullName

    $doc.add_PrintPage($PrintPageHandler)

    $doc.PrinterSettings.PrintFileName = "$($InputDocument.DirectoryName)\$($InputDocument.BaseName).pdf"
    $doc.Print()

    $streamToPrint.Close()
}

Get-Childitem -Path "$PSScriptRoot\TextFiles" -File -Filter "*.txt" |
    ForEach-Object { Out-Pdf $_ $_.Directory }

Incidentally, this is based on the official Microsoft C# example here:

PrintDocumentClass

like image 52
boxdog Avatar answered Nov 15 '22 05:11

boxdog