Java गाइड: GroupDocs का उपयोग करके प्रोग्रामेटिकली PDF एनोटेट करें

क्यों आपको अपने Java ऐप्स में PDF एनोटेशन की आवश्यकता है

आइए ईमानदार रहें—सही टूल्स के बिना दस्तावेज़ समीक्षा और सहयोग का प्रबंधन एक दुःस्वप्न बन सकता है। चाहे आप एंटरप्राइज़ दस्तावेज़ प्रबंधन प्रणाली बना रहे हों या अपने Java एप्लिकेशन में PDFs में कुछ टिप्पणी जोड़नी हो, प्रोग्रामेटिक एनोटेशन एक गेम‑चेंजर है। यदि आप प्रोग्रामेटिकली PDF एनोटेट करना चाहते हैं, तो यह गाइड आपको न्यूनतम रुकावट के साथ इसे कैसे करना है, बिल्कुल दिखाता है।

इस व्यापक ट्यूटोरियल में, आप GroupDocs.Annotation का उपयोग करके Java PDF एनोटेशन में महारत हासिल करेंगे—जो उपलब्ध सबसे मजबूत दस्तावेज़ एनोटेशन लाइब्रेरीज़ में से एक है। अंत तक, आप जानेंगे कि स्ट्रीम्स से दस्तावेज़ कैसे लोड करें, विभिन्न प्रकार के एनोटेशन कैसे जोड़ें, और आम समस्याओं को कैसे संभालें जो अधिकांश डेवलपर्स को फँसाती हैं।

इस ट्यूटोरियल को क्या अलग बनाता है? हम वास्तविक‑दुनिया के परिदृश्यों पर ध्यान देंगे, न कि केवल बुनियादी उदाहरणों पर। आप गॉटचाज़, प्रदर्शन संबंधी विचारों, और प्रोडक्शन‑रेडी तकनीकों को सीखेंगे जो वास्तव में मायने रखते हैं।

तैयार हैं? चलिए शुरू करते हैं।

त्वरित उत्तर

  • Java में PDF को प्रोग्रामेटिकली एनोटेट करने वाली लाइब्रेरी कौन सी है? GroupDocs.Annotation.
  • क्या इसे आज़माने के लिए मुझे पेड लाइसेंस चाहिए? नहीं, एक फ्री ट्रायल विकास और परीक्षण के लिए काम करता है।
  • क्या मैं डेटाबेस या क्लाउड स्टोरेज से PDFs लोड कर सकता हूँ? हाँ—स्ट्रीम‑आधारित लोडिंग का उपयोग करें।
  • कौन सा Java संस्करण सुझाया जाता है? सर्वोत्तम प्रदर्शन के लिए Java 11+।
  • मैं मेमोरी लीक कैसे रोकूँ? हमेशा Annotator को डिस्पोज़ करें या try‑with‑resources का उपयोग करें।

Java में PDF को प्रोग्रामेटिकली एनोटेट कैसे करें

नीचे आप चरण‑दर‑चरण प्रक्रिया देखेंगे, Maven सेटअप से लेकर एनोटेटेड फ़ाइल को सेव करने तक। प्रत्येक सेक्शन में संक्षिप्त व्याख्याएँ शामिल हैं ताकि आप कोड की हर पंक्ति के क्यों को समझ सकें।

पूर्वापेक्षाएँ: अपना वातावरण तैयार करना

PDFs को प्रो की तरह एनोटेट करने से पहले, सुनिश्चित करें कि आपने ये बुनियादी चीजें कवर कर ली हैं:

आवश्यक सेटअप आवश्यकताएँ

Java वातावरण:

  • JDK 8 या उससे ऊपर (बेहतर प्रदर्शन के लिए JDK 11+ सुझाया जाता है)
  • आपका पसंदीदा IDE (IntelliJ IDEA, Eclipse, या VS Code)

प्रोजेक्ट निर्भरताएँ:

  • निर्भरताओं के प्रबंधन के लिए Maven 3.6+
  • GroupDocs.Annotation लाइब्रेरी संस्करण 25.2 या बाद वाला

आपको जो ज्ञान चाहिए

