주석

뷰어에 주석 지원 추가

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

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

주석 UI 활성화

Annotation은 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)초기화된 뷰어에 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) =>
{
    // 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/데이터를 영구 저장한 뒤 새 토큰에 로드

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