Bu soruya verilen çoğu yanıt beş yıl eski, sadece PDF ve sizi göremediğiniz bir sayfaya göre koordinat ölçmenizi istiyor. İşte 2026 sürümü: yedi çağrı, işaretlemenin gerçekten nereye yerleşeceğini belirleyen iki kural ve bir Linux konteynerinin ihtiyaç duyduğu tek satır.

Aşağıdaki her şey .NET 8 ve .NET 10 üzerinde, Windows ve Linux’ta, Adobe Acrobat ve Microsoft Office yüklü olmadan çalışır — ve aynı kod DOCX, XLSX, PPTX ve görüntüler üzerinde de çalışır. Sadece dosya yolu değişir.

Çalıştırılabilir sürüm GitHub’da bulunur: annotate-pdf-csharp-net.

Kurulum

dotnet add package GroupDocs.Annotation

Bir lisans olmadan kütüphane değerlendirme modunda çalışır ve çıktı değerlendirme işaretleri içerir. Bir geçici lisans bunları kaldırır:

new License().SetLicense("GroupDocs.Annotation.lic");

Belgeyi Aç

using (Annotator annotator = new Annotator("quarterly-review.pdf"))
{
    // add, read or remove markup here
}

İlk bilmeniz gereken bir şey: annotator dosyayı kapatılana kadar açık tutar ve okuduğu dosyanın üzerine kaydedemez. Farklı bir yola, ya da daha sonra yerine taşıyacağınız geçici bir dosyaya kaydedin.

Koordinatları ölçmeyin — metni arayın

Bu, çoğu öğreticinin atladığı kısımdır ve anotasyon kodunun belgenin bir sonraki sürümünde de çalışmasını sağlayan bölümdür. GetDocumentInfo() her sayfayı boyutu ve metin satırlarıyla, her birini bir kutu ile birlikte döndürür:

IDocumentInfo info = annotator.Document.GetDocumentInfo();

foreach (PageInfo page in info.PagesInfo)
{
    foreach (TextLineInfo line in page.TextLines)
    {
        if (line.Text.Contains("Hosting costs"))
        {
            // line.LeftIndent, line.TopIndent, line.Width, line.Height — points, from the top-left
        }
    }
}

Artık anotasyon, belgenin geçen yıl ne dediği yerine, bugün ne söylediği üzerine yerleştirilebilir.

Yorum ve yanıt içeren bir vurgulama ekleyin

float left   = (float)line.LeftIndent;
float right  = (float)(line.LeftIndent + line.Width);
float top    = (float)(page.Height - line.TopIndent);
float bottom = (float)(page.Height - line.TopIndent - line.Height);

annotator.Add(new HighlightAnnotation
{
    PageNumber = page.PageNumber - 1,
    BackgroundColor = 0xFFE699,
    Opacity = 0.5,
    Message = "Check this against the invoice",
    CreatedOn = DateTime.Now,
    User = new User { Id = 1, Name = "Reviewer", Role = Role.Editor },
    Points = new List<Point>
    {
        new Point(left, top),    new Point(right, top),
        new Point(left, bottom), new Point(right, bottom)
    },
    Replies = new List<Reply>
    {
        new Reply { Comment = "Confirmed with finance.", RepliedOn = DateTime.Now }
    }
});

annotator.Save("result.pdf");

Message yorumdur. Replies bir konu başlatır — PDF okuyucusunda yanıtı olan bir not, Word’de ise altına yanıt eklenmiş bir yorumdur. User, daha sonraki bir aşamanın tam olarak bu inceleyicinin işaretlemesini bulup kaldırmasını ve diğerlerinin işaretlemesinin olduğu gibi kalmasını sağlar.

Herkesi yakalayan iki kural

top ve bottom değerlerini hesaplayan iki satıra ve PageNumber‘a yakından bakın.