चिंता न करें—आपको Java विशेषज्ञ होने की जरूरत नहीं है। बुनियादी परिचय होना चाहिए:

  • Java सिंटैक्स और ऑब्जेक्ट‑ओरिएंटेड कॉन्सेप्ट्स
  • Maven निर्भरता प्रबंधन
  • फ़ाइल I/O ऑपरेशन्स

बस इतना ही! हम आगे चलकर बाकी सब समझाएंगे।

GroupDocs.Annotation सेटअप: सही तरीका

अधिकांश ट्यूटोरियल महत्वपूर्ण सेटअप विवरण को छोड़ देते हैं। यह नहीं। चलिए GroupDocs.Annotation को आपके प्रोजेक्ट में सही तरीके से इंटीग्रेट करते हैं।

Maven कॉन्फ़िगरेशन जो वास्तव में काम करता है

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 रिलीज़ पेज पर नवीनतम संस्करण देखें। संस्करण 25.2 में पहले के संस्करणों की तुलना में उल्लेखनीय प्रदर्शन सुधार हैं।

लाइसेंसिंग: आपके विकल्प

आपके पास यहाँ तीन विकल्प हैं:

  1. Free Trial: परीक्षण और छोटे प्रोजेक्ट्स के लिए उपयुक्त
  2. Temporary License: विकास और प्रूफ़‑ऑफ़‑कॉन्सेप्ट्स के लिए शानदार
  3. Full License: प्रोडक्शन डिप्लॉयमेंट्स के लिए आवश्यक

इस ट्यूटोरियल के लिए, फ्री ट्रायल पूरी तरह काम करता है। बस याद रखें कि प्रोडक्शन ऐप्स को उचित लाइसेंस की आवश्यकता होगी।

त्वरित सेटअप सत्यापन

आइए मज़ेदार हिस्से में जाने से पहले सुनिश्चित करें कि सब कुछ काम कर रहा है:

import com.groupdocs.annotation.Annotator;

public class AnnotationSetup {
    public static void main(String[] args) {
        System.out.println("GroupDocs.Annotation is ready to use!");
        // If this compiles and runs without errors, you're good to go
    }
}

स्ट्रीम्स से दस्तावेज़ लोड करना: आधार

यहाँ चीज़ें रोचक हो जाती हैं। अधिकांश डेवलपर्स फ़ाइल पाथ से दस्तावेज़ लोड करते हैं, लेकिन स्ट्रीम‑आधारित लोडिंग आपको अद्भुत लचीलापन देती है। आप दस्तावेज़ डेटाबेस, वेब अनुरोध, या किसी भी अन्य स्रोत से लोड कर सकते हैं।

क्यों स्ट्रीम्स महत्वपूर्ण हैं

सोचिए: वास्तविक एप्लिकेशन में, आपके PDFs यहाँ से आ सकते हैं:

  • क्लाउड स्टोरेज (AWS S3, Google Cloud, Azure)
  • डेटाबेस BLOBs
  • HTTP अनुरोध
  • एन्क्रिप्टेड फ़ाइल सिस्टम

स्ट्रीम्स इन सभी परिदृश्यों को सहजता से संभालते हैं।

चरण 1: अपना इनपुट स्ट्रीम खोलें

import java.io.FileInputStream;
import java.io.InputStream;

public class LoadDocument {
    public static void main(String[] args) throws Exception {
        // Replace with your actual document path
        InputStream stream = new FileInputStream("YOUR_DOCUMENT_DIRECTORY/input.pdf");
        // The stream is now ready for GroupDocs.Annotation
    }
}

वास्तविक‑दुनिया नोट: प्रोडक्शन में, आप आमतौर पर इसे उचित एक्सेप्शन हैंडलिंग और रिसोर्स मैनेजमेंट (try‑with‑resources आपका दोस्त है) में रैप करेंगे।

चरण 2: Annotator को इनिशियलाइज़ करें

import com.groupdocs.annotation.Annotator;

public class LoadDocument {
    public static void main(String[] args) throws Exception {
        InputStream stream = new FileInputStream("YOUR_DOCUMENT_DIRECTORY/input.pdf");
        final Annotator annotator = new Annotator(stream);
        
        // Now you're ready to start adding annotations
        // Don't forget to dispose() when you're done!
    }
}

