技術文章

將 RTF 檔案轉換為 PDF:開發人員指南

· PDF 程式設計

將 RTF 檔案轉換為 PDF:Delphi、C# 和 VB.Net 開發人員指南。

在文件處理領域,將 Rich Text Format (RTF) 檔案轉換為 Portable Document Format (PDF) 是一項常見的任務。 這種轉換可確保文件在不同平臺上的佈局保持一致。 本指南提供了使用我們的 PDF 開發庫在 Delphi、C# 和 VB.Net 中進行高效 RTF 到 PDF 轉換的詳細解決方案。 PDF 開發庫 在 Delphi、C# 和 VB.Net 中。 遵循本指南,您可以無縫地將 RTF 檔案轉換為 PDF 文件,同時保持與原始內容的高度一致性。 讓我們逐步瞭解此過程。

問題描述

這裡的主要挑戰是將 RTF 檔案轉換為 PDF 檔案,同時確保原始文件的格式和佈局得到保留。 這涉及:

  1. 載入 RTF 檔案。
  2. 設定用於渲染的虛擬裝置上下文。
  3. 將 RTF 內容列印到虛擬裝置上下文。
  4. 將渲染後的內容儲存為 PDF 檔案。

實現目標的步驟。

  1. 初始化環境:
    • 載入 losLab PDF 庫。
    • 將 RTF 檔案載入到富文本控制元件中。
  2. 設定裝置上下文:
    • 建立一個虛擬裝置上下文,該上下文模擬 A4 頁面的尺寸。
    • 計算所需的縮放比例,以使內容適應 PDF 頁面的大小。
  3. 處理 RTF 內容:
    • 使用虛擬裝置上下文來格式化和列印 RTF 內容。
    • 捕獲列印的內容,並從中建立一個新的 PDF 頁面。
  4. 儲存 PDF 文件:
    • 將生成的 PDF 頁面儲存到磁碟。
    • 通過迴圈遍歷 RTF 內容,並在需要時建立新的 PDF 文件,來處理多個頁面。

Delphi 程式碼示例。

以下是使用我們的 PDF 庫將 RTF 檔案轉換為 PDF 檔案的 Delphi 實現示例。
以下程式碼使用 PDF 開發庫的 DLL 版本(Delphi 也有可用的 Pascal 原始碼版本)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
unit MainUnit;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, PDFlibAX_TLB, ActiveX;
 
type
  TForm1 = class(TForm)
    RichEdit1: TRichEdit;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    function PrintRtfBox(hDc: HDC; rtfBox: TRichEdit; FirstChar: Integer): Integer;
  public
  end;
 
var
  Form1: TForm1;
  PdfDoc: TPDFLibrary;
 
implementation
 
{$R *.dfm}
 
procedure TForm1.FormCreate(Sender: TObject);
begin
  PdfDoc := TPDFLibrary.Create(Self);
  RichEdit1.Width := Round(ScaleX(210, mmPixel));
  RichEdit1.Height := Round(ScaleY(297, mmPixel));
  RichEdit1.Lines.LoadFromFile(ExtractFilePath(Application.ExeName) + 'Test.rtf');
end;
 
procedure TForm1.Button1Click(Sender: TObject);
var
  Dc: HDC;
  PageNumber, LastChar, PdfDocId: Integer;
begin
  PageNumber := 1;
  LastChar := 0;
  repeat
    Dc := PdfDoc.GetCanvasDC(Round(ScaleX(210, mmPixel)), Round(ScaleY(297, mmPixel)));
    LastChar := PrintRtfBox(Dc, RichEdit1, LastChar);
    PdfDoc.LoadFromCanvasDc(96, 0);
    PdfDocId := PdfDoc.SelectedPdfDocument;
    PdfDoc.SaveToFile(ExtractFilePath(Application.ExeName) + 'Test' + IntToStr(PageNumber) + '.pdf');
    PdfDoc.RemovePdfDocument(PdfDocId);
    Inc(PageNumber);
  until LastChar = 0;
  ShowMessage('Done');
end;
 
function TForm1.PrintRtfBox(hDc: HDC; rtfBox: TRichEdit; FirstChar: Integer): Integer;
var
  RcDrawTo, RcPage: TRect;
  Fr: TFormatRange;
  NextCharPosition: Integer;
begin
  RcPage.Left := 0;
  RcPage.Right := rtfBox.Left + rtfBox.Width + 100;
  RcPage.Top := 0;
  RcPage.Bottom := rtfBox.Top + rtfBox.Height + 100;
  RcDrawTo.Left := rtfBox.Left;
  RcDrawTo.Top := rtfBox.Top;
  RcDrawTo.Right := rtfBox.Left + rtfBox.Width;
  RcDrawTo.Bottom := rtfBox.Top + rtfBox.Height;
 
  Fr.hdc := hDc;
  Fr.hdcTarget := hDc;
  Fr.rc := RcDrawTo;
  Fr.rcPage := RcPage;
  Fr.chrg.cpMin := FirstChar;
  Fr.chrg.cpMax := -1;
 
  NextCharPosition := SendMessage(rtfBox.Handle, EM_FORMATRANGE, 1, LPARAM(@Fr));
  if NextCharPosition < Length(rtfBox.Text) then
    Result := NextCharPosition
  else
    Result := 0;
