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"
- Dokümantasyon: C#’ta PDF Anotasyonu
- Bir LLM’nin bir sözleşmeyi incelemesine ve gerçek yorumlar bırakmasına izin ver — aynı çağrılar, bir model tarafından yönlendirilir
- Bir tarayıcı anotasyon UI’sı oluştur — aynı çağrılar, bir kişi tarafından tarayıcıda yönlendirilir