주석

뷰어에 주석 지원 추가

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

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

주석 UI 활성화

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

annotation 번들을 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>

annBarMount 내부에 Ribbon DOM을 생성하는 Annotation 번들을 사용합니다; 버튼이나 대화상자 마크업을 복사할 필요가 없습니다. 먼저 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는 다음과 같습니다:

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 시작 순서는 빠른 시작를 참조하십시오.

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. 문서 세션을 종료합니다.

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

보안 및 렌더링 참고사항

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

문제 해결

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

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