Teknisk artikel

Omvandla RTF-filer till PDF: En guide för utvecklare

· PDF-programmering

Omvandla RTF-filer till PDF: En guide för Delphi-, C#- och VB.Net-utvecklare

I en värld av dokumentbehandling är det en vanlig uppgift att konvertera RTF-filer (Rich Text Format) till Portable Document Format (PDF). Denna konvertering säkerställer att dokumentets layout förblir konsekvent på olika plattformar. Den här guiden ger detaljerade lösningar för effektiv RTF till PDF-konvertering med vår PDF-utvecklarbibliotek i Delphi, C# och VB.Net. Genom att följa den här guiden kan du sömlöst omvandla RTF-filer till PDF-dokument samtidigt som du bibehåller hög trohet mot det ursprungliga innehållet. Låt oss gå igenom processen steg för steg.

Problembeskrivning

Den primära utmaningen här är att konvertera en RTF-fil till en PDF-fil samtidigt som man säkerställer att formateringen och layouten för originaldokumentet bevaras. Detta innebär:

  1. Laddar RTF-filen.
  2. Konfigurera en virtuell enhetskontext för rendering.
  3. Skriver ut RTF-innehållet till den virtuella enhetskontexten.
  4. Spara det renderade innehållet som en PDF-fil.

Steg för att uppnå målet

  1. Initiera miljön:
    • Ladda losLab PDF-biblioteket.
    • Ladda RTF-filen till en RTF-kontroll.
  2. Ställ in enhetskontexten:
    • Skapa en virtuell enhetskontext som efterliknar dimensionerna på en A4-sida.
    • Beräkna den nödvändiga skalningen för att passa innehållet på PDF-sidan.
  3. Bearbeta RTF-innehållet:
    • Använd den virtuella enhetskontexten för att formatera och skriva ut RTF-innehållet.
    • Fånga det utskrivna innehållet och skapa en ny PDF-sida från det.
  4. Spara PDF-dokumentet:
    • Spara de genererade PDF-sidorna på disken.
    • Hantera flera sidor genom att gå igenom RTF-innehållet och skapa nya PDF-dokument efter behov.

Delphi Code Exempel

Nedan är en Delphi-implementering som visar konverteringen av en RTF-fil till en PDF-fil med vårt PDF-bibliotek,
Följande använder DLL-versionen av PDF-utvecklingsbiblioteket (En Pascal-källkodsversion finns också tillgänglig för 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.

Exempel på C#-kod

Nedan är en C#-implementation som uppnår samma funktionalitet som Delphi-exemplet.

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

Förklaring och motivering

  • Initiering: Både Delphi- och C#-versionerna initierar losLab PDF-bibliotek DLL-utgåva. RTF-filen laddas in i en rich text-kontroll (RichEdit i Delphi och RichTextBox i C#).
  • Kontextinställningar för enhet: Enhetskontexten är inställd för att simulera en A4-sida, vilket säkerställer att innehållet passar när det renderas.
  • Innehållsbearbetning: Den PrintRtfBox funktion (eller metod) formaterar RTF-innehållet och skriver ut det till enhetskontexten. Denna metod beräknar de nödvändiga dimensionerna och anropar lämpligt Windows API för att hantera textformateringen.
  • PDF-generering: Slingan i båda exemplen bearbetar RTF-innehållet sida för sida och skapar nya PDF-dokument efter behov. Varje sida sparas på disk och tas sedan bort från minnet för att hantera resurser effektivt.
  • Avisering om slutförande: Efter bearbetning av alla sidor informerar en meddelanderuta användaren om att konverteringen är klar.

Genom att följa dessa steg säkerställer vi en tillförlitlig och korrekt omvandling av RTF-filer till PDF-dokument, och utnyttjar möjligheterna hos losLab PDF-bibliotek. Detta tillvägagångssätt garanterar att den ursprungliga formateringen och layouten bevaras, vilket resulterar i PDF-utdata av professionell kvalitet.

VisualBasic.Net RTF till PDF Exempel

Dessutom tillhandahåller vi också en VB.Net-implementering, som har samma funktionalitet som Delphi- och C#-exemplen:

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