Technical Article

Transforming RTF Files into PDF: A Guide for Developers

· PDF Programming

Transforming RTF Files into PDF: A Guide for Delphi, C# & VB.Net Developers

In the world of document processing, converting Rich Text Format (RTF) files to Portable Document Format (PDF) is a common task. This conversion ensures the document’s layout remains consistent across different platforms. This guide provides detailed solutions for efficient RTF to PDF conversion using our PDF developer library in Delphi, C# and VB.Net. By following this guide, you can seamlessly transform RTF files into PDF documents while maintaining high fidelity to the original content. Let’s walk through the process step-by-step.

Problem Statement

The primary challenge here is to convert an RTF file into a PDF file while ensuring that the formatting and layout of the original document are preserved. This involves:

  1. Loading the RTF file.
  2. Setting up a virtual device context for rendering.
  3. Printing the RTF content to the virtual device context.
  4. Saving the rendered content as a PDF file.

Steps to Achieve the Goal

  1. Initialize the Environment:
    • Load the losLab PDF Library.
    • Load the RTF file into a rich text control.
  2. Set Up the Device Context:
    • Create a virtual device context that mimics the dimensions of an A4 page.
    • Calculate the necessary scaling to fit the content within the PDF page.
  3. Process the RTF Content:
    • Use the virtual device context to format and print the RTF content.
    • Capture the printed content and create a new PDF page from it.
  4. Save the PDF Document:
    • Save the generated PDF pages to disk.
    • Handle multiple pages by looping through the RTF content and creating new PDF documents as needed.

Delphi Code Example

Below is a Delphi implementation that demonstrates the conversion of an RTF file to a PDF file using the our PDF Library,
The following uses the DLL version of the PDF development library (A Pascal source code version is also available for 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
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# Code Example

Below is a C# implementation that achieves the same functionality as the Delphi example.

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;
            }
        }
    }
}

Explanation and Justification

  • Initialization: Both the Delphi and C# versions initialize the losLab PDF Library DLL edition. The RTF file is loaded into a rich text control (RichEdit in Delphi and RichTextBox in C#).
  • Device Context Setup: The device context is set up to simulate an A4 page size, ensuring the content fits appropriately when rendered.
  • Content Processing: The PrintRtfBox function (or method) formats the RTF content and prints it to the device context. This method calculates the required dimensions and calls the appropriate Windows API to handle the text formatting.
  • PDF Generation: The loop in both examples processes the RTF content page by page, creating new PDF documents as necessary. Each page is saved to disk and then removed from memory to manage resources efficiently.
  • Completion Notification: After processing all pages, a message box informs the user that the conversion is complete.

By adhering to these steps, we ensure a reliable and accurate transformation of RTF files into PDF documents, leveraging the capabilities of losLab PDF Library. This approach guarantees that the original formatting and layout are preserved, resulting in professional-quality PDF outputs.

VisualBasic.Net RTF to PDF Example

In addition, we also provide a VB.Net implementation, which has the same functionality as the Delphi and C# examples:

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