| categories |
|
|||||
|---|---|---|---|---|---|---|
| date | 2026-02-18 | |||||
| description | تعلم كيفية إضافة قائمة منسدلة إلى نماذج PDF في Java باستخدام GroupDocs.Annotation. يغطي هذا الدليل حقول نماذج PDF في Java، الإعداد، أمثلة الشيفرة، استكشاف الأخطاء وإصلاحها، وأفضل الممارسات. | |||||
| keywords | Java PDF dropdown tutorial, create interactive PDF forms Java, PDF form fields Java, GroupDocs annotation dropdown, how to add dropdown to PDF Java | |||||
| lastmod | 2026-02-18 | |||||
| linktitle | Java PDF Dropdown Tutorial | |||||
| tags |
|
|||||
| title | كيفية إضافة قائمة منسدلة إلى نماذج PDF في جافا – إنشاء نماذج تفاعلية باستخدام GroupDocs | |||||
| type | docs | |||||
| url | /ar/java/form-field-annotations/create-pdf-dropdowns-groupdocs-annotation-java/ | |||||
| weight | 1 |
هل واجهت صعوبة في إنشاء نماذج PDF تفاعلية باستخدام Java؟ لست وحدك. كثير من المطورين يجدون أنفسهم يتعاملون مع مكتبات PDF معقدة إما تفتقر إلى الوثائق أو تتطلب منحنيات تعلم حادة. هنا يأتي دور GroupDocs.Annotation for Java – فهو كأداة سويسريّة متعددة الاستخدامات لمعالجة ملفات PDF.
في هذا الدليل الشامل، ستكتشف كيفية إضافة قائمة منسدلة إلى نماذج PDF الخاصة بـ Java باستخدام GroupDocs.Annotation. سواءً كنت تبني نماذج استبيانات، أنظمة طلبات، أو سير عمل للموافقات، سيوجهك هذا الدليل من الإعداد الأساسي إلى تقنيات التحسين المتقدمة.
ما ستتعلمه:
- إعداد GroupDocs.Annotation في مشروع Java الخاص بك (بالطريقة الصحيحة)
- إنشاء مكوّنات القوائم المنسدلة مع أمثلة واقعية
- استكشاف الأخطاء الشائعة التي تُعرقل معظم المطورين
- حيل تحسين الأداء التي يمكن أن توفر لك ساعات من التصحيح
- أفضل الممارسات لإنشاء نماذج PDF جاهزة للإنتاج
- ما هي المكتبة الأفضل لإضافة القوائم المنسدلة في ملفات PDF باستخدام Java؟ GroupDocs.Annotation توفر واجهة برمجة تطبيقات بسيطة لحقول نماذج PDF في Java.
- هل أحتاج إلى ترخيص للتطوير؟ النسخة التجريبية المجانية تكفي للاختبار؛ يلزم ترخيص إنتاج للاستخدام التجاري.
- هل يمكنني وضع القائمة المنسدلة في أي مكان على الصفحة؟ نعم – استخدم طريقة
setBoxمع إحداثيات PDF (الأصل في الزاوية السفلية‑اليسرى). - كيف أتجنب مشاكل الذاكرة مع ملفات PDF الكبيرة؟ استخدم
try‑with‑resources، عالج الملفات واحدةً تلو الأخرى، وزد حجم ذاكرة JVM إذا لزم الأمر. - هل يمكن تحميل الخيارات من قاعدة بيانات؟ بالتأكيد – املأ قائمة الخيارات ديناميكياً قبل استدعاء
setOptions.
القائمة المنسدلة في PDF هي في الأساس حقل نموذج يعرض قائمة مسبقة التعريف من الخيارات، مشابه لعنصر HTML <select>. تقوم GroupDocs.Annotation بتجريد تفاصيل PDF منخفضة المستوى، مما يتيح لك التركيز على منطق الأعمال الخاص بـ حقول نماذج PDF في Java.
قبل أن نبدأ كتابة الكود، قد تتساءل: "لماذا GroupDocs بدلاً من مكتبات PDF الأخرى؟" الحقيقة أنني جربت عدة مكتبات PDF، وGroupDocs تحقق التوازن المثالي بين القوة والبساطة.
المميزات الرئيسية:
- واجهة برمجة تطبيقات بديهية: على عكس بعض المكتبات التي تتطلب فهمًا عميقًا لبنية PDF، تقوم GroupDocs بتجريد التعقيد.
- دعم غني للتعليقات التوضيحية: إلى جانب القوائم المنسدلة، تحصل على حقول نصية، مربعات اختيار، توقيعات، وأكثر.
- توافق متعدد المنصات: تعمل بسلاسة عبر أنظمة تشغيل مختلفة.
- مجتمع نشط: منتدى دعم قوي وتحديثات دورية.
- مرونة الترخيص: توفر كلًا من الخيارات التجريبية والمؤسسية.
- مجموعة تطوير جافا (JDK): الإصدار 8 أو أعلى (يفضل JDK 11+).
- Maven: لإدارة التبعيات (Gradle يعمل أيضًا، لكن المثال يوضح Maven).
- IDE: IntelliJ IDEA، Eclipse، أو VS Code مع إضافات Java.
- معرفة أساسية بجافا: فهم الفئات، الكائنات، واستخدام
try‑with‑resources.
أضف GroupDocs.Annotation إلى مشروعك بإدراج ما يلي في ملف pom.xml الخاص بك:
<repositories>
<repository>
<id>repository.groupdocs.com</id>
<name>GroupDocs Repository</name>
<url>https://releases.groupdocs.com/annotation/java/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.groupdocs</groupId>
<artifactId>groupdocs-annotation</artifactId>
<version>25.2</version>
</dependency>
</dependencies>نصيحة احترافية: تحقق دائمًا من أحدث نسخة على موقع GroupDocs. استخدام نسخ قديمة قد يسبب مشاكل توافق وغياب ميزات.
للتعلم/الاختبار:
- حمّل النسخة التجريبية المجانية من GroupDocs Free Trial
- النسخة التجريبية تتضمن علامات مائية لكنها توفر جميع الوظائف.
للإنتاج:
- زر صفحة Purchase Page للحصول على تراخيص دائمة.
- هل تحتاج اختبارًا في بيئة الإنتاج؟ احصل على Temporary License.
هذا هو الأساس الذي ستستخدمه في جميع عمليات GroupDocs:
try (final Annotator annotator = new Annotator("YOUR_DOCUMENT_DIRECTORY/input.pdf")) {
// Your annotation magic happens here
// The try-with-resources ensures proper cleanup
}لماذا هذا النمط مهم: جملة try-with-resources تغلق الـ annotator تلقائيًا، مما يمنع تسرب الذاكرة – مشكلة شائعة عند العمل مع مكتبات PDF.
قبل كتابة الكود، دعنا نفهم ما سنبنيه. مكوّن القائمة المنسدلة في PDF هو حقل نموذج يعرض للمستخدمين قائمة مسبقة التعريف من الخيارات. فكر فيه كعنصر HTML <select>، لكن مدمج مباشرةً داخل مستند PDF.
حالات الاستخدام الشائعة:
- اختيار الدولة/الولاية في النماذج
- فئات المنتجات في نماذج الطلب
- تحديثات الحالة في مستندات سير العمل
- مقاييس التقييم في نماذج الاستبيان
ابدأ بإعداد معالج المستند الخاص بك:
try (final Annotator annotator = new Annotator("YOUR_DOCUMENT_DIRECTORY/input.pdf")) {
// We'll build our dropdown here
}ملاحظة مهمة: استبدل "YOUR_DOCUMENT_DIRECTORY/input.pdf" بالمسار الفعلي لملف PDF الخاص بك. الخطأ الشائع هو استخدام مسارات نسبية تتعطل عند تشغيل البرنامج من مجلدات مختلفة.
هنا يبدأ السحر:
// Create a new DropdownComponent object
dropdownComponent = new DropdownComponent();هذا ينشئ مكوّن قائمة منسدلة فارغ. فكّر فيه كإنشاء حقل نموذج فارغ سنقوم بتكوينه في الخطوات التالية.
الآن سنملأ القائمة بالخيارات القابلة للاختيار:
dropdownComponent.setOptions(new ArrayList<>(Arrays.asList("Item1", "Item2", "Item3")));مثال واقعي: لاستبيان رضا العملاء، قد تستخدم:
dropdownComponent.setOptions(new ArrayList<>(Arrays.asList(
"Very Satisfied",
"Satisfied",
"Neutral",
"Dissatisfied",
"Very Dissatisfied"
)));حدد أين تظهر القائمة على الصفحة:
dropdownComponent.setBox(new Rectangle(100, 100, 50, 20)); // x, y, width, heightفهم الإحداثيات: إحداثيات PDF تبدأ من الزاوية السفلية‑اليسرى (على عكس HTML التي تبدأ من الزاوية العليا‑اليسرى). لذا فإن (100, 100) يعني 100 نقطة إلى اليمين و100 نقطة إلى الأعلى من الزاوية السفلية‑اليسرى.
نصائح حول الحجم:
- يجب أن يكون العرض كافيًا لأطول نص في الخيارات.
- ارتفاع 20‑25 نقطة عادةً ما يناسب النص العادي.
- جرّب قيمًا مختلفة لتجد الأنسب في مستندك.
أخيرًا، أدمج القائمة المنسدلة في المستند:
annotator.add(dropdownComponent);
// Save changes to a new file or overwrite the existing one
annotator.save("YOUR_DOCUMENT_DIRECTORY/output.pdf");أفضل ممارسة: احفظ دائمًا إلى اسم ملف مختلف أثناء التطوير. بهذه الطريقة يمكنك مقارنة النتائج وتجنب إتلاف الملف الأصلي عن طريق الخطأ.
إليك كل شيء موحدًا في مثال كامل وقابل للتنفيذ:
import com.groupdocs.annotation.Annotator;
import com.groupdocs.annotation.models.annotationmodels.DropdownComponent;
import com.groupdocs.annotation.models.Rectangle;
import java.util.ArrayList;
import java.util.Arrays;
public class PDFDropdownExample {
public static void main(String[] args) {
try (final Annotator annotator = new Annotator("input.pdf")) {
// Create dropdown component
DropdownComponent dropdownComponent = new DropdownComponent();
// Set dropdown options
dropdownComponent.setOptions(new ArrayList<>(Arrays.asList(
"Priority: High",
"Priority: Medium",
"Priority: Low"
)));
// Position the dropdown
dropdownComponent.setBox(new Rectangle(150, 300, 120, 25));
// Add to document and save
annotator.add(dropdownComponent);
annotator.save("output_with_dropdown.pdf");
System.out.println("Dropdown successfully added to PDF!");
} catch (Exception e) {
System.err.println("Error creating dropdown: " + e.getMessage());
}
}
}المشكلة: يرمى الكود استثناء FileNotFoundException رغم وجود الملف.
الحل:
// Instead of relative paths like this:
new Annotator("input.pdf")
// Use absolute paths or properly constructed relative paths:
new Annotator(System.getProperty("user.dir") + "/documents/input.pdf")
// Or use Path.resolve() for more robust path handlingالمشكلة: تظهر القائمة في مكان غير متوقع على PDF.
السبب الجذري: ارتباك نظام إحداثيات PDF.
الحل:
- تذكّر أن (0,0) هو الزاوية السفلية‑اليسرى في PDFs، وليس العليا‑اليسرى.
- استخدم عارض PDF يُظهر الإحداثيات لتحديد المواقع بدقة.
- ابدأ بقيم إحداثيات أكبر ثم قلّصها تدريجيًا.
المشكلة: يعمل الكود في بيئة التطوير لكنه يفشل في الإنتاج بسبب أخطاء الترخيص.
الإصلاحات السريعة:
- تأكد من وجود ملف الترخيص في classpath.
- تحقق من تواريخ انتهاء الترخيص.
- تأكد من أن الترخيص يتطابق مع بيئة النشر (ترخيص تطوير ≠ ترخيص إنتاج).
المشكلة: OutOfMemoryError عند معالجة مستندات ضخمة.
الحلول:
// Set JVM memory parameters
// -Xmx2g -Xms1g
// Process documents in batches if possible
// Dispose of annotator objects properly (use try-with-resources)public void createFeedbackForm(String inputPdf, String outputPdf) {
try (final Annotator annotator = new Annotator(inputPdf)) {
// Department selection dropdown
DropdownComponent deptDropdown = new DropdownComponent();
deptDropdown.setOptions(new ArrayList<>(Arrays.asList(
"Engineering", "Marketing", "Sales", "HR", "Finance"
)));
deptDropdown.setBox(new Rectangle(200, 500, 100, 25));
// Performance rating dropdown
DropdownComponent ratingDropdown = new DropdownComponent();
ratingDropdown.setOptions(new ArrayList<>(Arrays.asList(
"Exceeds Expectations", "Meets Expectations", "Below Expectations"
)));
ratingDropdown.setBox(new Rectangle(200, 450, 150, 25));
annotator.add(deptDropdown);
annotator.add(ratingDropdown);
annotator.save(outputPdf);
} catch (Exception e) {
log.error("Failed to create feedback form: {}", e.getMessage());
}
}هذا المثال يوضح كيفية ملء خيارات القائمة من قاعدة بيانات:
public void createOrderForm(String inputPdf, List<String> products) {
try (final Annotator annotator = new Annotator(inputPdf)) {
DropdownComponent productDropdown = new DropdownComponent();
// Add a default option
List<String> options = new ArrayList<>();
options.add("-- Select Product --");
options.addAll(products);
productDropdown.setOptions(options);
productDropdown.setBox(new Rectangle(150, 400, 200, 25));
annotator.add(productDropdown);
annotator.save("order_form_" + System.currentTimeMillis() + ".pdf");
} catch (Exception e) {
throw new RuntimeException("Order form creation failed", e);
}
}عند معالجة عدة ملفات PDF أو مستندات كبيرة، تصبح إدارة الذاكرة أمرًا حاسمًا:
// Good: Process documents one at a time
for (String pdfFile : pdfFiles) {
try (final Annotator annotator = new Annotator(pdfFile)) {
// Process individual file
addDropdowns(annotator);
annotator.save(getOutputPath(pdfFile));
} // Annotator automatically closed here
}
// Avoid: Creating multiple annotators simultaneously
// This can quickly exhaust memoryللحالات ذات الحجم العالي:
public void processBatch(List<String> pdfFiles, int batchSize) {
for (int i = 0; i < pdfFiles.size(); i += batchSize) {
List<String> batch = pdfFiles.subList(i,
Math.min(i + batchSize, pdfFiles.size()));
processBatchOfFiles(batch);
// Force garbage collection between batches
System.gc();
}
}إذا كنت تعالج مستندات متشابهة بشكل متكرر:
// Cache dropdown configurations
private static final Map<String, List<String>> DROPDOWN_OPTIONS = Map.of(
"countries", Arrays.asList("USA", "Canada", "UK", "Germany"),
"priorities", Arrays.asList("High", "Medium", "Low")
);
public DropdownComponent createStandardDropdown(String type, Rectangle position) {
DropdownComponent dropdown = new DropdownComponent();
dropdown.setOptions(new ArrayList<>(DROPDOWN_OPTIONS.get(type)));
dropdown.setBox(position);
return dropdown;
}على الرغم من أن GroupDocs.Annotation يركز على الوظيفة أكثر من التخصيص البصري، يمكنك التحكم في المظهر إلى حد ما:
dropdownComponent.setBox(new Rectangle(100, 100, 150, 30)); // Wider for better readability
// The library handles font and color based on PDF defaultsأحيانًا تحتاج قوائم منسدلة فقط تحت ظروف معينة:
public void addConditionalDropdowns(Annotator annotator, DocumentType docType) {
if (docType == DocumentType.SURVEY) {
addSurveyDropdowns(annotator);
} else if (docType == DocumentType.ORDER_FORM) {
addOrderDropdowns(annotator);
}
}بينما يتولى GroupDocs إنشاء القوائم، قد ترغب في التحقق من صحة ملفات PDF بعد الإنشاء:
public boolean validateDropdownsAdded(String pdfPath) {
try (final Annotator annotator = new Annotator(pdfPath)) {
// Check if annotations were added successfully
return annotator.get().size() > 0;
} catch (Exception e) {
return false;
}
}فعّل تسجيل مفصل لتشخيص المشكلات:
// Add this to your logging configuration
Logger.getLogger("com.groupdocs").setLevel(Level.DEBUG);| Exception | Likely Cause | Solution |
|---|---|---|
FileNotFoundException |
مسار الملف غير صحيح | استخدم مسارات مطلقة أو تحقق من منطق المسارات النسبية |
InvalidLicenseException |
مشاكل الترخيص | تحقق من موقع ملف الترخيص وتاريخ الانتهاء |
OutOfMemoryError |
معالجة ملف كبير | زد حجم heap للـ JVM أو عالج الملفات على دفعات |
UnsupportedOperationException |
قيود PDF | تأكد من أن PDF يسمح بالتعديلات |
أنشئ اختبارًا بسيطًا للتحقق من أن كل شيء يعمل:
@Test
public void testDropdownCreation() {
String inputFile = "test-input.pdf";
String outputFile = "test-output.pdf";
try (final Annotator annotator = new Annotator(inputFile)) {
DropdownComponent dropdown = new DropdownComponent();
dropdown.setOptions(Arrays.asList("Test1", "Test2"));
dropdown.setBox(new Rectangle(100, 100, 80, 20));
annotator.add(dropdown);
annotator.save(outputFile);
// Verify output file exists and has content
assertTrue(Files.exists(Paths.get(outputFile)));
assertTrue(Files.size(Paths.get(outputFile)) > 0);
}
}طبق معالجة أخطاء قوية لبيئات الإنتاج:
public class PDFDropdownService {
private static final Logger logger = LoggerFactory.getLogger(PDFDropdownService.class);
public Result<String> addDropdownToPDF(String inputPath, DropdownConfig config) {
try (final Annotator annotator = new Annotator(inputPath)) {
DropdownComponent dropdown = createDropdownFromConfig(config);
annotator.add(dropdown);
String outputPath = generateOutputPath(inputPath);
annotator.save(outputPath);
logger.info("Successfully added dropdown to PDF: {}", outputPath);
return Result.success(outputPath);
} catch (Exception e) {
logger.error("Failed to add dropdown to PDF: {}", e.getMessage(), e);
return Result.error("PDF processing failed: " + e.getMessage());
}
}
}استخدم ملفات تكوين لتخزين خيارات القوائم المنسدلة:
# dropdown-config.yml
dropdowns:
priority:
options: ["High", "Medium", "Low"]
position: {x: 100, y: 200, width: 80, height: 25}
status:
options: ["New", "In Progress", "Completed"]
position: {x: 200, y: 200, width: 100, height: 25}تهانينا! لقد أتقنت الآن كيفية إضافة قائمة منسدلة إلى نماذج PDF التفاعلية باستخدام GroupDocs.Annotation for Java. تعلمت كل شيء من الإعداد الأساسي إلى تقنيات التحسين المتقدمة التي ستفيدك في بيئات الإنتاج.
- الإعداد سهل: دمج Maven والترخيص أبسط من معظم مكتبات PDF.
- الكود بديهي: تصميم API منطقي ويتبع تقاليد Java.
- الأداء مهم: إدارة الموارد بشكل صحيح تمنع مشاكل الذاكرة.
- الاختبار ضروري: تحقق دائمًا من عمل ملفات PDF عبر عارضات مختلفة.
الآن بعد أن أصبحت القوائم المنسدلة تحت سيطرتك، استكشف الميزات المتقدمة التالية:
- تعليقات نصية – مثالية لإدخال المستخدم الحر.
- مربعات اختيار – رائعة للاختيارات الثنائية.
- حقول توقيع – أساسية لسير عمل الموافقات.
- إضافة علامات مائية – لتأمين مستنداتك بشكل احترافي.
- مقارنة المستندات – لتتبع التغييرات بين الإصدارات.
استفد من هذه الموارد لتعميق معرفتك بـ GroupDocs:
- Official Documentation – أدلة شاملة ومراجع API
- Community Forum – احصل على مساعدة من مطورين آخرين
- Sample Projects – أمثلة تطبيقية من العالم الحقيقي
تذكر أن أفضل طريقة لإتقان أي تقنية هي بناء شيء عملي بها. ابدأ بمشروع بسيط – ربما نموذج ملاحظات لفريقك أو استبيان أساسي – ثم أضف تعقيدات تدريجية كلما ارتحت مع الـ API.
هل لديك أسئلة أو تواجه مشاكل؟ مجتمع GroupDocs متعاون للغاية، والوثائق واضحة (نعم، نادرًا ما تكون وثائق أدوات التطوير هكذا!).
برمجة سعيدة، ولتظل ملفات PDF الخاصة بك تفاعلية إلى الأبد! 🚀
GroupDocs.Annotation for Java مكتبة شاملة تتيح لك إضافة أنواع مختلفة من التعليقات التوضيحية إلى المستندات، بما في ذلك PDFs. فكر فيها كصندوق أدوات يجعل المستندات الثابتة تفاعلية – يمكنك إضافة قوائم منسدلة، حقول نصية، مربعات اختيار، توقيعات، وأكثر دون الحاجة لفهم بنية PDF المعقدة.
الأمر أبسط مما تتوقع! إذا كنت تستخدم Maven، يكفي إضافة المستودع والاعتماد إلى pom.xml. يستغرق الإعداد بالكامل حوالي 5 دقائق. الجزء الأكثر تعقيدًا عادةً هو ضبط الترخيص، لكن حتى ذلك موثّق جيدًا.
بالطبع! يدعم GroupDocs مجموعة واسعة من الصيغ بما فيها مستندات Word، جداول Excel، عروض PowerPoint، وأنواع متعددة من الصور. تبقى واجهة البرمجة متسقة عبر الصيغ، لذا إذا تعلمتها للـ PDFs يمكنك تطبيقها بسهولة على صيغ أخرى.
غالبًا ما يكون السبب ارتباك نظام الإحداثيات. تذكّر أن PDFs تستخدم أصلًا في الزاوية السفلية‑اليسرى (على عكس صفحات الويب التي تبدأ من الأعلى‑اليسار). ابدأ بقيم Y أكبر ثم قلّصها تدريجيًا. استخدم عارض PDF يُظهر الإحداثيات لتحديد الموضع بدقة.
نعم! تقدم GroupDocs نسخة تجريبية مجانية تشمل جميع الوظائف. القيد الوحيد هو وجود علامة مائية على المستندات المعالجة. هذا يكفي للتطوير والاختبار قبل شراء ترخيص إنتاج.
استخدم نمط try‑with‑resources بانتظام – فهو يضمن تحرير الموارد تلقائيًا. للمعالجة الدفعية، عالج ملفًا واحدًا في كل مرة بدلاً من تحميل عدة ملفات في الذاكرة. قد تحتاج أيضًا إلى زيادة حجم heap للـ JVM (-Xmx) حسب حجم ملفاتك.
تركّز GroupDocs أكثر على الوظيفة مقارنةً بالتخصيص البصري. القوائم المنسدلة تتبع التنسيق الافتراضي للـ PDF. يمكنك التحكم في الحجم والموقع بدقة، لكن إذا كنت تحتاج إلى تخصيص بصري متقدم قد تحتاج إلى مكتبة PDF أكثر تخصصًا. ومع ذلك، التنسيق الافتراضي يكفي لمعظم التطبيقات التجارية.
منتدى الدعم الخاص بـ GroupDocs Support Forum نشط جدًا ومفيد. يشارك فيه كل من المستخدمين وموظفي GroupDocs الذين يردون بسرعة. كما أن الوثائق الرسمية جيدة، لذا ابدأ بالتحقق منها أولًا.
الأمر الأساسي هو الفرق بين تراخيص التطوير والإنتاج. تأكد من أن الترخيص يتطابق مع بيئة النشر الخاصة بك. تراخيص التجربة مؤقتة ولها تواريخ انتهاء – لا تدعها تنتهي أثناء تشغيل الإنتاج.
GroupDocs يركز على التعليقات التوضيحية وحقول النماذج، بينما iText أكثر شمولية لإنشاء وتعديل PDF. توفر GroupDocs واجهة أبسط لمهام التعليق، لكن iText يمنح مرونة أكبر لإنشاء مستندات PDF من الصفر. إذا كان هدفك الأساسي هو إضافة عناصر تفاعلية إلى ملفات PDF موجودة، فإن GroupDocs عادةً ما يكون الخيار الأنسب.
- GroupDocs Documentation - وثائق API كاملة ودروس تعليمية
- API Reference - مراجع مفصلة للطرق والفئات
- Download Center - أحدث الإصدارات والنسخ التجريبية
- Purchase Options - معلومات الترخيص والأسعار
- Free Trial - تجربة كاملة للوظائف
- Temporary License - ترخيص قصير الأمد للتقييم
- Support Forum - مساعدة المجتمع والدعم الرسمي
آخر تحديث: 2026-02-18
تم الاختبار مع: GroupDocs.Annotation 25.2
المؤلف: GroupDocs