مقالة تقنية

تحويل ملفات RTF إلى PDF: دليل للمطورين

· برمجة PDF

تحويل ملفات RTF إلى PDF: دليل للمطورين في Delphi و C# و VB.Net.

في عالم معالجة المستندات، يعد تحويل ملفات Rich Text Format (RTF) إلى Portable Document Format (PDF) مهمة شائعة. يضمن هذا التحويل بقاء تخطيط المستند متسقًا عبر منصات مختلفة. يوفر هذا الدليل حلولًا مفصلة لتحويل 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.

فيما يلي تطبيق بلغة Delphi يوضح كيفية تحويل ملف RTF إلى ملف PDF باستخدام مكتبة PDF الخاصة بنا.
المثال التالي يستخدم إصدار DLL من مكتبة تطوير PDF (يتوفر أيضًا إصدار من التعليمات البرمجية المصدر بلغة Pascal لـ 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#:

فيما يلي تطبيق بلغة 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 في عنصر تحكم نص غني (RichEdit في Delphi و RichTextBox في C#).
  • إعداد سياق الجهاز: يتم إعداد سياق الجهاز لمحاكاة حجم صفحة A4، مما يضمن أن المحتوى يتناسب بشكل مناسب عند عرضه.
  • معالجة المحتوى: الـ PrintRtfBox تقوم الدالة (أو الطريقة) بتنسيق محتوى RTF وطباعته إلى سياق الجهاز. تحسب هذه الطريقة الأبعاد المطلوبة وتستدعي واجهة برمجة تطبيقات Windows المناسبة للتعامل مع تنسيق النص.
  • توليد ملفات PDF: تقوم الحلقة في كلا المثالين بمعالجة محتوى RTF صفحة تلو الأخرى، مما يؤدي إلى إنشاء مستندات PDF جديدة حسب الحاجة. يتم حفظ كل صفحة على القرص ثم إزالتها من الذاكرة لإدارة الموارد بكفاءة.
  • إشعار بالإكمال: بعد معالجة جميع الصفحات، يظهر مربع رسالة للمستخدم لإعلامه بأن عملية التحويل قد اكتملت.

باتباع هذه الخطوات، نضمن تحويلًا موثوقًا ودقيقًا لملفات RTF إلى مستندات PDF، مع الاستفادة من إمكانات مكتبة losLab PDF. تضمن هذه الطريقة الحفاظ على التنسيق والتصميم الأصليين، مما ينتج عنه ملفات PDF بجودة احترافية.

مثال VisualBasic.Net لتحويل RTF إلى PDF.

بالإضافة إلى ذلك، نقدم أيضًا تطبيق VB.Net، والذي يتمتع بنفس وظائف أمثلة Delphi و C#.

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