Лучший отвечающий

Help with printing to PDF

Вопрос

I have an automated process that generates various reports for security audits in various file formats (depending on the source of the data). I'm trying to automate printing these reports to PDF as this is the format required by the security team and
the auditors.

Here's my ConvertTo-PDF function, which I adopted from a blog post from a few years ago.
Both the unmodified code and my updated function just print a blank 1KB PDF file. I feel like there is a missing step in here somewhere. Any help would be appreciated.

Function ConvertTo-PDF{
<#
.NOTES
Created by Adam Gloyd on 6/12/2019
Adapted from a TechNet blog post here: https://social.technet.microsoft.com/Forums/ie/en-US/04ddfe8c-a07f-4d9b-afd6-04b147f59e28/automating-printing-to-pdf
.SYNOPSIS
Converts a specified file to a PDF using the Microsoft Print to PDF file printer.
.DESCRIPTION
Uses the built-in Microsoft Print to PDF file printer to convert virtually any file to a PDF.
Be aware that if the source file is wider than the standard portrait document, the resulting PDF may not be properly formatted.
Currently, the function automatically saves the PDF in the same folder as the source document with the same name, simply updating the file extension to 'PDF.'
.Parameter File
The source file that needs to be converted to a PDF.
.Example
ConvertTo-PDF -File "C:\Test\MyWordDoc.docx"
This command will print the specified word document to a PDF called MyWordDoc.pdf stored in the 'C:\Test' directory.
.LINK
https://social.technet.microsoft.com/Forums/ie/en-US/04ddfe8c-a07f-4d9b-afd6-04b147f59e28/automating-printing-to-pdf
.INPUTS
System.String
.OUTPUTS
None
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true,Position=0)]
[String]$File
)
# Import System.Drawing namespace; testing whether the module definition can load this assembly instead of placing it inside the function.
Add-Type -AssemblyName System.Drawing
# Create a new PrintDocument object
$ObjDoc = New-Object System.Drawing.Printing.PrintDocument
# Set the document display name to the specified document path
$ObjDoc.DocumentName = $File
# Add a PrinterSettings object to the existing print document object
$ObjDoc.PrinterSettings = new-Object System.Drawing.Printing.PrinterSettings
# Specify the name of the printer (in this case, the 'Microsoft Print to PDF' file printer)
$ObjDoc.PrinterSettings.PrinterName = 'Microsoft Print to PDF'
# Specify that we are printing to a file and not to a physical printer
$ObjDoc.PrinterSettings.PrintToFile = $true
# We need to set the name and path of the destination pdf file, which we will base on the name and location of the source document.
$source = Get-Item -Path $File # Create an object containing information about the source file
$pdf = Join-Path -Path ($source.DirectoryName) -ChildPath ($source.BaseName + '.pdf') # Create a new object with an updated path
# The pdf will be stored in the same location as the base file
# Update the destination PDF file name/path based on previous section
$ObjDoc.PrinterSettings.PrintFileName = $pdf
$ObjDoc.Print() # Print the document (creating the PDF file)
$ObjDoc.Dispose() # Release all related resources so the garbage collector can release associated memory.
}

Ответы