मेमोरी मैनेजमेंट टिप: समाप्त होने पर हमेशा annotator.dispose() कॉल करें। यह मेमोरी लीक को रोकता है जो समय के साथ आपके एप्लिकेशन के प्रदर्शन को नुकसान पहुंचा सकता है।

अपनी पहली एनोटेशन जोड़ना: एरिया एनोटेशन

एरिया एनोटेशन दस्तावेज़ के विशिष्ट क्षेत्रों को हाइलाइट करने के लिए उत्तम हैं। इन्हें डिजिटल स्टिकी नोट्स की तरह समझें जिन्हें आप अपने PDF में कहीं भी रख सकते हैं।

एरिया एनोटेशन बनाना

import com.groupdocs.annotation.models.Rectangle;
import com.groupdocs.annotation.models.annotationmodels.AreaAnnotation;

public class LoadDocument {
    public static void main(String[] args) throws Exception {
        InputStream stream = new FileInputStream("YOUR_DOCUMENT_DIRECTORY/input.pdf");
        final Annotator annotator = new Annotator(stream);

        // Create an area annotation
        AreaAnnotation area = new AreaAnnotation();
        area.setBox(new Rectangle(100, 100, 100, 100)); // x, y, width, height
        area.setBackgroundColor(65535); // ARGB color format (this is cyan)

        // Add the annotation to your document
        annotator.add(area);
        
        // Save the annotated document
        String outputPath = "YOUR_OUTPUT_DIRECTORY/LoadDocumentFromStream.pdf";
        annotator.save(outputPath);
        
        // Clean up resources
        annotator.dispose();
    }
}

Rectangle कॉर्डिनेट्स को समझना

Rectangle(100, 100, 100, 100) पैरामीटर इस प्रकार काम करते हैं:

  • पहला 100: X स्थिति (बाएँ किनारे से पिक्सेल)
  • दूसरा 100: Y स्थिति (ऊपर किनारे से पिक्सेल)
  • तीसरा 100: एनोटेशन की चौड़ाई
  • चौथा 100: एनोटेशन की ऊँचाई

कोऑर्डिनेट टिप: PDF कोऑर्डिनेट्स टॉप‑लेफ़्ट कोने से शुरू होते हैं। यदि आप गणितीय कोऑर्डिनेट्स (बॉटम‑लेफ़्ट ओरिजिन) के आदी हैं, तो यह पहले उल्टा लग सकता है।

उन्नत एनोटेशन तकनीकें

कई प्रकार के एनोटेशन

आप केवल एरिया एनोटेशन तक सीमित नहीं हैं। विभिन्न प्रकार जोड़ने का तरीका यहाँ है:

import com.groupdocs.annotation.models.Rectangle;
import com.groupdocs.annotation.models.annotationmodels.AreaAnnotation;

public class AddAnnotations {
    public static void main(String[] args) throws Exception {
        InputStream stream = new FileInputStream("YOUR_DOCUMENT_DIRECTORY/input.pdf");
        final Annotator annotator = new Annotator(stream);

        // Area annotation with custom styling
        AreaAnnotation area = new AreaAnnotation();
        area.setBox(new Rectangle(100, 100, 100, 100));
        area.setBackgroundColor(65535); // Semi-transparent cyan
        area.setOpacity(0.7); // 70% opacity for subtle highlighting

        annotator.add(area);
        
        String outputPath = "YOUR_OUTPUT_DIRECTORY/AnnotatedDocument.pdf";
        annotator.save(outputPath);
        annotator.dispose();
    }
}

रंग प्रबंधन टिप्स

ARGB रंग कभी‑कभी जटिल हो सकते हैं। यहाँ कुछ सामान्य मान हैं:

  • 65535 = सियान
  • 16711680 = लाल
  • 65280 = हरा
  • 255 = नीला
  • 16777215 = सफ़ेद
  • 0 = काला

प्रो टिप: ऑनलाइन ARGB कलर कैलकुलेटर का उपयोग करके आवश्यक सटीक मान प्राप्त करें, या हेक्स रंग को Integer.parseInt("FF0000", 16) से रेड के लिए कनवर्ट करें।

वास्तविक‑दुनिया के अनुप्रयोग जिन्हें आप बना सकते हैं

