주석

뷰어에 주석 지원 추가

Doconut의 주석은 두 가지 방식으로 작동합니다: 사용자가 브라우저 위젯에서 직접 그리면 서버가 페이지별로 저장하고, 혹은 코드를 통해 프로그래밍 방식으로 생성하여 열린 세션에 로드합니다. 어느 쪽이든 페이지에 렌더링되며 PDF/PNG 내보내기에 포함될 수 있습니다.

주석 지원은 Annotation 라이선스 기능에 의해 제한됩니다(활성 임시 라이선스가 있으면 자동으로 부여됩니다).

주석 UI 활성화

주석은 Viewer 모듈이며 독립적인 툴바가 아닙니다. 전체 페이지에는 Viewer 리소스, Viewer 툴바, Viewer 마운트 및 초기화된 objViewer가 포함되어야 하며, 그 후에 Annotation Ribbon이 동일한 인스턴스에 마운트되고 연결됩니다.

주석 번들을 Viewer 번들과 함께 내보냅니다 — 이들은 라이선스에 따라 제한되므로 해당 기능이 사용 가능할 때만 태그가 표시됩니다:

html
@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 구성을 표시된 상태로 유지합니다:

html
<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을 생성합니다:

html
<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 툴바에서 언제든지 열고 닫을 수 있습니다:

javascript
annBar.open();
annBar.close();

공용 Ribbon API는 다음과 같습니다:

MethodPurpose
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를 위해 사용합니다):

csharp
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 포인트로부터 자유로운 스트로크
ImageAnnotationURL에서 가져온 이미지. 상대 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).

주석이 포함된 내보내기

csharp
// 주석이 렌더링된 모든 페이지의 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");
});

내보내기는 화면 렌더링과 동일한 번인 방식을 사용하므로 사용자가 보는 내용이 파일에 그대로 포함됩니다.

지속성 워크플로우

  1. 문서를 열고 토큰을 가져옵니다.
  2. 이전에 저장된 XML 또는 인코딩된 데이터를 해당 토큰에 로드합니다.
  3. 위젯이 세션 주석을 읽고 편집하도록 합니다.
  4. 애플리케이션이 지속을 결정할 때 GetAnnotationXML(token)으로 XML을 가져옵니다.
  5. 평면화된 결과물이 필요할 경우 PDF/PNG를 내보냅니다.
  6. 문서 세션을 종료합니다.

불투명한 뷰어 토큰을 영구적인 주석 식별자로 사용하지 마세요. 지속된 주석 데이터를 자체 문서 및 버전 식별자와 연결하십시오.

보안 및 렌더링 참고 사항

  • Annotation 요청은 페이지 요청과 동일한 세션/토큰 보안을 사용합니다.
  • 상대 ImageAnnotation URL은 요청 호스트를 기준으로 해결되며, 번인 시 서버에서 접근 가능해야 합니다.
  • 서버 측 요청 위조를 방지하기 위해 사용자 제공 이미지 URL을 검증하고 제어합니다.
  • 내보내기는 화면상의 페이지 렌더링과 동일한 라이선스/맞춤 워터마크 결정을 적용합니다.
  • 대용량 자유형 페이로드와 고해상도 내보내기는 메모리 사용량을 증가시킵니다; 실제 문서와 줌 값을 테스트하십시오.

문제 해결

증상확인 항목
주석 리본이 누락되었습니다Annotation 기능 및 네 개의 annotation CSS/script 플래그
저장 콜백이 오류를 보고합니다Token/세션 만료 및 미들웨어 BasePath
C# 주석이 표시되지 않습니다페이지 번호가 1부터 시작하며 데이터가 활성 토큰에 로드되었습니다
이미지 주석이 화면에 표시되지만 내보내기에서는 표시되지 않습니다서버가 번인 중에 이미지 URL에 접근할 수 있습니다
다시 연 문서에 주석이 없습니다뷰어 세션 외부에 XML/데이터를 지속하고, 새 토큰에 로드합니다

이 페이지가 도움이 되었나요?