列印概觀

有了 Microsoft .NET Framework,使用 Windows Presentation Foundation (WPF) 的應用程式開發人員等於擁有了一套豐富全新的列印與列印系統管理 API。 在 Windows Vista 系統中,部分列印系統增強功能也可供開發 Windows Forms 應用程式和使用非受控程式碼的開發人員使用。 這項新功能的核心是新的 XML 文件規格 (XPS) 檔案格式和 XPS 列印路徑。

本主題包含下列各節。

關於 XPS

XPS 是一種電子文件格式、多工緩衝檔案格式及頁面描述語言。 它是一種開放文件格式,使用 XML、開放式封裝慣例 (OPC) 以及其他業界標準,來建立跨平台文件。 XPS 可簡化數位文件的建立、共用、列印、檢視和封存過程。 如需有關 XPS 的其他資訊,請參閱 XPS 文件

以程式設計方式列印 XPS 檔案中會示範數種使用 WPF 列印 XPS 格式內容的技術。 在檢閱本主題所包含內容期間,您會發現參考這些範例相當有用。 非受控程式碼開發人員應參閱 MXDC_ESCAPE 函式的相關文件。Windows Forms 開發人員必須使用 System.Drawing.Printing 命名空間中的 API,該命名空間不支援完整的 XPS 列印路徑,但支援混合式 GDI 到 XPS 的列印路徑。請參閱下方的列印路徑架構)。

XPS 列印路徑

XML 文件規格 (XPS) 列印路徑是 Windows 新增的功能,重新定義了 Windows 應用程式中列印處理的方式 由於 XPS 可取代文件呈現語言 (如 RTF)、列印多工緩衝處理器格式 (如 WMF) 和頁面描述語言 (如 PCL 或 Postscript),新的列印路徑,從應用程式發佈,到列印驅動程式或裝置的最終處理過程中,都會保持 XPS 格式的一致性。

XPS 列印路徑是基於 XPS 印表機驅動程式模型 (XPSDrv) 而建,這為開發人員提供了多項優勢,例如「所見即所得」(WYSIWYG) 列印、改善的色彩支援,以及顯著提升的列印效能。 (若要了解更多關於 XPSDrv 的資訊,請參閱 Windows 驅動程式套件。)

XPS 文件的列印多工緩衝處理器基本上與舊版 Windows 相同。 不過,除了現有的 GDI 列印路徑之外,增強版的多工緩衝處理器已能支援 XPS 列印路徑。 新的列印路徑會以原生方式取用 XPS 多工緩衝處理檔案。 雖然為舊版 Windows 撰寫的使用者模式印表機驅動程式仍可運行,但若要使用 XPS 列印路徑,則需要 XPS 印表機驅動程式 (XPSDrv)。

XPS 列印路徑的優勢顯著,包含以下幾點:

  • 支援所見即所得 (WYSIWYG) 列印

  • 進階色彩設定檔的原生支援,其中包含每個通道 32 位元 (bpc)、CMYK、具名色彩、n-inks 和透明及漸層效果的原生支援。

  • 提升 .NET Framework 和 Win32 應用程式的列印效能

  • 業界標準 XPS 格式。

對於基本列印案例,提供簡單且直覺的 API,單一進入點即可進行使用者介面、設定及工作提交。 針對進階案例,在同步或非同步列印,以及批次列印功能的使用者介面,新增額外支援。 這兩個選項都提供完整或部分信任模式中的列印支援。

XPS 的設計考量了擴充性。 透過擴充性架構,能以模組化方式新增特色與功能至 XPS。 擴充性功能包括:

  • 列印結構描述。 會定期更新公用結構描述,並讓裝置功能可快速擴充。 (請參閱下方的 PrintTicket 和 PrintCapabilities)。

  • 可擴充的篩選管線。 XPS 印表機驅動程式 (XPSDrv) 的篩選管線設計能同時支援 XPS 文件的直接列印與可調整列印。 如需詳細資訊,請參閱 XPSDrv 印表機驅動程式

雖然 Win32 和 .NET Framework 應用程式都支援 XPS,但 Win32 和 Windows Forms 應用程式會使用 GDI 到 XPS 轉換功能,以建立 XPS 格式內容供 XPS 印表機驅動程式 (XPSDrv) 使用。 這些應用程式不需要使用 XPS 列印路徑,而且可以繼續使用增強型中繼檔 (EMF) 列印。 不過,大部分 XPS 功能和增強功能僅適用於以 XPS 列印路徑為目標的應用程式。

