يرسم HotPDF الرسوميات المتجهية ببناء مسار على الصفحة الحالية ثم طلب رسمه. ولا توجد خطوة صورة نقطية بينهما. فالخط الذي ترسمه بـ MoveTo وLineTo ينتهي به المطاف كمعاملات مسار PDF في دفق المحتوى، لذا يبقى متجهًا حقيقيًا: حادًا عند تكبير 50%، وحادًا عند 1600%، وبجزء يسير من الحجم الذي ستكلّفه نسخة منقَّطة. وللمخططات وخطوط الجداول ومحاور الرسوم البيانية وزخارف النماذج، هذا بالضبط ما تريده، والواجهة خلفه صغيرة بما يكفي لتعلّمها في جلسة واحدة
يقيم سطح الرسم كله على THotPDF.CurrentPage. وبين BeginDoc وEndDoc تضبط اللون وعرض الخط على كائن الصفحة ذاك، وتضع الهندسة، وتستدعي معامل رسم لتثبيتها. والعناصر الأولية الأربعة التي ستستخدمها أكثر هي MoveTo وLineTo للمسارات الاعتباطية، وRectangle للصناديق، وCircle للأقراص، ومعاملا الرسم Stroke وFill
نظام الإحداثيات أصله الزاوية السفلية اليسرى
هذا هو الشيء الوحيد الذي يتعثر فيه كل قادم من VCL. فـ TCanvas الذي ترسم به عناصر التحكم يضع نقطة الأصل في الزاوية العلوية اليسرى مع نمو Y نحو الأسفل. ويفعل PDF العكس. يقيس HotPDF من الزاوية السفلية اليسرى للصفحة بالنقاط (1/72 بوصة)، مع تزايد Y كلما تحركت لأعلى. فنقطة عند Y := 720 تقع قرب أعلى صفحة US Letter، التي يبلغ ارتفاعها 792 نقطة، وY := 50 تقع قرب الأسفل. وإذا خرج رسمك الأول معكوسًا رأسيًا، فهذا هو السبب: كود منقول من رسوميات الشاشة يفترض الاتجاه الخاطئ ويخرج عن الحافة السفلية
وتحكم القاعدة نفسها TextOut، لذا يتشارك النص والأشكال نموذجًا ذهنيًا واحدًا ما إن تستوعبه. خطط للتخطيط بتحديد موضع أسفل كل عنصر، لا أعلاه، ويتبع الباقي
المسارات: MoveTo و LineTo و Stroke
المسار المحدَّد هو قلم يُرفع ويوضع ويُسحب. ترفع MoveTo القلم وتضبط نقطة البداية من دون تعليم أي شيء. وكل LineTo تمدّ المسار الحالي إلى نقطة جديدة. ولا يظهر شيء على الصفحة حتى تستدعي Stroke، التي ترسم المسار المتراكم باستخدام لون التحديد وعرض الخط الحاليين، ثم تمسح المسار بحيث تبدأ MoveTo التالية من جديد
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'DrawPaths.pdf';
Pdf.BeginDoc;
// عرض الخط بالنقاط ويسري حتى تغيّره
Pdf.CurrentPage.SetLineWidth(1.5);
Pdf.CurrentPage.SetRGBStrokeColor(clBlack);
// خط أفقي قرب أعلى الصفحة (Y يُقاس من الأسفل)
Pdf.CurrentPage.MoveTo(72, 720);
Pdf.CurrentPage.LineTo(523, 720);
Pdf.CurrentPage.Stroke; // ثبّت المسار؛ لم يُرسم شيء قبل هذا
// خط متعدد القطع متصل وأسمك: ثلاث قطع في مسار واحد
Pdf.CurrentPage.SetLineWidth(3);
Pdf.CurrentPage.SetRGBStrokeColor(RGB(30, 90, 200));
Pdf.CurrentPage.MoveTo(72, 640);
Pdf.CurrentPage.LineTo(172, 690);
Pdf.CurrentPage.LineTo(272, 620);
Pdf.CurrentPage.LineTo(372, 680);
Pdf.CurrentPage.Stroke;
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
تفصيلان يوفّران وقت تنقيح حقيقيًا. عرض الخط حالة لا وسيط: تضبطه SetLineWidth مرة واحدة وتستخدم كل Stroke لاحقة تلك القيمة حتى تغيّرها مجددًا، ولهذا فإن الخط متعدد القطع أعلاه أسمك من الخط الأفقي. ويُعاد ضبط المسار بعد كل Stroke، لذا فإن نسيان Stroke يعني أن الهندسة التي وضعتها بعناية لا تُصيَّر على الإطلاق. وإذا غاب شكل من المخرجات، فاستدعاء الرسم هو أول مكان تنظر فيه
والإحداثيات نقاط، والنقاط كسرية. تقبل MoveTo وLineTo قيم Single، لذا فإن خطًا شعريًا بعرض 0.5 نقطة أو موضعًا عند 72.25 مشروع وذو معنى، ولا يُقرَّب بعيدًا إلى أقرب وحدة كاملة. وتلك الدقة مهمة في اتجاهين متعاكسين. فعرض خط أقل من نحو 0.5 قد يُصيَّر كأنحف خط ممكن يعتمد على الجهاز، يختفي على الشاشة ويعود للظهور عند الطباعة، لذا فإن خطًا مرئيًا يريد عرضًا تضبطه عمدًا لا الافتراضي. وفي الطرف الآخر، محاذاة خطوط الجداول وخطوط الشبكة إلى إحداثيات بنقاط كاملة تمنع شبكة كثيفة من أن تبدو غير متساوية قليلًا حيث تُقرَّب الخطوط المتجاورة بشكل مختلف. حدّد تباعد الشبكة بالنقاط مسبقًا ويرث بقية التخطيط ذلك
الأشكال المعبّأة والألوان
يمكن تعبئة العناصر الأولية المغلقة بدلًا من تحديدها. يأخذ Rectangle موضعًا وحجمًا، ويأخذ Circle مركزًا ونصف قطر، ويُثبَّت أي منهما بـ Fill، التي ترسم الداخل بلون التعبئة الحالي، أو بـ Stroke للتحديد فقط. ولون التعبئة ولون التحديد قطعتا حالة منفصلتان، تُضبطان بـ SetRGBFillColor وSetRGBStrokeColor، وكلتاهما تأخذ TColor واحدًا. وهذا يعني أنك تستطيع إعادة استخدام ثوابت ألوان Delphi والمساعد RGB مباشرة
// Rectangle(X, Y, Width, Height): X و Y هما الزاوية السفلية اليسرى
Pdf.CurrentPage.SetRGBFillColor(RGB(220, 60, 60));
Pdf.CurrentPage.Rectangle(72, 500, 160, 90);
Pdf.CurrentPage.Fill;
// Circle(X, Y, Radius): X و Y هما المركز
Pdf.CurrentPage.SetRGBFillColor(clNavy);
Pdf.CurrentPage.Circle(420, 545, 45);
Pdf.CurrentPage.Fill;
// التحديد فقط: اضبط لون تحديد وعرضًا، ثم Stroke
Pdf.CurrentPage.SetLineWidth(2);
Pdf.CurrentPage.SetRGBStrokeColor(clBlack);
Pdf.CurrentPage.Rectangle(72, 400, 160, 60);
Pdf.CurrentPage.Stroke;
انتبه إلى شكل الوسائط في Rectangle. فهو موضع زائد حجم، X, Y, Width, Height، لا زاويتان متقابلتان. وTCanvas.Rectangle الذي يعرفه مطورو Delphi يأخذ (Left, Top, Right, Bottom)، لذا ستسلّم الذاكرة العضلية إلى HotPDF زاوية ثانية حيث يتوقع عرضًا وارتفاعًا، فيخرج الصندوق بالحجم الخاطئ. والزوج (X, Y) هو الزاوية السفلية اليسرى، اتساقًا مع نقطة أصل الصفحة. وللدائرة، (X, Y) هو المركز والوسيط الثالث هو نصف القطر بالنقاط
خيار لوني واحد أخطأت فيه العيّنة الأصلية
كانت نسخة أقدم من هذا المثال تبذر الألوان بـ Random($FFFFFF) على كل شكل. يبدو ذلك حيويًا، وهو الغريزة الخاطئة للمستندات المولَّدة. فملف PDF تبنيه من كود هو عادةً شيء تريد اختباره أيضًا، وألوان التعبئة العشوائية تجعل المخرجات مستحيلة المقارنة من تشغيل إلى آخر: يفشل فرق بايت مقابل بايت مع ملف معروف الصحة في كل مرة، بلا سبب حقيقي. اختر ألوانًا صريحة. وحين تريد تنوعًا عبر سلسلة من الأشكال، قُده من بياناتك أو من مصفوفة لوحة ألوان ثابتة، بحيث ينتج المدخل نفسه الملف نفسه دائمًا. فالحتمية تساوي أكثر من الجدّة حين يمر الناتج عبر خط أنابيب إصدار
تجميع العناصر الأولية معًا: صندوق تعليق
كل عنصر أولي بسيط بمفرده؛ ويظهر المكسب حين تتركب حفنة منها في شيء يحتاجه تقرير فعليًا. فصندوق التعليق، أي الصندوق المشروح الذي يشير إلى شكل ويفسّره، يستخدم كل ما غُطّي حتى الآن: مستطيل معبّأ بإطار، وخط مؤشر محدَّد، ونقطة لتثبيت المؤشر، ونص موضوع داخل الصندوق باستخدام الإحداثيات ذاتها ذات الأصل السفلي الأيسر التي تستخدمها الأشكال. وتكسب FillAndStroke مكانها هنا، إذ ترسم الداخل والتحديد لمسار واحد في تثبيت واحد بدلًا من بناء المستطيل مرتين
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'Callout.pdf';
Pdf.BeginDoc;
// 1. الصندوق: تعبئة باهتة زائد إطار مرئي، مسار واحد، تثبيت واحد
// Rectangle هو الزاوية السفلية اليسرى زائد الحجم، Y يُقاس من الأسفل
Pdf.CurrentPage.SetRGBFillColor(RGB(255, 244, 214)); // لوحة كهرمانية باهتة
Pdf.CurrentPage.SetRGBStrokeColor(RGB(180, 130, 40)); // حافة أغمق
Pdf.CurrentPage.SetLineWidth(1);
Pdf.CurrentPage.Rectangle(90, 600, 240, 70);
Pdf.CurrentPage.FillAndStroke;
// 2. المؤشر: قطعة محدَّدة واحدة من حافة الصندوق نزولًا
// نحو الشيء المشروح
Pdf.CurrentPage.SetLineWidth(1.5);
Pdf.CurrentPage.MoveTo(90, 615); // الحافة اليسرى للصندوق
Pdf.CurrentPage.LineTo(66, 546);
Pdf.CurrentPage.Stroke;
// 3. نقطة معبّأة تثبّت المؤشر عند هدفه
Pdf.CurrentPage.SetRGBFillColor(RGB(180, 130, 40));
Pdf.CurrentPage.Circle(64, 542, 3);
Pdf.CurrentPage.Fill;
// 4. التسمية، موضوعة نسبةً إلى الزاوية السفلية اليسرى للصندوق
// يتشارك النص والأشكال نظام إحداثيات واحدًا، لذا فإن الإزاحات
// حساب بسيط نسبةً إلى (90, 600)
Pdf.CurrentPage.SetFont('Arial', [fsBold], 10);
Pdf.CurrentPage.TextOut(102, 645, 0, 'Check this total');
Pdf.CurrentPage.SetFont('Arial', [], 9);
Pdf.CurrentPage.TextOut(102, 628, 0, 'The rounding rule changed in the');
Pdf.CurrentPage.TextOut(102, 616, 0, 'June release; verify against v2.1');
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
لاحظ كم هي قليلة إدارة الحالة التي يحتاجها المركّب. فلون التعبئة ولون التحديد وعرض الخط يُضبط كل منها مباشرة قبل الشكل الذي يستخدمه، لذا تُقرأ كل كتلة في الرسم كوحدة مستقلة بذاتها ويمكن إعادة ترتيبها أو استخراجها في مساعد من دون جرّ حالة خفية معها. لفّ هذا في إجراء يأخذ نقطة التثبيت والسلاسل، وتحصل على شرح مخطط قابل لإعادة الاستخدام بثمن أربعين سطرًا
أين يؤتي الرسم المتجهي ثماره، وأين لا
الجأ إلى استدعاءات المسارات والأشكال هذه حين تكون الهندسة مولَّدة: خطوط شبكة الرسوم البيانية وأعمدتها، والخطوط المسطّرة لجدول فاتورة، وصناديق التعليق على مخطط، وعلامة شعار معبَّر عنها بحفنة من المسارات. كل ذلك يتحجّم من دون ضبابية ولا يضيف شيئًا يُذكر إلى حجم الملف، لأن المستطيل بضعة أرقام لا آلاف البكسلات. والوجه الآخر صادق أيضًا. فإذا كان ما لديك فعليًا صورة فوتوغرافية أو لقطة شاشة، فارسمها كصورة بـ AddImage وShowImage بدلًا من ذلك؛ فتتبّع صورة نقطية باستدعاءات متجهية لا يفيدك شيئًا. وتحمل القطع المستقيمة والمستطيلات والدوائر أعلاه الغالبية العظمى من أعمال التقارير الحقيقية، والتحسينات الثلاثة التي يسأل عنها المطورون بعد ذلك، أي المنحنيات وأنماط التقطيع والشفافية، تقع على كائن الصفحة نفسه
المنحنيات والتقطيع والشفافية باختصار
تمدّ المنحنيات الحرة آلية المسار نفسها التي لديك بالفعل. تُلحق CurveToC(X1, Y1, X2, Y2, X3, Y3) قطعة Bezier تكعيبية من النقطة الحالية إلى (X3, Y3)، منحنيةً نحو نقطتي التحكم، وتغطي النسختان المختصرتان CurveToV وCurveToY الحالات التي تتطابق فيها إحدى نقطتي التحكم مع نقطة طرفية. ويمكن للمسار أن يمزج قطع LineTo وCurveToC بحرية قبل أن تثبّته Stroke أو Fill واحدة، وهكذا تُبنى الزوايا المستديرة وخطوط الرسوم البيانية الناعمة
والتحديدات المقطّعة حالة، تمامًا مثل عرض الخط. تبدّل SetDash([3, 3], 0) كل تحديد لاحق إلى نمط ثلاث نقاط تشغيل وثلاث نقاط إيقاف، إذ تحدد المصفوفة أطوال مقاطع التشغيل/الإيقاف بالنقاط ويضبط الوسيط الثاني طور بداية الدورة؛ وتعيد NoDash القلم إلى خط متصل. اضبطه، وحدّد خطوط الشبكة التي تريده، وأعد ضبطه قبل الخط المتصل التالي، وإلا أصاب التقطيع بهدوء كل ما يليه
وتمر الشفافية عبر حالة رسوميات مسمّاة لا عبر وسيط لون، لأن ألفا في PDF خاصية لقاموس حالة الرسوميات. سجّل واحدة على المستند بـ RegisterExtGState، ممرّرًا ألفا تعبئة وألفا تحديد بين 0 و1، ثم طبّق الاسم الذي تُعيده بـ CurrentPage.SetGraphicsState؛ وترسم التعبئات والتحديدات من تلك النقطة فصاعدًا بالعتامة المسجَّلة. إنها طقوس أثقل من ضابطات الألوان، وتستحق ذلك في أول مرة يضطر فيها شريط تمييز إلى الجلوس فوق نص من دون إخفائه
والعادة المتبقية الجديرة بالحفاظ عليها هي التحقق. فالهندسة المولَّدة قد تنجح على جهازك وتفشل على جهاز عميل، عادةً بسبب استبدال الخط في أي نص تمزجه أو افتراض حجم صفحة لا يصمد. افتح الملف المكتمل عند بضعة مستويات تكبير لتأكيد بقاء الحواف نظيفة، وتحقق من أن كل شكل يحط داخل صندوق الهوامش الذي قصدته. وبمخطط ألوان حتمي، يمكن أتمتة ذلك الفحص مقابل ملف PDF مرجعي بدلًا من الفحص بالعين
واستدعاءات MoveTo وLineTo وStroke وFill والألوان المعروضة هنا جزء من HotPDF Delphi Component لـ Delphi و C++Builder