end;
 
end.

C# 程式碼示例

以下是 C# 實現,它實現了與 Delphi 示例相同的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using PDFLibrary;
 
namespace RtfToPdf
{
    public partial class Form1 : Form
    {
        private PDFLibrary PdfDoc;
 
        [StructLayout(LayoutKind.Sequential)]
        private struct RECT
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
        }
 
        [StructLayout(LayoutKind.Sequential)]
        private struct CharRange
        {
            public int cpMin;
            public int cpMax;
        }
 
        [StructLayout(LayoutKind.Sequential)]
        private struct FormatRange
        {
            public IntPtr hDc;
            public IntPtr hdcTarget;
            public RECT rc;
            public RECT RcPage;
            public CharRange chrg;
        }
 
        private const int WM_USER = 0x400;
        private const int EM_FORMATRANGE = WM_USER + 57;
 
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern IntPtr SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, ref FormatRange lParam);
 
        public Form1()
        {
            InitializeComponent();
            PdfDoc = new PDFLibrary();
        }
 
        private void Form1_Load(object sender, EventArgs e)
        {
            richTextBox1.SetBounds(0, 0, Convert.ToInt32(ScaleX(210, GraphicsUnit.Millimeter, GraphicsUnit.Pixel)), Convert.ToInt32(ScaleY(297, GraphicsUnit.Millimeter, GraphicsUnit.Pixel)));
            richTextBox1.LoadFile(Application.StartupPath + "\\Test.rtf");
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            IntPtr Dc;
            int PageNumber = 1;
            int LastChar = 0;
            int PdfDocId;
            do
            {
                Dc = PdfDoc.GetCanvasDC(Convert.ToInt32(ScaleX(210, GraphicsUnit.Millimeter, GraphicsUnit.Pixel)), Convert.ToInt32(ScaleY(297, GraphicsUnit.Millimeter, GraphicsUnit.Pixel)));
                LastChar = PrintRtfBox(Dc, richTextBox1, LastChar);
                PdfDoc.LoadFromCanvasDc(96, 0);
                PdfDocId = PdfDoc.SelectedPdfDocument;
                PdfDoc.SaveToFile(Application.StartupPath + "\\Test" + PageNumber + ".pdf");
                PdfDoc.RemovePdfDocument(PdfDocId);
                PageNumber++;
            } while (LastChar != 0);
            MessageBox.Show("Done");
        }
 
        private int PrintRtfBox(IntPtr hDc, RichTextBox rtfBox, int FirstChar)
        {
            RECT RcDrawTo = new RECT();
            RECT RcPage = new RECT();
            FormatRange Fr = new FormatRange();
 
            RcPage.Left = 0;
            RcPage.Right = rtfBox.Left + rtfBox.Width + 100;
            RcPage.Top = 0;
            RcPage.Bottom = rtfBox.Top + rtfBox.Height + 100;
            RcDrawTo.Left = rtfBox.Left;
            RcDrawTo.Top = rtfBox.Top;
            RcDrawTo.Right = rtfBox.Left + rtfBox.Width;
            RcDrawTo.Bottom = rtfBox.Top + rtfBox.Height;
 
            Fr.hDc = hDc;
            Fr.hdcTarget = hDc;
            Fr.rc = RcDrawTo;
            Fr.RcPage = RcPage;
            Fr.chrg.cpMin = FirstChar;
            Fr.chrg.cpMax = -1;
 
            IntPtr wParam = new IntPtr(1);
            IntPtr NextCharPosition = SendMessage(rtfBox.Handle, EM_FORMATRANGE, wParam, ref Fr);
 
            if (NextCharPosition.ToInt32() < rtfBox.Text.Length)
                return NextCharPosition.ToInt32();
            else
                return 0;
        }
 
        private float ScaleX(float value, GraphicsUnit fromUnit, GraphicsUnit toUnit)
        {
            using (Graphics g = this.CreateGraphics())
            {
                return value * g.DpiX / 96.0f;
            }
        }
 
        private float ScaleY(float value, GraphicsUnit fromUnit, GraphicsUnit toUnit)
        {
            using (Graphics g = this.CreateGraphics())
            {
                return value * g.DpiY / 96.0f;
            }
        }
    }
}

說明和理由

  • 初始化: Delphi 和 C# 版本都初始化了 losLab PDF Library) DLL 版本。RTF 檔案載入到富文本控制元件中(Delphi 中的 RichEdit 和 C# 中的 RichTextBox)。
  • 裝置上下文設定: 裝置上下文被設定為模擬 A4 頁面大小,以確保內容在渲染時能夠正確地適應。
  • 內容處理: 好的。 PrintRtfBox 函式(或方法)格式化 RTF 內容並將其列印到裝置上下文中。該方法計算所需的尺寸,並呼叫適當的 Windows API 來處理文本格式。
  • PDF 生成: 在兩個示例中的迴圈中,RTF 內容按頁面逐頁處理,並在必要時建立新的 PDF 文件。每個頁面都儲存到磁碟,然後從記憶體中刪除,以高效地管理資源。
  • 完成通知: 在處理完所有頁面後,一個訊息框會通知使用者轉換已完成。

