تخصيص أشكال العرض التقديمي في .NET

نظرة عامة

تشرح هذه المقالة كيفية تخصيص أشكال العرض التقديمي في Aspose.Slides عن طريق تحرير هندسة الشكل عبر نقاط التحرير ومسارات الهندسة. تُظهر كيفية العمل مع GeometryPath وIGeometryPath لتعديل الأشكال الموجودة، وإجراء عمليات تحرير أساسية للمسار، وإضافة أو إزالة نقاط، وتطبيق الهندسة المحدثة على شكل.

كما توضح كيفية إنشاء أشكال مخصصة ومركبة، وبناء أشكال ذات زوايا منحنية، وتحديد ما إذا كانت هندسة الشكل مغلقة، واستخدام التحويل القديم بين GeometryPath وGraphicsPath لمزيد من سيناريوهات تخصيص الهندسة.

تغيير شكل باستخدام نقاط التحرير

في PowerPoint، يمكنك استخدام نقاط التحرير لتعديل مربع:

  • تحريك زاوية إلى الداخل أو الخارج.
  • تعديل الانحناء عند نقطة.
  • إضافة نقاط جديدة.

تتيح لك تحرير النقاط تعديل مخطط الشكل أو استخدام شكل موجود كأساس لخلق شكل جديد.

نصائح تحرير الشكل

overview_image

عند تحرير أشكال PowerPoint، احرص على هذه المفاهيم:

  • الشكل (أو مساره) يمكن أن يكون مغلقًا أو مفتوحًا.
  • يتكون مسار الشكل من نقاط ربط متصلة بقطاعات.
  • يمكن أن تكون القطاعات مستقيمة أو منحنية. تحدد نقاط الربط ومقابض التحكم شكلها.
  • توجد نقاط الربط كنقاط زاوية أو نقاط مستقيمة أو نقاط ناعمة:
    • عند نقطة زاوية، يمكن للقطاعات المتجاورة أن تلتقي بزاوية.
    • عند نقطة ناعمة، يكون مقبضان التحكم محاذيين وعلى مسافة متساوية من نقطة الربط.
    • عند نقطة مستقيمة، يكون مقبضان التحكم محاذيين لكن قد تكون المسافة منهما إلى نقطة الربط مختلفة.
  • تحريك نقاط الربط أو تعديل مقابضها يغيّر مخطط الشكل.

لتحرير أشكال PowerPoint عبر نقاط التحرير، توفر Aspose.Slides الفئة GeometryPath والواجهة IGeometryPath.

عمليات التحرير البسيطة

استخدم الطرق التالية لتحرير مسار هندسي. التحميلات التي تقبل فهرس تُدرج قطاعًا في ذلك الموضع الصفري في مصفوفة قطاعات المسار.

العملية الطريقة
إضافة أو إدراج خط مستقيم. GeometryPath.LineTo
إضافة أو إدراج منحنى بيزير مكعّب مع نقطتي تحكم ونقطة نهائية. GeometryPath.CubicBezierTo
إضافة أو إدراج منحنى بيزير رباعي مع نقطة تحكم واحدة ونقطة نهائية. GeometryPath.QuadraticBezierTo
إضافة قوس بيضاوي بالأبعاد والزوايا المحددة بالدرجات. GeometryPath.ArcTo
إغلاق الشكل الحالي بربط نقطته النهائية بنقطة البداية. GeometryPath.CloseFigure
تحديد نقطة البداية للشكل. GeometryPath.MoveTo
إزالة قطاع عند فهرس يبدأ من الصفر. GeometryPath.RemoveAt

إضافة نقاط مخصصة إلى شكل

يوضح هذا المثال إدراج نقطتين في شكل مستطيل باستخدام GeometryPath.LineTo وتطبيق المسار المعدل عبر IGeometryShape.SetGeometryPath. الإحداثيات محلية للشكل: النقطتان الجديدتان في مركزه، على بعد 100 نقطة من اليسار و50 نقطة من الأعلى. مؤشرات القطاعات 1 و4 تضعهما على الحافتين العلوية والسفلية، سحباً لتلك الحواف إلى الداخل.