為了讓 Win32 和 Windows Forms 應用程式能夠使用 XPSDrv 型印表機,XPS 印表機驅動程式 (XPSDrv) 支援將 GDI 轉換為 XPS 格式。 XPSDrv 模型也提供將 XPS 轉換為 GDI 格式的轉換器,使得 Win32 應用程式也能列印 XPS 文件。 針對 WPF 應用程式,當寫入作業的目標列印佇列沒有 XPSDrv 驅動程式時,XpsDocumentWriter 類別的 WriteWriteAsync 方法會自動將 XPS 轉換成 GDI 格式。 (Windows Forms 應用程式無法列印 XPS 文件。)

下圖說明列印子系統,並定義 Microsoft 所提供的部分,以及軟硬體廠商所定義的部分:

顯示 XPS 列印系統的螢幕擷取畫面。

基本 XPS 列印

WPF 定義了基本與進階的 API。 對於不需要大量列印自訂功能或不需存取完整 XPS 功能集的應用程式,有基本的列印支援可供使用。 基本列印支援透過列印對話框控制項提供,該控制項僅需基本設定,並具有熟悉的使用者介面。 透過此簡化的列印模型,許多 XPS 功能也能被使用。

PrintDialog

System.Windows.Controls.PrintDialog 控制項提供單一的進入點,用於使用者介面、設定及 XPS 工作提交。 如需如何具現化及使用控制項的相關資訊,請參閱叫用列印對話方塊

進階的 XPS 列印

若要存取完整的 XPS 功能集,則必須使用進階列印 API。 以下更詳細地說明幾個相關的 API。 如需 XPS 列印路徑 API 的完整清單,請參閱 System.Windows.XpsSystem.Printing 命名空間參考文件。

PrintTicket 和 PrintCapabilities

PrintTicketPrintCapabilities 類別是進階 XPS 功能的基礎。 這兩種類別的物件都是 XML 格式結構,用於列印相關功能,例如定序、雙面列印、裝訂等。列印的結構描述會定義這些結構。 PrintTicket 會指示印表機該如何處理列印工作。 PrintCapabilities 類別會定義印表機的功能。 藉由查詢印表機的功能,可以建立 PrintTicket 來完整利用印表機支援的功能。 同樣地,您也可避免不支援的功能。

下列範例示範如何查詢印表機的 PrintCapabilities,並使用程式碼建立 PrintTicket

// ---------------------- GetPrintTicketFromPrinter -----------------------
/// <summary>
///   Returns a PrintTicket based on the current default printer.</summary>
/// <returns>
///   A PrintTicket for the current local default printer.</returns>
PrintTicket^ GetPrintTicketFromPrinter () 
{
   PrintQueue^ printQueue = nullptr;

   LocalPrintServer^ localPrintServer = gcnew LocalPrintServer();

   // Retrieving collection of local printer on user machine
   PrintQueueCollection^ localPrinterCollection = localPrintServer->GetPrintQueues();

   System::Collections::IEnumerator^ localPrinterEnumerator = localPrinterCollection->GetEnumerator();

   if (localPrinterEnumerator->MoveNext())
   {
      // Get PrintQueue from first available printer
      printQueue = ((PrintQueue^)localPrinterEnumerator->Current);
   } else
   {
      return nullptr;
   }
   // Get default PrintTicket from printer
   PrintTicket^ printTicket = printQueue->DefaultPrintTicket;

   PrintCapabilities^ printCapabilites = printQueue->GetPrintCapabilities();

   // Modify PrintTicket
   if (printCapabilites->CollationCapability->Contains(Collation::Collated))
   {
      printTicket->Collation = Collation::Collated;
   }
   if (printCapabilites->DuplexingCapability->Contains(Duplexing::TwoSidedLongEdge))
   {
      printTicket->Duplexing = Duplexing::TwoSidedLongEdge;
   }
   if (printCapabilites->StaplingCapability->Contains(Stapling::StapleDualLeft))
   {
      printTicket->Stapling = Stapling::StapleDualLeft;
   }
   return printTicket;
};// end:GetPrintTicketFromPrinter()
// ---------------------- GetPrintTicketFromPrinter -----------------------
/// <summary>
///   Returns a PrintTicket based on the current default printer.</summary>
/// <returns>
///   A PrintTicket for the current local default printer.</returns>
private PrintTicket GetPrintTicketFromPrinter()
{
    PrintQueue printQueue = null;

    LocalPrintServer localPrintServer = new LocalPrintServer();

    // Retrieving collection of local printer on user machine
    PrintQueueCollection localPrinterCollection =
        localPrintServer.GetPrintQueues();

    System.Collections.IEnumerator localPrinterEnumerator =
        localPrinterCollection.GetEnumerator();

    if (localPrinterEnumerator.MoveNext())
    {
        // Get PrintQueue from first available printer
        printQueue = (PrintQueue)localPrinterEnumerator.Current;
    }
    else
    {
        // No printer exist, return null PrintTicket
        return null;
    }

    // Get default PrintTicket from printer
    PrintTicket printTicket = printQueue.DefaultPrintTicket;

    PrintCapabilities printCapabilites = printQueue.GetPrintCapabilities();

    // Modify PrintTicket
    if (printCapabilites.CollationCapability.Contains(Collation.Collated))
    {
        printTicket.Collation = Collation.Collated;
    }

    if ( printCapabilites.DuplexingCapability.Contains(
            Duplexing.TwoSidedLongEdge) )
    {
        printTicket.Duplexing = Duplexing.TwoSidedLongEdge;
    }

    if (printCapabilites.StaplingCapability.Contains(Stapling.StapleDualLeft))
    {
        printTicket.Stapling = Stapling.StapleDualLeft;
    }

    return printTicket;
}// end:GetPrintTicketFromPrinter()
' ---------------------- GetPrintTicketFromPrinter -----------------------
''' <summary>
'''   Returns a PrintTicket based on the current default printer.</summary>
''' <returns>
'''   A PrintTicket for the current local default printer.</returns>
Private Function GetPrintTicketFromPrinter() As PrintTicket
    Dim printQueue As PrintQueue = Nothing

    Dim localPrintServer As New LocalPrintServer()

    ' Retrieving collection of local printer on user machine
    Dim localPrinterCollection As PrintQueueCollection = localPrintServer.GetPrintQueues()

    Dim localPrinterEnumerator As System.Collections.IEnumerator = localPrinterCollection.GetEnumerator()

    If localPrinterEnumerator.MoveNext() Then
        ' Get PrintQueue from first available printer
        printQueue = CType(localPrinterEnumerator.Current, PrintQueue)
    Else
        ' No printer exist, return null PrintTicket
        Return Nothing
    End If

    ' Get default PrintTicket from printer
    Dim printTicket As PrintTicket = printQueue.DefaultPrintTicket

    Dim printCapabilites As PrintCapabilities = printQueue.GetPrintCapabilities()

    ' Modify PrintTicket
    If printCapabilites.CollationCapability.Contains(Collation.Collated) Then
        printTicket.Collation = Collation.Collated
    End If

    If printCapabilites.DuplexingCapability.Contains(Duplexing.TwoSidedLongEdge) Then
        printTicket.Duplexing = Duplexing.TwoSidedLongEdge
    End If

    If printCapabilites.StaplingCapability.Contains(Stapling.StapleDualLeft) Then
        printTicket.Stapling = Stapling.StapleDualLeft
    End If

    Return printTicket