दस्तावेज़ समीक्षा सिस्टम

कानूनी दस्तावेज़ समीक्षाओं, अनुबंध प्रबंधन, या शैक्षणिक पेपर सहयोग के लिए उत्तम:

// Example: Highlighting important clauses in contracts
AreaAnnotation contractClause = new AreaAnnotation();
contractClause.setBox(new Rectangle(50, 200, 400, 50));
contractClause.setBackgroundColor(16776960); // Yellow highlight
contractClause.setMessage("Review this clause for compliance");

क्वालिटी एश्योरेंस वर्कफ़्लोज़

तकनीकी दस्तावेज़ में समस्याओं को चिह्नित करने के लिए एनोटेशन का उपयोग करें:

// Example: Marking sections that need updates
AreaAnnotation updateNeeded = new AreaAnnotation();
updateNeeded.setBox(new Rectangle(100, 300, 300, 100));
updateNeeded.setBackgroundColor(16711680); // Red for urgent attention
updateNeeded.setMessage("Content outdated - requires immediate update");

शैक्षिक उपकरण

इंटरैक्टिव लर्निंग मैटेरियल बनाएं:

// Example: Highlighting key concepts in textbooks
AreaAnnotation keyconcept = new AreaAnnotation();
keyContent.setBox(new Rectangle(75, 150, 450, 75));
keyContent.setBackgroundColor(65280); // Green for important information
keyContent.setMessage("Key concept: Remember this for the exam!");

प्रदर्शन अनुकूलन: प्रोडक्शन‑रेडी टिप्स

मेमोरी मैनेजमेंट सर्वश्रेष्ठ प्रथाएँ

जब संभव हो तो हमेशा try‑with‑resources का उपयोग करें:

public void annotateDocument(InputStream documentStream) throws Exception {
    try (Annotator annotator = new Annotator(documentStream)) {
        AreaAnnotation area = new AreaAnnotation();
        area.setBox(new Rectangle(100, 100, 100, 100));
        area.setBackgroundColor(65535);
        
        annotator.add(area);
        annotator.save("output.pdf");
        // annotator.dispose() called automatically
    }
}

बड़े दस्तावेज़ों की बैच प्रोसेसिंग

जब कई दस्तावेज़ प्रोसेस कर रहे हों:

public void processBatch(List<InputStream> documents) throws Exception {
    for (InputStream doc : documents) {
        try (Annotator annotator = new Annotator(doc)) {
            // Process each document
            // Memory is properly released after each iteration
        }
    }
}

स्ट्रीम अनुकूलन

बड़े फ़ाइलों के लिए, बफ़रिंग पर विचार करें:

import java.io.BufferedInputStream;

InputStream bufferedStream = new BufferedInputStream(
    new FileInputStream("large-document.pdf"), 
    8192 // 8KB buffer
);

सामान्य समस्याएँ और उनके समाधान

समस्या 1: “Document format not supported”

समस्या: आप ऐसी फ़ाइल को एनोटेट करने की कोशिश कर रहे हैं जिसे GroupDocs.Annotation पहचान नहीं पाता।

समाधान: दस्तावेज़ में समर्थित फ़ॉर्मेट्स देखें। अधिकांश सामान्य फ़ॉर्मेट्स (PDF, DOCX, PPTX) समर्थित हैं, लेकिन कुछ विशेष फ़ॉर्मेट्स नहीं हो सकते।

समस्या 2: बड़े फ़ाइलों के साथ OutOfMemoryError

समस्या: बड़े PDFs प्रोसेस करते समय आपका एप्लिकेशन क्रैश हो जाता है।

समाधान:

  1. JVM हीप साइज बढ़ाएँ: -Xmx2g
  2. दस्तावेज़ों को छोटे बैचों में प्रोसेस करें
  3. सुनिश्चित करें कि आप dispose() सही ढंग से कॉल कर रहे हैं

समस्या 3: एनोटेशन गलत स्थानों पर दिखते हैं

समस्या: आपके एनोटेशन अप्रत्याशित स्थानों पर दिख रहे हैं।