using Aspose.Slides;

using var presentation = new Presentation();
var slide = presentation.Slides[0];

var shape = slide.Shapes.AddAutoShape(ShapeType.Rectangle, 100, 100, 200, 100);

var geometryPath = shape.GetGeometryPaths()[0];
geometryPath.LineTo(100, 50, 1);
geometryPath.LineTo(100, 50, 4);

shape.SetGeometryPath(geometryPath);

example1_image

إزالة نقاط من شكل

يغيّر هذا المثال مخطط شكل على شكل قلب بإزالة القطاع عند الفهرس الصفري 2 باستخدام GeometryPath.RemoveAt. تطبيق المسار المعدل عبر IGeometryShape.SetGeometryPath يحدث حدود الشكل.

using Aspose.Slides;

using var presentation = new Presentation();
var slide = presentation.Slides[0];

var shape = slide.Shapes.AddAutoShape(ShapeType.Heart, 100, 100, 300, 300);

var path = shape.GetGeometryPaths()[0];
path.RemoveAt(2);

shape.SetGeometryPath(path);

example2_image

إنشاء شكل مخصص

يبني هذا المثال نجمة خماسية باستخدام GeometryPath. تحدد أنصاف الأقطار الخارجية والداخلية 100 و50 نقطة لتحديد الأطراف والفتحات؛ خطوة 72 درجة تفرق الأطراف الخمسة بالتساوي. يُغلق المسار باستخدام GeometryPath.CloseFigure ويُطبق على شكل مستطيل عبر IGeometryShape.SetGeometryPath، مستبدلاً مخططه.

using System;
using System.Collections.Generic;
using System.Drawing;
using Aspose.Slides;

var points = new List<PointF>();

var outerRadius = 100f;
var innerRadius = 50f;
var angleStep = 72;

for (var angle = -90; angle < 270; angle += angleStep)
{
    var radians = angle * (Math.PI / 180f);
    var x = outerRadius * Math.Cos(radians);
    var y = outerRadius * Math.Sin(radians);
    points.Add(new PointF((float)x + outerRadius, (float)y + outerRadius));

    radians = Math.PI * (angle + angleStep / 2) / 180.0;
    x = innerRadius * Math.Cos(radians);
    y = innerRadius * Math.Sin(radians);
    points.Add(new PointF((float)x + outerRadius, (float)y + outerRadius));
}

var starPath = new GeometryPath();
starPath.MoveTo(points[0]);

for (var i = 1; i < points.Count; i++)
{
    starPath.LineTo(points[i]);
}

starPath.CloseFigure();

using var presentation = new Presentation();
var slide = presentation.Slides[0];

var shape = slide.Shapes.AddAutoShape(ShapeType.Rectangle, 100, 100, outerRadius * 2, outerRadius * 2);
shape.SetGeometryPath(starPath);

example3_image

إنشاء شكل مخصص مركب

يعّرف هذا المثال مسارين مغلقين من نوع GeometryPath للثلث العلوي والسفلي من الشكل. تطبيق المسارين عبر IGeometryShape.SetGeometryPaths يخلق شكلًا مركبًا واحدًا به فجوة عبر الثلث الأوسط.

using Aspose.Slides;

using var presentation = new Presentation();
var slide = presentation.Slides[0];

var shape = slide.Shapes.AddAutoShape(ShapeType.Rectangle, 100, 100, 200, 100);

var topPath = new GeometryPath();
topPath.MoveTo(0, 0);
topPath.LineTo(shape.Width, 0);
topPath.LineTo(shape.Width, shape.Height / 3);
topPath.LineTo(0, shape.Height / 3);
topPath.CloseFigure();