End Function ' end:GetPrintTicketFromPrinter()

PrintServer 和 PrintQueue

PrintServer 類別代表網路列印伺服器,且 PrintQueue 類別代表印表機以及相關聯的輸出工作佇列。 同時,這些 API 允許伺服器列印工作的進階管理。 PrintServer 或其中一個衍生的類別,會用來管理 PrintQueueAddJob 方法會用來將新的列印工作插入佇列。

下列範例示範如何建立 LocalPrintServer 及使用程式碼存取其預設的 PrintQueue

// -------------------- GetPrintXpsDocumentWriter() -------------------
/// <summary>
///   Returns an XpsDocumentWriter for the default print queue.</summary>
/// <returns>
///   An XpsDocumentWriter for the default print queue.</returns>
private XpsDocumentWriter GetPrintXpsDocumentWriter()
{
    // Create a local print server
    LocalPrintServer ps = new LocalPrintServer();

    // Get the default print queue
    PrintQueue pq = ps.DefaultPrintQueue;

    // Get an XpsDocumentWriter for the default print queue
    XpsDocumentWriter xpsdw = PrintQueue.CreateXpsDocumentWriter(pq);
    return xpsdw;
}// end:GetPrintXpsDocumentWriter()
' -------------------- GetPrintXpsDocumentWriter() -------------------
''' <summary>
'''   Returns an XpsDocumentWriter for the default print queue.</summary>
''' <returns>
'''   An XpsDocumentWriter for the default print queue.</returns>
Private Function GetPrintXpsDocumentWriter() As XpsDocumentWriter
    ' Create a local print server
    Dim ps As New LocalPrintServer()

    ' Get the default print queue
    Dim pq As PrintQueue = ps.DefaultPrintQueue

    ' Get an XpsDocumentWriter for the default print queue
    Dim xpsdw As XpsDocumentWriter = PrintQueue.CreateXpsDocumentWriter(pq)
    Return xpsdw
End Function ' end:GetPrintXpsDocumentWriter()

XpsDocumentWriter

XpsDocumentWriter 和其許多的 WriteWriteAsync 方法,可用來寫入 PrintQueue 文件到 。 例如,Write(FixedPage, PrintTicket) 方法用來同步輸出 PrintTicket 文件和 。 WriteAsync(FixedDocument, PrintTicket) 方法用來非同步輸出 PrintTicket 文件和 。

