주석

뷰어에 주석 지원 추가

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">
    <!-- Viewer controls, including the button that opens Annotation -->
</nav>
<div id="annBarMount"></div>
<div id="divDocViewer"><div id="div_ctlDoc"></div></div>

Annotation 번들은 annBarMount 내부에 Ribbon DOM을 생성합니다; 버튼이나 대화상자 마크업을 복사할 필요가 없습니다. 먼저 docViewer를 초기화하고, 서버가 Annotation이 라이선스가 있음을 확인했을 때만 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는 다음과 같습니다:

메서드목적
attach(objViewer)초기화된 Viewer에 Ribbon을 연결합니다; 한 번만 필요합니다
open() / close()주석 편집 모드에 진입하거나 종료합니다
reset()Ribbon을 닫힌 비편집 상태로 되돌립니다
isOpen() / annotating()Ribbon 상태 / Viewer의 주석 편집 상태를 읽습니다
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) =>
{
    // Bound to the open session's page dimensions
    var manager = viewer.GetAnnotationManager(token);
    var pageCount = viewer.GetPageCount(token);

    // One stamp per page
    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));

    // Load into the session — the widget fetches them via AnnLoad and the
    // renderer burns them into image/PDF exports.
    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 of all pages with annotations rendered onto them
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");
});

// Or a ZIP of per-page PNGs
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. 문서 세션을 종료합니다.

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

보안 및 렌더링 참고사항

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

문제 해결

증상확인 사항
주석 리본이 보이지 않음Annotation 기능과 네 개의 주석 CSS/스크립트 플래그
저장 콜백이 오류를 보고함토큰/세션 만료 및 미들웨어 BasePath
C# 주석이 나타나지 않음페이지 번호가 1부터 시작하며 데이터가 활성 토큰에 로드되었는지 확인
이미지 주석이 화면에 보이지만 내보내기에 포함되지 않음버닝 시 서버가 이미지 URL에 접근 가능한지 확인
재열린 문서에 주석이 없음뷰어 세션 외부에 XML/데이터를 지속하고 새 토큰에 로드했는지 확인

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