var bottomPath = new GeometryPath();
bottomPath.MoveTo(shape.Height / 3 * 2, 0);
bottomPath.LineTo(shape.Width, shape.Height / 3 * 2);
bottomPath.LineTo(shape.Width, shape.Height);
bottomPath.LineTo(0, shape.Height);
bottomPath.CloseFigure();

shape.SetGeometryPaths(new GeometryPath[] { topPath, bottomPath });

example4_image

إنشاء شكل مخصص بزوايا منحنية

يجمع هذا المثال قطاعات مستقيمة مع GeometryPath.ArcTo لإنشاء شكل بزوايا منحنية للداخل. يحدد كل حجم زاوية عرض وارتفاع القوس بالنقاط، وتدور القطعة -90 درجة لترسم ربع إهليلج عكس اتجاه عقارب الساعة. يطبق المسار المكتمل عبر IGeometryShape.SetGeometryPath، ويحفظ العرض التقديمي بصيغة PPTX.

using System.Drawing;
using Aspose.Slides;
using Aspose.Slides.Export;

var shapeX = 20f;
var shapeY = 20f;
var shapeWidth = 300f;
var shapeHeight = 200f;

var leftTopSize = 50f;
var rightTopSize = 20f;
var rightBottomSize = 40f;
var leftBottomSize = 10f;

using var presentation = new Presentation();
var slide = presentation.Slides[0];

var topLeft = new PointF(leftTopSize, 0);
var topRight = new PointF(shapeWidth - rightTopSize, 0);
var rightBottom = new PointF(shapeWidth, shapeHeight - rightBottomSize);
var bottomLeft = new PointF(leftBottomSize, shapeHeight);
var leftTop = new PointF(0, leftTopSize);

var geometryPath = new GeometryPath();
geometryPath.MoveTo(topLeft);
geometryPath.LineTo(topRight);
geometryPath.ArcTo(rightTopSize, rightTopSize, 180, -90);
geometryPath.LineTo(rightBottom);
geometryPath.ArcTo(rightBottomSize, rightBottomSize, -90, -90);
geometryPath.LineTo(bottomLeft);
geometryPath.ArcTo(leftBottomSize, leftBottomSize, 0, -90);
geometryPath.LineTo(leftTop);
geometryPath.ArcTo(leftTopSize, leftTopSize, 90, -90);
geometryPath.CloseFigure();

var shape = slide.Shapes.AddAutoShape(ShapeType.Custom, shapeX, shapeY, shapeWidth, shapeHeight);
shape.SetGeometryPath(geometryPath);

presentation.Save("output.pptx", SaveFormat.Pptx);

اكتشاف ما إذا كانت هندسة الشكل مغلقة

يتحقق المساعد التالي ما إذا كان كل مسار هندسي غير فارغ ينتهي بقطاع إغلاق. يقرأ IGeometryPath.PathData ويقارن أمر القطاع النهائي IPathSegment.PathCommand بـPathCommandType.Close. يُعيد false عندما لا توجد مسارات غير فارغة. يتحقق من الأمر النهائي لكل مسار؛ لا يفحص كل شكل فرعي داخل المسار. يطبع المثال True لمستطيل.

using System;
using Aspose.Slides;

using var presentation = new Presentation();
var slide = presentation.Slides[0];

var shape = slide.Shapes.AddAutoShape(ShapeType.Rectangle, 100, 100, 200, 100);
Console.WriteLine(IsGeometryClosed(shape));

bool IsGeometryClosed(IGeometryShape geometryShape)
{
    var hasNonemptyPath = false;

    foreach (var geometryPath in geometryShape.GetGeometryPaths())
    {
        var segments = geometryPath.PathData;
        if (segments.Length == 0)
        {
            continue;
        }

        hasNonemptyPath = true;
        var lastSegment = segments[segments.Length - 1];
        if (lastSegment.PathCommand != PathCommandType.Close)
        {
            return false;
        }
    }

    return hasNonemptyPath;
}