下列範例示範如何使用程式碼建立 XpsDocumentWriter

// -------------------- GetPrintXpsDocumentWriter() -------------------
/// <summary>
///   Returns an XpsDocumentWriter for the default print queue.</summary>
/// <returns>
///   An XpsDocumentWriter for the default print queue.</returns>
private XpsDocumentWriter GetPrintXpsDocumentWriter()
{
    // Create a local print server
    LocalPrintServer ps = new LocalPrintServer();

    // Get the default print queue
    PrintQueue pq = ps.DefaultPrintQueue;

    // Get an XpsDocumentWriter for the default print queue
    XpsDocumentWriter xpsdw = PrintQueue.CreateXpsDocumentWriter(pq);
    return xpsdw;
}// end:GetPrintXpsDocumentWriter()
' -------------------- GetPrintXpsDocumentWriter() -------------------
''' <summary>
'''   Returns an XpsDocumentWriter for the default print queue.</summary>
''' <returns>
'''   An XpsDocumentWriter for the default print queue.</returns>
Private Function GetPrintXpsDocumentWriter() As XpsDocumentWriter
    ' Create a local print server
    Dim ps As New LocalPrintServer()

    ' Get the default print queue
    Dim pq As PrintQueue = ps.DefaultPrintQueue

    ' Get an XpsDocumentWriter for the default print queue
    Dim xpsdw As XpsDocumentWriter = PrintQueue.CreateXpsDocumentWriter(pq)
    Return xpsdw
End Function ' end:GetPrintXpsDocumentWriter()

AddJob 方法也會提供列印的方式。 請參閱以程式設計方式列印 XPS 檔。 。

GDI 列印路徑

雖然 WPF 應用程式原本就支援 XPS 列印路徑,但 Win32 和 Windows Forms 應用程式也可以利用部分 XPS 功能。 XPS 印表機驅動程式 (XPSDrv) 可以轉換 GDI 基礎的輸出為 XPS 格式。 針對進階案例,使用 Microsoft XPS 檔轉換器 (MXDC),可支援內容的自訂轉換。 同樣地,WPF 應用程式也可以呼叫 XpsDocumentWriter 類別的其中一個 WriteWriteAsync 方法,並將非 XpsDrv 印表機指定為目標列印佇列,以輸出至 GDI 列印路徑。

對於不需要 XPS 功能或支援的應用程式、目前的 GDI 列印路徑會維持不變。

XPSDrv 驅動程式模型

XPS 列印路徑,將 XPS 作為原生列印多工緩衝處理格式,因此,當列印至支援 XPS 的印表機或驅動程式時,可有效提升多工緩衝處理器的效率。 簡化的列印多工緩衝過程,不再需要先產生中間多工緩衝暫存檔案,例如 EMF 資料檔案,便可直接進行文件的多工緩衝暫存。 較小的多工緩衝處理檔案,使得 XPS 列印路徑得以減少網路流量並改善列印效能。

EMF 是種封閉格式,可將應用程式的輸出表示為一系列對 GDI 的呼叫,以執行轉譯服務。 不同於 EMF ,XPS 列印多工緩衝處理格式即實際文件,無需進一步轉譯即可輸出至 XPS 型印表機驅動程式 (XPSDrv)。 驅動程式可以直接在格式中的資料上運作。 這項功能免除了使用 EMF 檔案和 GDI 型印表機驅動程式時所需的數據和色彩空間轉換過程。

相較於 EMF 檔案,當您使用目標為 XPS 印表機驅動程式 (XPSDrv) 的 XPS 文件時,多工緩衝處理暫存檔案通常會被縮小;然而,也存在例外情況:

  • 非常複雜、多層或無效率寫入的向量圖形可能大於相同圖形的點陣圖版本。

  • 供螢幕顯示之用,XPS 檔內嵌裝置字型,以及以電腦為基礎的字型;而 GDI 多工緩衝處理檔案則不會內嵌裝置字型。 但是這兩種字型為部分內嵌的字型 (如下所示),而且印表機驅動程式可以移除裝置字型,然後再將檔案傳輸至印表機。

多工緩衝處理大小縮減會透過數種機制執行:

  • 字型部分內嵌。 只有實際文件中使用的字元會儲存在 XPS 檔案中。

  • 進階圖形支援。 原生支援透明及漸層效果基本類型,避免 XPS 文件內容點陣化。

  • 識別通用資源。 多次使用的資源 (例如代表公司標誌的影像) 會被視為共用資源,並只會載入一次。

  • ZIP 壓縮。 所有 XPS 檔案都會使用 ZIP 壓縮。

另請參閱