주석
뷰어에 주석 지원 추가
Doconut의 주석은 두 가지 방식으로 작동합니다: 사용자가 브라우저 위젯에서 직접 그리면 서버가 페이지별로 저장하고, 혹은 코드를 통해 프로그래밍 방식으로 생성하여 열린 세션에 로드합니다. 어느 쪽이든 페이지에 렌더링되며 PDF/PNG 내보내기에 포함될 수 있습니다.
주석 지원은 Annotation 라이선스 기능에 의해 제한됩니다(활성 임시 라이선스가 있으면 자동으로 부여됩니다).
주석 UI 활성화
주석은 Viewer 모듈이며 독립적인 툴바가 아닙니다. 전체 페이지에는 Viewer 리소스, Viewer 툴바, Viewer 마운트 및 초기화된 objViewer가 포함되어야 하며, 그 후에 Annotation Ribbon이 동일한 인스턴스에 마운트되고 연결됩니다.
주석 번들을 Viewer 번들과 함께 내보냅니다 — 이들은 라이선스에 따라 제한되므로 해당 기능이 사용 가능할 때만 태그가 표시됩니다:
@Html.Raw(Viewer.ReferenceCss(new CssConfig
{
IncludeViewerCss = true,
IncludeAnnotationCss = true // jquery-ui.min.css + annotationBar.css
}))
@Html.Raw(Viewer.ReferenceScripts(new ScriptConfig
{
IncludeJQuery = true,
IncludeViewerScripts = true,
IncludeAnnotationScripts = true, // jquery-ui, raphael.js, annotation.js
IncludeAnnotationBar = true // the embedded annotation ribbon
}))마크업에 전체 Viewer 구성을 표시된 상태로 유지합니다:
<nav id="toolbar" aria-label="Document viewer controls">
<!-- 주석을 여는 버튼을 포함한 뷰어 컨트롤 -->
</nav>
<div id="annBarMount"></div>
<div id="divDocViewer"><div id="div_ctlDoc"></div></div>annBarMount 내부에 Ribbon DOM을 생성하는 Annotation 번들이며, 버튼이나 대화상자 마크업을 복사할 필요가 없습니다. 먼저 docViewer를 초기화하고, 서버가 주석이 라이선스가 부여되었음을 확인할 때만 Ribbon을 생성합니다:
<script>
let annBar = null;
let currentToken = '';
const objViewer = $('#div_ctlDoc').docViewer({
BasePath: 'doconut',
ResPath: 'doconut-res/images',
onAnnLoaded: () => annBar?.handleAnnLoaded(),
onAnnSaved: () => annBar?.handleAnnSaved(),
onAnnSaveError: () => annBar?.handleAnnSaveError(),
onAnnClosed: () => annBar?.handleAnnClosed(),
onError: (message) => console.error('Viewer error:', message)
});
@if (Viewer.IsAnnotationEnabled)
{
<text>
annBar = $('#annBarMount').doconutAnnotationBar({
docId: 'ctlDoc',
getRequestParams: () => ({ token: currentToken }),
onStatus: (message) => console.log(message),
onToast: (message, type) => console.log(type, message),
onLayout: () => requestAnimationFrame(() => objViewer.Refit())
});
annBar.attach(objViewer);
</text>
}
</script>Ribbon에서 저장하면 미들웨어(AnnSave)를 통해 데이터를 전송하며, 이는 페이지별 문서 세션에 저장됩니다. 로드(AnnLoad)는 주석이 있는 페이지가 렌더링될 때 자동으로 이루어집니다. 네 개의 onAnn* 콜백은 Ribbon을 Viewer 라이프사이클과 동기화합니다.
호스트가 소유한 Viewer 툴바에서 언제든지 열고 닫을 수 있습니다:
annBar.open();
annBar.close();공용 Ribbon API는 다음과 같습니다:
| Method | Purpose |
|---|---|
attach(objViewer) | 초기화된 뷰어에 Ribbon을 연결합니다; 한 번만 필요합니다 |
open() / close() | 주석 편집 모드에 들어가거나 나갑니다 |
reset() | Ribbon을 닫힌 비편집 상태로 되돌립니다 |
isOpen() / annotating() | Ribbon 상태 / 뷰어의 주석 편집 상태를 읽습니다 |
reopenEditable() | 현재 페이지의 주석을 편집 가능한 객체로 다시 로드합니다 |
updateActionState() | 호스트 변경 후 저장/삭제 제어 가능성을 새로 고칩니다 |
headerSlot() | 호스트가 소유한 컨트롤을 위한 선택적 헤더 확장 슬롯을 가져옵니다 |
onStatus, onToast, onLayout, onEditStart, onEditEnd는 선택적인 호스트 콜백입니다. endpoints 객체는 추가로 exportPdf, exportPng, imageUpload, imageList를 제공할 수 있으며, 설정된 엔드포인트가 없는 컨트롤은 숨겨진 상태로 유지됩니다. 결합된 Viewer, Search, 및 Annotation 시작 순서는 빠른 시작 을 참조하십시오.
주석 번들은 브라우저 저작 도구를 추가하지만, 데이터는 토큰으로 식별되는 서버 측 문서 세션에 속합니다. 소스를 다시 열면 새로운 세션이 생성됩니다; 주석이 세션 수명보다 오래 유지되어야 한다면 XML 또는 인코딩된 주석 봉투를 애플리케이션에 저장하십시오.
C#에서 주석 만들기
열린 세션에 바인딩된 매니저를 가져와 주석을 추가하고 로드합니다 (using Doconut.Annotations;는 타입을 위해, using System.Drawing;는 Rectangle/Color를 위해 사용합니다):
app.MapPost("/api/annotations/load-sample", (string token, Viewer viewer) =>
{
// 열린 세션의 페이지 차원에 맞춰짐
var manager = viewer.GetAnnotationManager(token);
var pageCount = viewer.GetPageCount(token);
// 페이지당 하나의 스탬프
for (int page = 1; page <= pageCount; page++)
{
manager.Add(new StampAnnotation(page, new Rectangle(30, 20, 240, 90),
$"PAGE {page}", 28, 4, Color.Maroon)
{
Opacity = 60,
Rotate = -8
});
}
manager.Add(new NoteAnnotation(1, new Rectangle(420, 150, 220, 120),
"Loaded from C# code.", Color.FromArgb(255, 255, 255, 170), 14));
// 세션에 로드 — 위젯이 AnnLoad를 통해 가져오고
// 렌더러가 이를 이미지/PDF 내보내기에 포함시킵니다.
viewer.LoadAnnotationData(token, manager);
return Results.Ok();
});주석 유형
Doconut.Annotations에 모든 유형이 존재하며 BaseAnnotation을 상속합니다 (페이지 번호와 경계 Rectangle 포함):
| 유형 | 설명 |
|---|---|
StampAnnotation | 글꼴 크기, 테두리, 색상을 가진 텍스트 스탬프; Opacity, Rotate 지원 |
NoteAnnotation | 텍스트, 배경 색상, 글꼴 크기, TitleColor를 가진 메모 |
RectangleAnnotation | 테두리와 채우기 색상, Title/ShowTitle |
CircleAnnotation | 테두리와 채우기, ShowBorder |
EllipseAnnotation | 테두리와 채우기, ShowBorder |
TriangleAnnotation | 테두리 색상, BackColor, ShowBorder |
LineAnnotation | 너비와 색상을 가진 직선 |
ArrowAnnotation | 화살표 머리와 함께하는 선; 설정 가능한 Direction (타입 ArrowDirection, 방위점, 기본값 E) |
FreehandAnnotation | 인코딩된 FreehandData 포인트로부터 자유로운 스트로크 |
ImageAnnotation | URL에서 가져온 이미지. 상대 URL은 주석이 추가될 때 요청 호스트를 기준으로 해석됩니다(이미지 가져오기는 번인 시점에만 수행) — 서버에서 접근 가능해야 합니다(예: UseStaticFiles로 제공되는 wwwroot 아래 파일). |
AnnotationManager API
| 멤버 | 목적 |
|---|---|
Add(BaseAnnotation) | 주석을 큐에 추가 |
GetAnnotations() / GetAnnotations(int page) | 매니저가 보유한 주석을 확인 |
ClearAnnotations() / ClearAnnotations(int page) | 전체 또는 페이지별로 제거 |
GetAnnotationData() / GetAnnotationData(int page) | 인코딩된 주석 데이터 문자열 — 위젯이 사용하는 Base64 전송 포맷 |
GetAnnotationXml() | XML 형태 |
Viewer는 세션에 대한 로드/읽기 작업을 반영합니다: LoadAnnotationData(token, manager) 또는 LoadAnnotationData(token, encodedData)(GetAnnotationData()에서 반환된 Base64 전송 포맷), LoadAnnotationXML(token, xml), GetAnnotationXML(token).
주석이 포함된 내보내기
// 주석이 렌더링된 모든 페이지의 PDF
app.MapGet("/api/annotations/export-pdf", async (string token, Viewer viewer) =>
{
byte[] pdf = await viewer.ExportAnnotationsToPdfAsync(token, zoom: 100);
return Results.File(pdf, "application/pdf", "export.pdf");
});
// 또는 페이지별 PNG의 ZIP
app.MapGet("/api/annotations/export-png-zip", async (string token, Viewer viewer) =>
{
byte[] zip = await viewer.ExportAnnotationsToPngZipAsync(token, zoom: 100);
return Results.File(zip, "application/zip", "annotations-png.zip");
});내보내기는 화면 렌더링과 동일한 번인 방식을 사용하므로 사용자가 보는 내용이 파일에 그대로 포함됩니다.
지속성 워크플로우
- 문서를 열고 토큰을 가져옵니다.
- 이전에 저장된 XML 또는 인코딩된 데이터를 해당 토큰에 로드합니다.
- 위젯이 세션 주석을 읽고 편집하도록 합니다.
- 애플리케이션이 지속을 결정할 때
GetAnnotationXML(token)으로 XML을 가져옵니다. - 평면화된 결과물이 필요할 경우 PDF/PNG를 내보냅니다.
- 문서 세션을 종료합니다.
불투명한 뷰어 토큰을 영구적인 주석 식별자로 사용하지 마세요. 지속된 주석 데이터를 자체 문서 및 버전 식별자와 연결하십시오.
보안 및 렌더링 참고 사항
- Annotation 요청은 페이지 요청과 동일한 세션/토큰 보안을 사용합니다.
- 상대
ImageAnnotationURL은 요청 호스트를 기준으로 해결되며, 번인 시 서버에서 접근 가능해야 합니다. - 서버 측 요청 위조를 방지하기 위해 사용자 제공 이미지 URL을 검증하고 제어합니다.
- 내보내기는 화면상의 페이지 렌더링과 동일한 라이선스/맞춤 워터마크 결정을 적용합니다.
- 대용량 자유형 페이로드와 고해상도 내보내기는 메모리 사용량을 증가시킵니다; 실제 문서와 줌 값을 테스트하십시오.
문제 해결
| 증상 | 확인 항목 |
|---|---|
| 주석 리본이 누락되었습니다 | Annotation 기능 및 네 개의 annotation CSS/script 플래그 |
| 저장 콜백이 오류를 보고합니다 | Token/세션 만료 및 미들웨어 BasePath |
| C# 주석이 표시되지 않습니다 | 페이지 번호가 1부터 시작하며 데이터가 활성 토큰에 로드되었습니다 |
| 이미지 주석이 화면에 표시되지만 내보내기에서는 표시되지 않습니다 | 서버가 번인 중에 이미지 URL에 접근할 수 있습니다 |
| 다시 연 문서에 주석이 없습니다 | 뷰어 세션 외부에 XML/데이터를 지속하고, 새 토큰에 로드합니다 |
이 페이지가 도움이 되었나요?