استخراج وفحص عناصر الشكل

استخدم IGeometryShape.CreateShapeElements لفحص شكل موجود كعناصر تحتوي على نقاط المسار وأنواع النقاط. حول كل IShapeElement إلى ShapeElement لقراءة مصفوفات PathPoints وPathTypes. هذا التمثيل مفيد لتحليل الحدود أو تمرير الهندسة إلى أدوات رسم مخصصة.

يفتح المثال عرضًا تقديميًا، يجد أول شكل هندسي مباشرةً على الشريحة الأولى، ويطبع قطاعات مساره. استخدم عرضًا يحتوي على شكل بقطاعات مستقيمة ومنحنية لتشهد كلا النوعين من المخرجات. لا يبحث داخل المجموعات.

using System;
using System.Linq;
using Aspose.Slides;

using var presentation = new Presentation("input.pptx");
var slide = presentation.Slides[0];

var shape = slide.Shapes.OfType<IGeometryShape>().FirstOrDefault();
if (shape == null)
{
    Console.WriteLine("No geometry shape found on the first slide.");
    return;
}

foreach (IShapeElement element in shape.CreateShapeElements())
{
    var shapeElement = (ShapeElement)element;
    var points = shapeElement.PathPoints;
    var types = shapeElement.PathTypes;
    Console.WriteLine("Shape element:");

    for (var i = 0; i < points.Length; i++)
    {
        var pointType = types[i] & 0x07; // احتفظ بنوع القطعة، مع استبعاد العلامات.
        switch (pointType)
        {
            case 0:
                Console.WriteLine($"Start figure at {points[i]}");
                break;
            case 1:
                Console.WriteLine($"Line to {points[i]}");
                break;
            case 3:
                Console.WriteLine($"Cubic Bezier: controls {points[i]}, {points[i + 1]}; end {points[i + 2]}");
                i += 2; // استهلك كل من نقاط التحكم والنقطة النهائية.
                break;
        }

        if ((types[i] & 0x80) != 0)
        {
            Console.WriteLine("Close subpath back to its starting point.");
        }
    }
}

المصفوفات متطابقة وفق الفهرس: points[i] هي نقطة وtypes[i] يصف دورها. النوع 0 يبدأ شكلًا، والنوع 1 يرسم خطًا من النقطة السابقة إلى الحالية. يبدأ قطاع بيزير مكعّب من النقطة السابقة ويستخدم ثلاثة مدخلات نوع 3 متتالية: نقطتي تحكم ثم النقطة النهائية الجديدة. يستهلك الحلقة هذه الثلاث مدخلات معًا ويفحص النقطة النهائية للإغلاق.

تحتوي أنواع النقاط أيضًا على أعلام. استخدم القناع 0x07 لتحديد نوع القطاع، واختبر 0x80 منفصلًا لاكتشاف مسار فرعي مغلق. على سبيل المثال، 129 (0x81) يعني نقطة نهائية لخط تغلق مسارًا فرعيًا، و131 (0x83) يعني نقطة نهائية لبيزير تغلق واحدًا. مقارنة القيمة الخام فقط مع 1 أو 3 سيفوت هذه الحالات. الأعلام الأخرى مثل الشرط (0x10) والعلامة (0x20) لا تغير النوع الأساسي للقطاع.

الإحداثيات والتحويلات. تُقاس النقاط بوحدات النقطة (1/72 بوصة)، مع زيادة X إلى اليمين وزيادة Y إلى الأسفل. بالنسبة لشكل غير مجمع، تشمل الإحداثيات المستخرجة موقعه X وY وتعكس عرضه وارتفاعه الحالي. على سبيل المثال، مستطيل غير مدوّر عند (100, 200) بحجم 120 × 80 له حدود الحد الخارجي (100, 200)–(220, 280). لا تُضف موقع الشكل مرة أخرى. اطرح X وY للحصول على إحداثيات نسبية لإطار غير مدوّر.