Sorry for the slow reply. I rethought my approach based on a blog post that I found (btw, I couldn't get your first post to run). I now have a function that works (depending on source file type), which is part of a module that should be on
GitHub soon.

Function ConvertTo-PDF{
<#
.NOTES
2019/06/17: Created by Adam Gloyd; inspired by code found on the Idera community website. Link listed in the links section of help.
2019/06/18: Added debug output
2019/06/18: Used debug output to fix the last couple of symantic errors
2019/06/18: Added help documentation
.SYNOPSIS
Converts the specified file to a PDF file.
.DESCRIPTION
Converts the specified file to a PDF file by printing it using the Microsoft Print to PDF driver. The new file will be in same path as the original and keep the
original name, just with a new .pdf extension. Only certain file types are supported at this time as each file type needs to be handled differently, usually by
an application designed to manage the file type.
The following file types are currently supported and are treated as raw text:
.txt
.log
.csv
.xml
The following file types will have added support in the future by using the appropriate application to handle the content and send it to the printer:
.html
.xhtml
.doc
.docx
.xls
.xlsx
Csv files are currently handled via Import-Csv, so they are not printed in a tabular format.
In the future, an option will be added to treat CSVs as an Excel document and print them using Excel.
Another option in the future may be added to take PowerShell output via the pipeline and print it to a PDF.
.EXAMPLE
ConnvertTo-PDF -File C:\Test\Test.txt
Prints the text contained within Test.txt to a PDF file: C:\Test\Test.pdf
.EXAMPLE
Get-ADGroupMember -Identity 'Domain Admins' | Out-File C:\Test\DomainAdmins.txt
ConvertTo-PDF -File C:\Test\DomainAdmins.txt
Exports a list of all of the accounts in the domain admins group and saves it to a text file.
It then imports the data and prints it to a new PDF file called: C:\Test\DomainAdmins.pdf
.LINK
Get-Content
Get-Item
Import-Csv
Import-CliXml
New-PDFPrinterPort
New-PDFUnattendPrinter
Out-Printer
Remove-PDFPrinterPort
Remove-Printer
https://community.idera.com/database-tools/powershell/powertips/b/tips/posts/sending-powershell-results-to-pdf-part-2
.INPUTS
System.String[]
.OUTPUTS
PDF File
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true,Position=0)]
[String[]]$File
)
Begin{
# Get source file information
Write-Debug "ConvertTo-PDF: Starting 'Begin' block."
$Item = Get-Item $File
Write-Debug "ConvertTo-PDF: Setting variable `$Item to the file located at: '$File'"
Write-Debug ("ConvertTo-PDF: `$Item parent directory: "+ $Item.DirectoryName)
Write-Debug ("ConvertTo-PDF: `$Item file name: " + $Item.Name)
Write-Debug ("ConvertTo-PDF: `$Item full name: " + $Item.FullName)
Write-Debug ("ConvertTo-PDF: `$Item base name: " + $Item.BaseName)
Write-Debug ("ConvertTo-PDF: `$Item file extension: " + $Item.Extension)
# Create the necessary printer that will be used to print the specified file unattended.
Write-Verbose "Creting a temporary printer and printer port for unattended printing."
Write-Debug "ConvertTo-PDF: Setting variable `$PDFPrinter to string value 'UnattendPDFPrinter'"
$PDFPrinter = "UnattendPDFPrinter"
$PDFPath = $Item.DirectoryName + '\' + $Item.BaseName + '.pdf'
Write-Debug "ConvertTo-PDF: Set variable `$PDFPath to '$PDFPath'"
Write-Debug "ConvertTo-PDF: The destination PDF file will be '$PDFPath'"
Write-Debug "ConvertTo-PDF: Creating new printer port called '$PDFPath'"
New-PDFPrinterPort -FilePath $PDFPath
Write-Debug "ConvertTo-PDF: Creating new printer called '$PDFPrinter'; it will use the port called '$PDFPath'"
New-PDFUnattendPrinter -PrinterName $PDFPrinter -PortName $PDFPath
# Rationalize file information and extensions
Write-Verbose "Checking file type to determine method for conversion."
Write-Debug "ConvertTo-PDF: Creating variable `$FileType and setting to an empty string."
[String]$FileType = ""
Write-Debug ("ConvertTo-PDF: Processing switch statement for `$Item.Extension; current value: " + $Item.Extension)
Switch($Item.Extension){
'.txt' {$FileType = "Text"; Write-Debug "File type was .txt; set `$FileType to '$FileType'"}
'.log' {$FileType = "Text"; Write-Debug "File type was .log; set `$FileType to '$FileType'"}
'.html' {$FileType = "HTML"; Write-Debug "File type was .html; set `$FileType to '$FileType'"}
'.xhtml' {$FileType = "HTML"; Write-Debug "File type was .xhtml; set `$FileType to '$FileType'"}
'.csv' {$FileType = "CSV"; Write-Debug "File type was .csv; set `$FileType to '$FileType'"}
'.xml' {$FileType = "XML"; Write-Debug "File type was .xml; set `$FileType to '$FileType'"}
'.xls' {$FileType = "Excel"; Write-Debug "File type was .xls; set `$FileType to '$FileType'"}
'.xlsx' {$FileType = "Excel"; Write-Debug "File type was .xlsx; set `$FileType to '$FileType'"}
'.doc' {$FileType = "Word"; Write-Debug "File type was .doc; set `$FileType to '$FileType'"}
'.docx' {$FileType = "Word"; Write-Debug "File type was .docx; set `$FileType to '$FileType'"}
}
Write-Debug "ConvertTo-PDF: Ending 'Begin' block."
}
Process{
Write-Debug "ConvertTo-PDF: Starting 'Process' block."
# Based on file extension, import the data and send it to the temporary printer
Write-Debug "ConvertTo-PDF: Using If statements to import content based on the value of `$FileType: $FileType."
If($FileType -eq "Text"){
# The file should be treated as raw text; use Get-Content
Write-Verbose "The file is a type of text file. Importing data with Get-Content and sending to PDF printer."
Write-Debug "ConvertTo-PDF: `$FileType matched string 'Text'; importing source file with Get-Content."
Get-Content $File | Out-Printer -Name $PDFPrinter
}ElseIf($FileType -eq "HTML"){
# The file is an HTML document; use Edge to open and print the file
Write-Debug "ConvertTo-PDF: `$FileType matched string 'HTML'; file type not supported at this time."
Write-Warning "Unsupported file type. Support for the file type of the provided file will be added soon."
}ElseIf($FileType -eq "CSV"){
# The file contains comma delimited values; use Import-Csv
Write-Verbose "File is a CSV. Using Import-Csv to retrieve the data and print raw CSV entries into target PDF file."
Write-Verbose "Support will be added in the future to treat a Csv as an Excel document. If Csv should printed in a table, convert to Excel and try again."
Write-Debug "ConvertTo-PDF: `$FileType matched string 'CSV'; Using Import-Csv to import content. This means the data will be exported in raw text."
Import-Csv $File | Out-Printer -Name $PDFPrinter
}ElseIf($FileType -eq "XML"){
# The file contains XML data; use Import-CliXml
Write-Verbose "File is an XML document; attempting to retrieve data with Import-CliXml and printing raw data to target PDF file."
Try{
Write-Debug "ConvertTo-PDF: `$FileType matched string 'XML'; Using Import-CliXml to import content. This means the data will be exported in raw text."
Import-Clixml $File -ErrorAction SilentlyContinue | Out-Printer -Name $PDFPrinter
}Catch{
Write-Verbose "Failed to import the XML document. It may not be in a supported schema."
Write-Debug "ConvertTo-PDF: failed to import the XML document; it may not be in the correct schema for use with Import-CliXml; processing the XML document as text using Get-Content"
Get-Content $File -ErrorAction SilentlyContinue | Out-Printer -Name $PDFPrinter
}
}ElseIf($FileType -eq "Excel"){
# The file is an Excel workbook; use the Excel application to open and print the workbook
Write-Debug "ConvertTo-PDF: `$FileType matched string 'Excel'; file type not supported at this time."
Write-Warning "Unsupported file type. Support for the file type of the provided file will be added soon."
}ElseIf($FileType -eq "Word"){
# The file is a Word document; use the Word appliction to open and print the the document
Write-Debug "ConvertTo-PDF: `$FileType matched string 'Word'; file type not supported at this time."
Write-Warning "Unsupported file type. Support for the file type of the provided file will be added soon."
}Else{
# The file type is currently unsupported
Write-Debug "ConvertTo-PDF: `$FileType value unmatched; file type not supported at this time."
Write-Error "Unsupported file type. The file type of the provided file is not surrently supported by this converter."
}
Write-Debug "ConvertTo-PDF: Ending 'Process' block."
}
End{
Write-Debug "ConvertTo-PDF: Starting 'End' block."
# Do any post-run cleanup processes
Write-verbose "Removing temporary PDF printer."
Write-Debug "ConvertTo-PDF: Running 'Remove-Printer -Name $PDFPrinter -Confirm:`$false' to delete the temporary printer."
Remove-Printer -Name $PDFPrinter -Confirm:$false
Write-Verbose "Removing temporary PDF printer port."
Write-Debug "ConvertTo-PDF: Running 'Remove-PDFPrinterPort -FilePath $PDFPath' to delete the temporary printer port."
Remove-PDFPrinterPort -FilePath $PDFPath
Write-Debug "ConvertTo-PDF: Closing 'End' block."
}
}