समाधान: अपने कोऑर्डिनेट सिस्टम को दोबारा जांचें। याद रखें कि PDF कोऑर्डिनेट्स टॉप‑लेफ़्ट कोने से शुरू होते हैं, और यूनिट्स पॉइंट्स में होते हैं (1 इंच = 72 पॉइंट्स)।

समस्या 4: रंग सही ढंग से नहीं दिख रहे हैं

समस्या: एनोटेशन के रंग आपके अपेक्षित रंग से मेल नहीं खाते।

समाधान: सुनिश्चित करें कि आप ARGB फ़ॉर्मेट सही ढंग से उपयोग कर रहे हैं। अल्फा चैनल पारदर्शिता को प्रभावित करता है, जिससे रंग अपेक्षा से अलग दिख सकते हैं।

प्रोडक्शन उपयोग के लिए सर्वश्रेष्ठ प्रथाएँ

1. एरर हैंडलिंग

प्रोडक्शन कोड में उचित एक्सेप्शन हैंडलिंग कभी न छोड़ें:

public boolean annotateDocument(InputStream input, String outputPath) {
    try (Annotator annotator = new Annotator(input)) {
        AreaAnnotation area = new AreaAnnotation();
        area.setBox(new Rectangle(100, 100, 100, 100));
        area.setBackgroundColor(65535);
        
        annotator.add(area);
        annotator.save(outputPath);
        return true;
    } catch (Exception e) {
        logger.error("Failed to annotate document: " + e.getMessage(), e);
        return false;
    }
}

2. कॉन्फ़िगरेशन मैनेजमेंट

सामान्य सेटिंग्स के लिए कॉन्फ़िगरेशन फ़ाइलों का उपयोग करें:

# application.properties
annotation.default.color=65535
annotation.default.opacity=0.7
annotation.output.directory=/path/to/output

3. वैलिडेशन

इनपुट्स को हमेशा वैलिडेट करें:

public void validateAnnotationParameters(Rectangle box) {
    if (box.getWidth() <= 0 || box.getHeight() <= 0) {
        throw new IllegalArgumentException("Annotation dimensions must be positive");
    }
    if (box.getX() < 0 || box.getY() < 0) {
        throw new IllegalArgumentException("Annotation position must be non-negative");
    }
}

अपने एनोटेशन कोड का परीक्षण

यूनिट टेस्टिंग एप्रोच

@Test
public void testAreaAnnotationCreation() throws Exception {
    // Given
    InputStream testDocument = getTestDocumentStream();
    
    // When
    try (Annotator annotator = new Annotator(testDocument)) {
        AreaAnnotation area = new AreaAnnotation();
        area.setBox(new Rectangle(100, 100, 100, 100));
        area.setBackgroundColor(65535);
        
        annotator.add(area);
        
        // Then
        // Verify annotation was added successfully
        // (implementation depends on your testing framework)
    }
}

लोकप्रिय फ्रेमवर्क्स के साथ इंटीग्रेशन

Spring Boot इंटीग्रेशन

@Service
public class DocumentAnnotationService {
    
    @Autowired
    private DocumentRepository documentRepository;
    
    public String annotateDocument(Long documentId, List<AnnotationRequest> annotations) {
        try (InputStream docStream = documentRepository.getDocumentStream(documentId);
             Annotator annotator = new Annotator(docStream)) {
            
            for (AnnotationRequest request : annotations) {
                AreaAnnotation area = createAnnotationFromRequest(request);
                annotator.add(area);
            }
            
            String outputPath = generateOutputPath(documentId);
            annotator.save(outputPath);
            
            return outputPath;
        } catch (Exception e) {
            throw new DocumentAnnotationException("Failed to annotate document", e);
        }
    }
}

आगे क्या: खोजने के लिए उन्नत फीचर्स

जब आप इस ट्यूटोरियल में कवर किए गए बुनियादी चीज़ों में महारत हासिल कर लें, तो इनका अन्वेषण करने पर विचार करें:

  • Text Annotations – विशिष्ट टेक्स्ट पासेज़ पर सीधे कमेंट्स और नोट्स जोड़ें।
  • Shape Annotations – डॉक्यूमेंट एलिमेंट्स को हाइलाइट करने के लिए एरो, सर्कल और अन्य शैप्स ड्रॉ करें।
  • Watermarks – ब्रांडिंग या सुरक्षा उद्देश्यों के लिए कस्टम वॉटरमार्क जोड़ें।
  • Annotation Extraction – विश्लेषण या माइग्रेशन के लिए दस्तावेज़ों से मौजूदा एनोटेशन पढ़ें।
  • Custom Annotation Types – आपके विशेष उपयोग केस के लिए कस्टम एनोटेशन टाइप बनाएं।