النقاط المستخرجة لا تُطبق دوران الشكل Rotation أو الانعكاسات الأفقية والرأسية. لت reproducing الشكل المعروض، طبّق تلك التحويلات حول مركز الشكل. بالنسبة للأشكال المجمعة، احسب أيضًا تحويلات إحداثيات المجموعة؛ لا تفترض أن النقاط المستخرجة هي إحداثيات الشريحة النهائية أو تُطبق ترجمة أو مقياس مرتين إذا كان قد تم تضمينه بالفعل في إطار العنصر الفرعي.

الفحص مقابل التحرير. تصف المصفوفات المستخرجة الحد؛ تعديلها لا يُحدّث الشكل الأصلي. اختر هذا النهج عندما يحتاج تحليلك أو كود الرسم إلى مصفوفات النقاط/الأنواع. استخدم GetGeometryPaths عندما تحتاج إلى أوامر هندسية في نظام إحداثيات الشكل المحلي أو تريد تحرير هندسته. طبق المسارات المعدلة عبر SetGeometryPath أو SetGeometryPaths.

تحويل GeometryPath إلى GraphicsPath (System.Drawing.Drawing2D)

يتطلب المثال التالي تجميعة قديمة لـ Aspose.Slides for .NET تدعم ShapeUtil.GraphicsPathToGeometryPath، مثل الإصدار 25.10، وبيئة Windows مع System.Drawing.Common. أُزيلت طرق التحويل في الإصدار 25.11.

يجمع هذا المثال مخططًا مستطيليًا مع نص محوّل إلى هندسة. يُضبط IGeometryPath.FillMode الخاص بالمخطط لإلغاء تعبئته. يخزن GraphicsPath مخطط النص، والذي يحوله ShapeUtil.GraphicsPathToGeometryPath إلى مسار هندسي مُعبأ. تطبيق كلا المسارين عبر IGeometryShape.SetGeometryPaths ينتج شكلًا واحدًا يحتوي على المستطيل والنص. للتحويل في الاتجاه المعاكس، استخدم ShapeUtil.GeometryPathToGraphicsPath.

using System.Drawing;
using System.Drawing.Drawing2D;
using Aspose.Slides;
using Aspose.Slides.Util;

using var presentation = new Presentation();
var slide = presentation.Slides[0];

var shape = slide.Shapes.AddAutoShape(ShapeType.Rectangle, 100, 100, 300, 100);

var originalPath = shape.GetGeometryPaths()[0];
originalPath.FillMode = PathFillModeType.None;

using var graphicsPath = new GraphicsPath();
using var fontFamily = new FontFamily("Arial");
using var stringFormat = StringFormat.GenericDefault;

graphicsPath.AddString("Text in shape", fontFamily, (int)FontStyle.Bold, 40, new PointF(10, 10), stringFormat);

var textPath = ShapeUtil.GraphicsPathToGeometryPath(graphicsPath);
textPath.FillMode = PathFillModeType.Normal;

shape.SetGeometryPaths(new[] { originalPath, textPath });

example5_image

الأسئلة الشائعة

ماذا سيحدث للملء والحد بعد استبدال الهندسة؟

يبقى النمط مع الشكل؛ يتغير الحد فقط. يُطبق الملء والحد تلقائيًا على الهندسة الجديدة.

كيف يمكنني تدوير شكل مخصص مع هندسته بشكل صحيح؟

استخدم خاصية Rotation الخاصة بالشكل؛ تدور الهندسة مع الشكل لأنها مرتبطة بنظام إحداثيات الشكل نفسه.

هل يمكنني تحويل الشكل المخصص إلى صورة “لتثبيت” النتيجة؟

نعم. صدّر الـالشريحة المطلوبة أو الـالشكل نفسه إلى تنسيق نقطي؛ هذا يبسط العمل الإضافي مع الهندسات المعقدة.