Все ответы

Simple. You have to write the text into the document. What you have is a blank document with a name but no contents. Get the graphics object and use the System.Drawing API to write the text into the document.

Here is a full example of how to render a text file to a document being printed. THe output is sent one page at a time until there is no more to print.

Here is the C# example that can be run under PowerShell as a quick demo. CHange the two file names in the code to your file names and test. You will see how it works:

Sorry for the slow reply. I rethought my approach based on a blog post that I found (btw, I couldn't get your first post to run). I now have a function that works (depending on source file type), which is part of a module that should be on
GitHub soon.

Function ConvertTo-PDF{
<#
.NOTES
2019/06/17: Created by Adam Gloyd; inspired by code found on the Idera community website. Link listed in the links section of help.
2019/06/18: Added debug output
2019/06/18: Used debug output to fix the last couple of symantic errors
2019/06/18: Added help documentation
.SYNOPSIS
Converts the specified file to a PDF file.
.DESCRIPTION
Converts the specified file to a PDF file by printing it using the Microsoft Print to PDF driver. The new file will be in same path as the original and keep the
original name, just with a new .pdf extension. Only certain file types are supported at this time as each file type needs to be handled differently, usually by
an application designed to manage the file type.
The following file types are currently supported and are treated as raw text:
.txt
.log
.csv
.xml
The following file types will have added support in the future by using the appropriate application to handle the content and send it to the printer:
.html
.xhtml
.doc
.docx
.xls
.xlsx
Csv files are currently handled via Import-Csv, so they are not printed in a tabular format.
In the future, an option will be added to treat CSVs as an Excel document and print them using Excel.
Another option in the future may be added to take PowerShell output via the pipeline and print it to a PDF.
.EXAMPLE
ConnvertTo-PDF -File C:\Test\Test.txt
Prints the text contained within Test.txt to a PDF file: C:\Test\Test.pdf
.EXAMPLE
Get-ADGroupMember -Identity 'Domain Admins' | Out-File C:\Test\DomainAdmins.txt
ConvertTo-PDF -File C:\Test\DomainAdmins.txt
Exports a list of all of the accounts in the domain admins group and saves it to a text file.
It then imports the data and prints it to a new PDF file called: C:\Test\DomainAdmins.pdf
.LINK
Get-Content
Get-Item
Import-Csv
Import-CliXml
New-PDFPrinterPort
New-PDFUnattendPrinter
Out-Printer
Remove-PDFPrinterPort
Remove-Printer
https://community.idera.com/database-tools/powershell/powertips/b/tips/posts/sending-powershell-results-to-pdf-part-2
.INPUTS
System.String[]
.OUTPUTS
PDF File
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true,Position=0)]
[String[]]$File
)
Begin{
# Get source file information
Write-Debug "ConvertTo-PDF: Starting 'Begin' block."
$Item = Get-Item $File
Write-Debug "ConvertTo-PDF: Setting variable `$Item to the file located at: '$File'"
Write-Debug ("ConvertTo-PDF: `$Item parent directory: "+ $Item.DirectoryName)
Write-Debug ("ConvertTo-PDF: `$Item file name: " + $Item.Name)
Write-Debug ("ConvertTo-PDF: `$Item full name: " + $Item.FullName)
Write-Debug ("ConvertTo-PDF: `$Item base name: " + $Item.BaseName)
Write-Debug ("ConvertTo-PDF: `$Item file extension: " + $Item.Extension)
# Create the necessary printer that will be used to print the specified file unattended.
Write-Verbose "Creting a temporary printer and printer port for unattended printing."
Write-Debug "ConvertTo-PDF: Setting variable `$PDFPrinter to string value 'UnattendPDFPrinter'"
$PDFPrinter = "UnattendPDFPrinter"
$PDFPath = $Item.DirectoryName + '\' + $Item.BaseName + '.pdf'
Write-Debug "ConvertTo-PDF: Set variable `$PDFPath to '$PDFPath'"
Write-Debug "ConvertTo-PDF: The destination PDF file will be '$PDFPath'"
Write-Debug "ConvertTo-PDF: Creating new printer port called '$PDFPath'"
New-PDFPrinterPort -FilePath $PDFPath
Write-Debug "ConvertTo-PDF: Creating new printer called '$PDFPrinter'; it will use the port called '$PDFPath'"
New-PDFUnattendPrinter -PrinterName $PDFPrinter -PortName $PDFPath
# Rationalize file information and extensions
Write-Verbose "Checking file type to determine method for conversion."
Write-Debug "ConvertTo-PDF: Creating variable `$FileType and setting to an empty string."
[String]$FileType = ""
Write-Debug ("ConvertTo-PDF: Processing switch statement for `$Item.Extension; current value: " + $Item.Extension)
Switch($Item.Extension){
'.txt' {$FileType = "Text"; Write-Debug "File type was .txt; set `$FileType to '$FileType'"}
'.log' {$FileType = "Text"; Write-Debug "File type was .log; set `$FileType to '$FileType'"}
'.html' {$FileType = "HTML"; Write-Debug "File type was .html; set `$FileType to '$FileType'"}
'.xhtml' {$FileType = "HTML"; Write-Debug "File type was .xhtml; set `$FileType to '$FileType'"}
'.csv' {$FileType = "CSV"; Write-Debug "File type was .csv; set `$FileType to '$FileType'"}
'.xml' {$FileType = "XML"; Write-Debug "File type was .xml; set `$FileType to '$FileType'"}
'.xls' {$FileType = "Excel"; Write-Debug "File type was .xls; set `$FileType to '$FileType'"}
'.xlsx' {$FileType = "Excel"; Write-Debug "File type was .xlsx; set `$FileType to '$FileType'"}
'.doc' {$FileType = "Word"; Write-Debug "File type was .doc; set `$FileType to '$FileType'"}
'.docx' {$FileType = "Word"; Write-Debug "File type was .docx; set `$FileType to '$FileType'"}
}
Write-Debug "ConvertTo-PDF: Ending 'Begin' block."
}
Process{
Write-Debug "ConvertTo-PDF: Starting 'Process' block."
# Based on file extension, import the data and send it to the temporary printer
Write-Debug "ConvertTo-PDF: Using If statements to import content based on the value of `$FileType: $FileType."
If($FileType -eq "Text"){
# The file should be treated as raw text; use Get-Content
Write-Verbose "The file is a type of text file. Importing data with Get-Content and sending to PDF printer."
Write-Debug "ConvertTo-PDF: `$FileType matched string 'Text'; importing source file with Get-Content."
Get-Content $File | Out-Printer -Name $PDFPrinter
}ElseIf($FileType -eq "HTML"){
# The file is an HTML document; use Edge to open and print the file
Write-Debug "ConvertTo-PDF: `$FileType matched string 'HTML'; file type not supported at this time."
Write-Warning "Unsupported file type. Support for the file type of the provided file will be added soon."
}ElseIf($FileType -eq "CSV"){
# The file contains comma delimited values; use Import-Csv
Write-Verbose "File is a CSV. Using Import-Csv to retrieve the data and print raw CSV entries into target PDF file."
Write-Verbose "Support will be added in the future to treat a Csv as an Excel document. If Csv should printed in a table, convert to Excel and try again."
Write-Debug "ConvertTo-PDF: `$FileType matched string 'CSV'; Using Import-Csv to import content. This means the data will be exported in raw text."
Import-Csv $File | Out-Printer -Name $PDFPrinter
}ElseIf($FileType -eq "XML"){
# The file contains XML data; use Import-CliXml
Write-Verbose "File is an XML document; attempting to retrieve data with Import-CliXml and printing raw data to target PDF file."
Try{
Write-Debug "ConvertTo-PDF: `$FileType matched string 'XML'; Using Import-CliXml to import content. This means the data will be exported in raw text."
Import-Clixml $File -ErrorAction SilentlyContinue | Out-Printer -Name $PDFPrinter
}Catch{
Write-Verbose "Failed to import the XML document. It may not be in a supported schema."
Write-Debug "ConvertTo-PDF: failed to import the XML document; it may not be in the correct schema for use with Import-CliXml; processing the XML document as text using Get-Content"
Get-Content $File -ErrorAction SilentlyContinue | Out-Printer -Name $PDFPrinter
}
}ElseIf($FileType -eq "Excel"){
# The file is an Excel workbook; use the Excel application to open and print the workbook
Write-Debug "ConvertTo-PDF: `$FileType matched string 'Excel'; file type not supported at this time."
Write-Warning "Unsupported file type. Support for the file type of the provided file will be added soon."
}ElseIf($FileType -eq "Word"){
# The file is a Word document; use the Word appliction to open and print the the document
Write-Debug "ConvertTo-PDF: `$FileType matched string 'Word'; file type not supported at this time."
Write-Warning "Unsupported file type. Support for the file type of the provided file will be added soon."
}Else{
# The file type is currently unsupported
Write-Debug "ConvertTo-PDF: `$FileType value unmatched; file type not supported at this time."
Write-Error "Unsupported file type. The file type of the provided file is not surrently supported by this converter."
}
Write-Debug "ConvertTo-PDF: Ending 'Process' block."
}
End{
Write-Debug "ConvertTo-PDF: Starting 'End' block."
# Do any post-run cleanup processes
Write-verbose "Removing temporary PDF printer."
Write-Debug "ConvertTo-PDF: Running 'Remove-Printer -Name $PDFPrinter -Confirm:`$false' to delete the temporary printer."
Remove-Printer -Name $PDFPrinter -Confirm:$false
Write-Verbose "Removing temporary PDF printer port."
Write-Debug "ConvertTo-PDF: Running 'Remove-PDFPrinterPort -FilePath $PDFPath' to delete the temporary printer port."
Remove-PDFPrinterPort -FilePath $PDFPath
Write-Debug "ConvertTo-PDF: Closing 'End' block."
}
}