Sayfa numaraları iki kez sayılır. PageInfo.PageNumber 1 tabanlıdır. AnnotationBase.PageNumber 0 tabanlıdır. Bunları karıştırırsanız her anotasyon bir sayfa kayar — ve son sayfaya yönelik olanlar kaybolur, çünkü var olmayan bir sayfaya yönlendirilmiş anotasyon kaydedilirken istisna veya uyarı vermeden atılır. İşaretleme “görünmediyse”, önce sayfa indeksini kontrol edin.

İki köken. TextLineInfo ve AreaAnnotation.Box sol üst köşeden ölçülür. Metin işaretlemesi — vurgulama, altı çizili, üstü çizili, dalgalı — sol alt köşeden ölçülür; bu PDF’in kendisinin çalışma şeklidir.

Üye Köken Sayfa numaralandırması
PageInfo, TextLineInfo sol üst 1 tabanlı
AnnotationBase.PageNumber — 0 tabanlı
AreaAnnotation.Box sol üst —
HighlightAnnotation.Points sol alt —
PreviewOptions.PageNumbers — 1 tabanlı

Dört nokta bir dikdörtgen oluşturur, bu sırayla: sol üst, sağ üst, sol alt, sağ alt. Aynı listede daha fazla dikdörtgen eklenebilir; bu, tek bir yorumun üç satıra yayılan bir cümleyi kapsamasını sağlar.

İşaretlemeyi geri okuyun

using (Annotator annotator = new Annotator("result.pdf"))
{
    foreach (AnnotationBase a in annotator.Get())
    {
        Console.WriteLine($"#{a.Id} {a.Type} on page {a.PageNumber + 1}: {a.Message}");

        foreach (Reply reply in a.Replies)
            Console.WriteLine($"   reply: {reply.Comment}");
    }
}

Get() belgenin gerçekte ne içerdiğini okur — Word ya da Acrobat’ta bir kişinin eklediği anotasyonlar dahil, bu da diğer araçlarla yapılan çift yönlü işlemlerin çalışmasını sağlar.

Bilmeniz gereken bir uyarı: Id belge okunurken atanır, içinde saklanmaz. Aynı oturumda bir anotasyonu silmek veya güncellemek için yeterince kararlıdır ve bir veritabanına konulacak bir anahtar değildir. Anotasyonları dosyanın dışına tutmak için içe ve dışa aktarımı kullanın.

Tekrar kaldırın

annotator.Remove(1);                                      // by id
annotator.Remove(annotator.Get()
    .Where(a => a.User?.Name == "Reviewer").ToList());     // or by author

Sayfaları oluşturun

PreviewOptions previewOptions = new PreviewOptions(n => File.Create($"page_{n}.png"));
previewOptions.PreviewFormat = PreviewFormats.PNG;
previewOptions.PageNumbers = new[] { 1, 2 };     // 1-based
previewOptions.RenderAnnotations = true;
previewOptions.Resolution = 144;

annotator.Document.GeneratePreview(previewOptions);

Üç yerde faydalıdır: belgeyi gösteren bir web UI, işaretlemenin istediğiniz yere yerleştiğini kanıtlayan bir test ve kendi çalışmasını incelemesi gereken bir ajan.

Linux’ta

Kod değişmez. Konteyner değişir:

FROM mcr.microsoft.com/dotnet/runtime:8.0
RUN apt-get update \
 && apt-get install -y --no-install-recommends fontconfig fonts-liberation \
 && rm -rf /var/lib/apt/lists/*

.NET runtime imajları yazı tipleri olmadan gelir ve metin render’ı bunlara ihtiyaç duyar. O tek satır, doğru çıktı ile boş sayfalar arasındaki farktır — ve genel olarak belge kütüphaneleriyle ilgili en yaygın Linux sorusudur.

Sonraki adım

git clone https://github.com/groupdocs-annotation/annotate-pdf-csharp-net.git
cd annotate-pdf-csharp-net
dotnet run --project src/AnnotatePdf -- highlight samples/quarterly-review.pdf --text "Hosting costs"