निष्कर्ष

अब आपके पास GroupDocs.Annotation का उपयोग करके Java PDF एनोटेशन में एक ठोस आधार है। स्ट्रीम्स के माध्यम से दस्तावेज़ लोड करने से लेकर एरिया एनोटेशन जोड़ने और प्रोडक्शन उपयोग के लिए अनुकूलन तक, आप मजबूत दस्तावेज़ एनोटेशन फीचर्स बनाने के लिए तैयार हैं।

मुख्य बिंदु:

  • स्ट्रीम‑आधारित लोडिंग अधिकतम लचीलापन प्रदान करती है।
  • उचित रिसोर्स मैनेजमेंट मेमोरी लीक को रोकता है।
  • ARGB कलर फ़ॉर्मेट उपस्थिति पर सटीक नियंत्रण देता है।
  • एरर हैंडलिंग और वैलिडेशन प्रोडक्शन सिस्टम के लिए अत्यंत महत्वपूर्ण हैं।

यहाँ सीखी गई तकनीकें साधारण प्रूफ़‑ऑफ़‑कॉन्सेप्ट से लेकर एंटरप्राइज़‑ग्रेड दस्तावेज़ प्रबंधन सिस्टम तक स्केल करती हैं। चाहे आप एक सहयोगी रिव्यू प्लेटफ़ॉर्म बना रहे हों या मौजूदा सॉफ़्टवेयर में एनोटेशन फीचर जोड़ रहे हों, अब आपके पास इसे सही तरीके से करने के टूल हैं।

अक्सर पूछे जाने वाले प्रश्न

Q: GroupDocs.Annotation के लिए न्यूनतम Java संस्करण क्या है?
A: Java 8 न्यूनतम है, लेकिन बेहतर प्रदर्शन और मेमोरी मैनेजमेंट के लिए Java 11+ सुझाया जाता है।

Q: क्या मैं PDFs के अलावा अन्य दस्तावेज़ों को भी एनोटेट कर सकता हूँ?
A: बिल्कुल! GroupDocs.Annotation 50 से अधिक दस्तावेज़ फ़ॉर्मेट्स को सपोर्ट करता है, जिसमें DOCX, PPTX, XLSX और विभिन्न इमेज फ़ॉर्मेट्स शामिल हैं।

Q: मैं बहुत बड़े PDF फ़ाइलों को मेमोरी खत्म हुए बिना कैसे हैंडल करूँ?
A: इन रणनीतियों का उपयोग करें: JVM हीप साइज बढ़ाएँ (-Xmx4g), दस्तावेज़ों को छोटे बैचों में प्रोसेस करें, और हमेशा Annotator इंस्टेंस को सही ढंग से डिस्पोज़ करें।

Q: क्या एनोटेशन के रंग और ट्रांसपैरेंसी को कस्टमाइज़ करना संभव है?
A: हाँ! सटीक नियंत्रण के लिए ARGB कलर वैल्यूज़ का उपयोग करें। उदाहरण के लिए, setBackgroundColor(65535) सियान सेट करता है, और setOpacity(0.5) इसे 50 % ट्रांसपेरेंट बनाता है।

Q: प्रोडक्शन उपयोग के लिए लाइसेंसिंग आवश्यकताएँ क्या हैं?
A: प्रोडक्शन डिप्लॉयमेंट के लिए आपको वैध GroupDocs.Annotation लाइसेंस चाहिए। विकास और परीक्षण के लिए फ्री ट्रायल उपयोग किया जा सकता है, लेकिन व्यावसायिक एप्लिकेशन के लिए खरीदा हुआ लाइसेंस आवश्यक है।

अतिरिक्त संसाधन


Last Updated: 2025-12-29
Tested With: GroupDocs.Annotation 25.2
Author: GroupDocs