遵循這些步驟,我們可以確保可靠且準確地將 RTF 檔案轉換為 PDF 文件,並利用 losLab PDF Library 的功能。這種方法可確保保留原始格式和佈局,從而生成高質量的 PDF 輸出。

VisualBasic.Net RTF 轉 PDF 示例

此外,我們還提供了一個 VB.Net 實現,它具有與 Delphi 和 C# 示例相同的 functionality。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
Imports System.Runtime.InteropServices
Imports PDFlibDLL
 
Public Class Form1
 
    Private PdfDoc As PDFlibrary
 
    <StructLayout(LayoutKind.Sequential)>
    Private Structure RECT
        Public Left As Integer
        Public Top As Integer
        Public Right As Integer
        Public Bottom As Integer
    End Structure
 
    <StructLayout(LayoutKind.Sequential)>
    Private Structure CharRange
        Public cpMin As Integer
        Public cpMax As Integer
    End Structure
 
    <StructLayout(LayoutKind.Sequential)>
    Private Structure FormatRange
        Public hDc As IntPtr
        Public hdcTarget As IntPtr
        Public rc As RECT
        Public rcPage As RECT
        Public chrg As CharRange
    End Structure
 
    Private Const WM_USER As Integer = &H400
    Private Const EM_FORMATRANGE As Integer = WM_USER + 57
 
    <DllImport("user32.dll", CharSet:=CharSet.Auto)>
    Private Shared Function SendMessage(hWnd As IntPtr, wMsg As Integer, wParam As IntPtr, ByRef lParam As FormatRange) As IntPtr
    End Function
 
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        PdfDoc = New PDFlibrary()
        RichTextBox1.SetBounds(0, 0, Convert.ToInt32(ScaleX(210, GraphicsUnit.Millimeter, GraphicsUnit.Pixel)), Convert.ToInt32(ScaleY(297, GraphicsUnit.Millimeter, GraphicsUnit.Pixel)))
        RichTextBox1.LoadFile(Application.StartupPath & "\Test.rtf")
    End Sub
 
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim Dc As IntPtr
        Dim PageNumber As Integer = 1
        Dim LastChar As Integer = 0
        Dim PdfDocId As Integer
 
        Do
            Dc = PdfDoc.GetCanvasDC(Convert.ToInt32(ScaleX(210, GraphicsUnit.Millimeter, GraphicsUnit.Pixel)), Convert.ToInt32(ScaleY(297, GraphicsUnit.Millimeter, GraphicsUnit.Pixel)))
            LastChar = PrintRtfBox(Dc, RichTextBox1, LastChar)
            PdfDoc.LoadFromCanvasDc(96, 0)
            PdfDocId = PdfDoc.SelectedPdfDocument
            PdfDoc.SaveToFile(Application.StartupPath & "\Test" & PageNumber & ".pdf")
            PdfDoc.RemovePdfDocument(PdfDocId)
            PageNumber += 1
        Loop While LastChar <> 0
 
        MessageBox.Show("Done")
    End Sub
 
    Private Function PrintRtfBox(hDc As IntPtr, rtfBox As RichTextBox, FirstChar As Integer) As Integer
        Dim RcDrawTo As New RECT()
        Dim RcPage As New RECT()
        Dim Fr As New FormatRange()
 
        RcPage.Left = 0
        RcPage.Right = rtfBox.Left + rtfBox.Width + 100
        RcPage.Top = 0
        RcPage.Bottom = rtfBox.Top + rtfBox.Height + 100
        RcDrawTo.Left = rtfBox.Left
        RcDrawTo.Top = rtfBox.Top
        RcDrawTo.Right = rtfBox.Left + rtfBox.Width
        RcDrawTo.Bottom = rtfBox.Top + rtfBox.Height
 
        Fr.hDc = hDc
        Fr.hdcTarget = hDc
        Fr.rc = RcDrawTo
        Fr.rcPage = RcPage
        Fr.chrg.cpMin = FirstChar
        Fr.chrg.cpMax = -1
 
        Dim wParam As IntPtr = New IntPtr(1)
        Dim NextCharPosition As IntPtr = SendMessage(rtfBox.Handle, EM_FORMATRANGE, wParam, Fr)
 
        If NextCharPosition.ToInt32() < rtfBox.Text.Length Then
            Return NextCharPosition.ToInt32()
        Else
            Return 0
        End If
    End Function
 
    Private Function ScaleX(value As Single, fromUnit As GraphicsUnit, toUnit As GraphicsUnit) As Single
        Using g As Graphics = Me.CreateGraphics()
            Return value * g.DpiX / 96.0F
        End Using
    End Function
 
    Private Function ScaleY(value As Single, fromUnit As GraphicsUnit, toUnit As GraphicsUnit) As Single
        Using g As Graphics = Me.CreateGraphics()
            Return value * g.DpiY / 96.0F
        End Using
    End Function
 
End Class