Skip to content

Commit 643408c

Browse files
1012852: Updated flatten and control annotation visibility UG
1 parent d6b16b0 commit 643408c

4 files changed

Lines changed: 362 additions & 2 deletions

File tree

Document-Processing-toc.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -998,6 +998,8 @@
998998
<li><a href="/document-processing/pdf/pdf-viewer/react/annotation/signature-annotation">Handwritten Signature</a></li>
999999
<li><a href="/document-processing/pdf/pdf-viewer/react/annotation/line-angle-constraints">Line Angle Constraint</a></li>
10001000
<li><a href="/document-processing/pdf/pdf-viewer/react/annotation/custom-data">Adding custom Data in annotations</a></li>
1001+
<li><a href="/document-processing/pdf/pdf-viewer/react/annotation/flatten-annotation">Flatten Annotations into the PDF </a></li>
1002+
<li><a href="/document-processing/pdf/pdf-viewer/react/how-to/toggle-annotation-visibility">Show, Hide, or Toggle Annotation Visibility</a></li>
10011003
<li>Export and Import Annotations
10021004
<ul>
10031005
<li><a href="/document-processing/pdf/pdf-viewer/react/annotation/export-import/export-annotation">Export Annotation</a></li>
@@ -1122,7 +1124,7 @@
11221124
<li><a href="/document-processing/pdf/pdf-viewer/react/how-to/get-base64">Retrieve base64-encoded value of a loaded PDF document</a></li>
11231125
<li><a href="/document-processing/pdf/pdf-viewer/react/how-to/enable-local-storage">Enable Local Storage</a></li>
11241126
<li><a href="/document-processing/pdf/pdf-viewer/react/how-to/add-annotation-in-text-search">Add Rectangle Annotation using Search Text Bounds</a></li>
1125-
<li><a href="/document-processing/pdf/pdf-viewer/react/how-to/control-annotation-visibility">Control Annotation Visibility</a></li>
1127+
<li><a href="/document-processing/pdf/pdf-viewer/react/how-to/toggle-annotation-visibility">Show, Hide or Toggle Annotation Visibility</a></li>
11261128
<li><a href="/document-processing/pdf/pdf-viewer/react/how-to/extract-text-option">Extract Text Option</a></li>
11271129
<li><a href="/document-processing/pdf/pdf-viewer/react/how-to/find-text-async">Find Text using findTextAsync</a></li>
11281130
<li><a href="/document-processing/pdf/pdf-viewer/react/how-to/extract-text">Extract Text</a></li>
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
---
2+
layout: post
3+
title: Flatten Annotations Before Saving the PDF | Syncfusion
4+
description: Learn how all about how to flatten annotations and formfields before saving a PDF in the Syncfusion React PDF Viewer.
5+
platform: document-processing
6+
control: PDF Viewer
7+
documentation: ug
8+
domainurl: ##DomainURL##
9+
---
10+
11+
## Flatten Annotations Before Saving the PDF in React
12+
Flattening annotations permanently merges them into the PDF content. Once flattened:
13+
- Annotations are **no longer editable** in any PDF viewer.
14+
- Useful for **secure sharing**, preventing modifications.
15+
- Ideal when **finalizing markup** before distribution.
16+
17+
## How to Flatten Annotations (React)
18+
Follow these steps to flatten annotations and form fields before saving the PDF.
19+
20+
{% tabs %}
21+
{% highlight js tabtitle="Standalone" %}
22+
{% raw %}
23+
import { createRoot } from 'react-dom/client';
24+
import './index.css';
25+
import * as React from 'react';
26+
import {
27+
PdfViewerComponent,
28+
Toolbar,
29+
Magnification,
30+
Navigation,
31+
LinkAnnotation,
32+
BookmarkView,
33+
ThumbnailView,
34+
Print,
35+
TextSelection,
36+
TextSearch,
37+
Annotation,
38+
FormFields,
39+
FormDesigner,
40+
PageOrganizer,
41+
Inject,
42+
} from '@syncfusion/ej2-react-pdfviewer';
43+
import {
44+
PdfDocument
45+
} from '@syncfusion/ej2-pdf';
46+
47+
function Default() {
48+
let viewer;
49+
50+
const flattenPdf = () => {
51+
viewer.saveAsBlob().then((value) => {
52+
const reader = new FileReader();
53+
reader.onloadend = function () {
54+
const arrayBuffer = reader.result;
55+
const byteArray = new Uint8Array(arrayBuffer);
56+
const document = new PdfDocument(byteArray);
57+
// Flatten all annotations and form fields
58+
document.flatten = true;
59+
document.save('flattened.pdf');
60+
};
61+
reader.readAsArrayBuffer(value);
62+
});
63+
};
64+
65+
return (
66+
<div>
67+
<div className="control-section">
68+
<button onClick={flattenPdf}>Flatten and download PDF</button>
69+
70+
<PdfViewerComponent
71+
ref={(scope) => { viewer = scope; }}
72+
id="container"
73+
documentPath="https://cdn.syncfusion.com/content/pdf/pdf-succinctly.pdf"
74+
resourceUrl="https://cdn.syncfusion.com/ej2/23.2.6/dist/ej2-pdfviewer-lib"
75+
style={{ height: '640px' }}
76+
>
77+
<Inject
78+
services={[
79+
Toolbar,
80+
Magnification,
81+
Navigation,
82+
LinkAnnotation,
83+
BookmarkView,
84+
ThumbnailView,
85+
Print,
86+
TextSelection,
87+
TextSearch,
88+
Annotation,
89+
FormFields,
90+
FormDesigner,
91+
PageOrganizer,
92+
]}
93+
/>
94+
</PdfViewerComponent>
95+
</div>
96+
</div>
97+
);
98+
}
99+
export default Default;
100+
101+
const root = createRoot(document.getElementById('sample'));
102+
root.render(<Default />);
103+
{% endraw %}
104+
{% endhighlight %}
105+
{% endtabs %}
106+
107+
## Notes
108+
109+
- Flattening applies to **all annotation types**: text markup, shapes, stamps, notes, ink, and form fields.
110+
- Once flattened, annotations **cannot be edited or removed**.
111+
- Use flattening **only at export time**, not during regular document interactions.
112+
113+
## See also
114+
115+
- [Annotation Overview](../overview)
116+
- [Annotation Types](../annotation/annotation-types/area-annotation)
117+
- [Annotation Toolbar](../toolbar-customization/annotation-toolbar)
118+
- [Create and Modify Annotation](../annotation/create-modify-annotation)
119+
- [Customize Annotation](../annotation/customize-annotation)
120+
- [Handwritten Signature](../annotation/signature-annotation)
121+
- [Export and Import Annotation](../annotation/export-import/export-annotation)
122+
- [Annotation Permission](../annotation/annotation-permission)
123+
- [Annotation in Mobile View](../annotation/annotations-in-mobile-view)
124+
- [Annotation Events](../annotation/annotation-event)
125+
- [Annotation API](../annotation/annotations-api)

Document-Processing/PDF/PDF-Viewer/react/how-to-overview.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ The following how-to articles and frequently asked questions for the Syncfusion
2020
* [How to clear all the annotations from the PDF document?](./how-to/clear-annotations)
2121
* [How to configure the annotation selector settings?](./how-to/configure-annotationselector-setting-ts)
2222
* [What are the supported conformance or standards of PDF Viewer?](./how-to/conformance)
23-
* [How to control the visibility of the annotation?](./how-to/control-annotation-visibility-ts)
23+
* [How to Show, Hide or Toggle Annotation Visibility](./how-to/toggle-annotation-visibility)
2424
* [How to convert the PDF library bounds to PDF viewer bounds?](./how-to/convert-pdf-library-bounds-to-pdf-viewer-bounds-ts)
2525
* [How to create PDF Viewer service using ASP.NET Core?](./how-to/create-pdfviewer-service-core)
2626
* [How to create PDF viewer web service using ASP.NET MVC?](./how-to/create-pdfviewer-service)
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
---
2+
layout: post
3+
title: Show, Hide, or Toggle Annotation Visibility | Syncfusion
4+
description: Learn here all about how to Show, Hide, or Toggle Annotation Visibility and Control annotation visibility in the React PDF Viewer
5+
control: PDF Viewer
6+
platform: document-processing
7+
documentation: ug
8+
domainurl: ##DomainURL##
9+
---
10+
11+
# Show, Hide, or Toggle Annotation Visibility
12+
13+
You can control annotation visibility at two levels in the React PDF Viewer:
14+
15+
- **In the Viewer UI**: Temporarily hide or show annotations to simplify the screen for certain workflows (presentations, reviews) without modifying the source PDF.
16+
- **At export time**: Keep annotations visible inside your app but **hide them only in the downloaded PDF** by setting the `noView` flag.
17+
18+
N> If you’re new to the component, set up the **Standalone** Viewer first using the [Getting Started guide](https://help.syncfusion.com/document-processing/pdf/pdf-viewer/react/getting-started).
19+
20+
## Hide a Specific Annotation
21+
22+
For concise Viewer‑level hiding, remove an annotation by its **id**. If you’ll want to re‑show it later, export annotations to a cache first, then delete the target. See the full, step‑by‑step sample in [Show or hide annotations in React PDF Viewer](./show-hide-annotation)
23+
24+
**Minimal snippet (delete by id):**
25+
26+
{% tabs %}
27+
{% highlight js tabtitle="Standalone" %}
28+
{% raw %}
29+
// Hide (remove) a specific annotation by id
30+
function hideAnnotationById(annotationId) {
31+
const viewer = document.getElementById('container').ej2_instances[0];
32+
viewer.annotationModule.deleteAnnotationById(annotationId);
33+
}
34+
35+
// Example: hide the first annotation currently in the document
36+
function exampleHideFirst() {
37+
const viewer = document.getElementById('container').ej2_instances[0];
38+
if (viewer.annotationCollection?.length) {
39+
hideAnnotationById(viewer.annotationCollection[0].annotationId || viewer.annotationCollection[0].id);
40+
}
41+
}
42+
{% endraw %}
43+
{% endhighlight %}
44+
{% endtabs %}
45+
46+
## Show a Specific Annotation
47+
48+
Re‑import previously exported annotations and (optionally) filter the JSON to reinsert only the one you need. (Export must be done **before** deletion.)
49+
50+
**Minimal snippet (re‑import from cache):**
51+
52+
{% tabs %}
53+
{% highlight js tabtitle="Standalone" %}
54+
{% raw %}
55+
// Assume you cached this earlier via viewer.exportAnnotationsAsObject()
56+
function showFromCache(exportedObjectString) {
57+
const viewer = document.getElementById('container').ej2_instances[0];
58+
if (exportedObjectString) {
59+
viewer.importAnnotation(JSON.parse(exportedObjectString));
60+
}
61+
}
62+
{% endraw %}
63+
{% endhighlight %}
64+
{% endtabs %}
65+
66+
## Hide or Show All Annotations
67+
68+
Use `deleteAnnotations()` to remove **all** annotations, then re-import them from cache to show again.
69+
70+
**Minimal snippet (toggle all using cache):**
71+
72+
{% tabs %}
73+
{% highlight js tabtitle="Standalone" %}
74+
{% raw %}
75+
let __cache = null;
76+
let __areHidden = false;
77+
78+
async function toggleAllAnnotations() {
79+
const viewer = document.getElementById('container').ej2_instances[0];
80+
81+
if (!__areHidden) {
82+
__cache = await viewer.exportAnnotationsAsObject();
83+
viewer.deleteAnnotations();
84+
__areHidden = true;
85+
} else {
86+
if (__cache) viewer.importAnnotation(JSON.parse(__cache));
87+
__areHidden = false;
88+
}
89+
}
90+
{% endraw %}
91+
{% endhighlight %}
92+
{% endtabs %}
93+
94+
## Conditional Visibility (Annotations by Type)
95+
96+
To hide a **type** (e.g., all text markups) while keeping others, export to cache, clear the UI, then **re-import only the allowed types**. The export payload is JSON; filter by each type’s bucket before importing. (Structure may vary by version—adjust keys accordingly.)
97+
98+
{% tabs %}
99+
{% highlight js tabtitle="Standalone" %}
100+
{% raw %}
101+
// Example: hide all Text Markup (Highlight, Underline, Strikethrough, Squiggly)
102+
async function hideTextMarkup() {
103+
const viewer = document.getElementById('container').ej2_instances[0];
104+
105+
const exportObject = await viewer.exportAnnotationsAsObject();
106+
viewer.deleteAnnotations();
107+
108+
const parsed = JSON.parse(exportObject);
109+
const filtered = {
110+
...parsed,
111+
textMarkupAnnotations: [] // Key name depends on version; inspect your export object
112+
};
113+
114+
viewer.importAnnotation(filtered);
115+
}
116+
{% endraw %}
117+
{% endhighlight %}
118+
{% endtabs %}
119+
120+
## Role‑based Visibility Control
121+
122+
Implement role filters on top of the export/delete/import strategy:
123+
124+
- **Admins →** see **all annotations**
125+
- **Editors →** see **all markup**
126+
- **Viewers →** see **only comments or approved stamps**
127+
128+
Use per‑annotation metadata (e.g., `subject`, `author`, `customData`) to include/exclude what each role can view.
129+
130+
{% tabs %}
131+
{% highlight js tabtitle="Standalone" %}
132+
{% raw %}
133+
// Example: 'Viewer' role → only Approved stamps
134+
async function applyViewerRole() {
135+
const viewer = document.getElementById('container').ej2_instances[0];
136+
const exportObject = await viewer.exportAnnotationsAsObject();
137+
const payload = JSON.parse(exportObject);
138+
139+
const roleFiltered = {
140+
...payload,
141+
textMarkupAnnotations: [],
142+
shapeAnnotations: [],
143+
freeTextAnnotations: [],
144+
inkAnnotations: [],
145+
stampAnnotations: (payload.stampAnnotations || []).filter(s =>
146+
s.subject === 'Approved' || s.customData?.status === 'approved'
147+
)
148+
};
149+
150+
viewer.deleteAnnotations();
151+
viewer.importAnnotation(roleFiltered);
152+
}
153+
{% endraw %}
154+
{% endhighlight %}
155+
{% endtabs %}
156+
157+
## Toggle Annotation Visibility (UI Button Example)
158+
159+
For a simple UI toggle (single button), reuse the **toggle all** function above.
160+
161+
## Events Related to Visibility
162+
163+
There aren’t built‑in `annotationShown`/`annotationHidden` events. Use **export/import** lifecycle events to infer visibility changes (e.g., `exportStart`/`exportSuccess` when hiding, and `importStart`/`importSuccess` when showing). For all annotation‑level events (add/remove/select/resize/etc.), see **Annotation Events** (link below).
164+
165+
**Tiny example:**
166+
167+
{% tabs %}
168+
{% highlight js tabtitle="Standalone" %}
169+
{% raw %}
170+
function wireVisibilityEvents() {
171+
const viewer = document.getElementById('container').ej2_instances[0];
172+
173+
viewer.exportStart = () => console.log('annotationHidden:start');
174+
viewer.exportSuccess = () => console.log('annotationHidden:success');
175+
176+
viewer.importStart = () => console.log('annotationShown:start');
177+
viewer.importSuccess = () => console.log('annotationShown:success');
178+
}
179+
{% endraw %}
180+
{% endhighlight %}
181+
{% endtabs %}
182+
183+
## Control annotation visibility only in the exported PDF
184+
185+
If you want annotations **visible in your app** but **hidden in the saved PDF**, set the **`PdfAnnotationFlag.noView`** flag on each annotation before download using the EJ2 PDF library.
186+
187+
{% tabs %}
188+
{% highlight js tabtitle="Standalone" %}
189+
{% raw %}
190+
import { PdfAnnotationFlag, PdfDocument } from '@syncfusion/ej2-pdf';
191+
192+
// Download the current PDF with annotations flagged as `noView`
193+
async function downloadWithNoView() {
194+
const viewer = document.getElementById('container').ej2_instances[0];
195+
196+
const blob = await viewer.saveAsBlob();
197+
const reader = new FileReader();
198+
reader.onload = function () {
199+
const base64 = String(reader.result).split('base64,')[1];
200+
const doc = new PdfDocument(base64);
201+
202+
for (let i = 0; i < doc.pageCount; i++) {
203+
const page = doc.getPage(i);
204+
for (let j = 0; j < page.annotations.count; j++) {
205+
const annot = page.annotations.at(j);
206+
annot.flags |= PdfAnnotationFlag.noView; // hide in exported PDF
207+
}
208+
}
209+
210+
doc.saveAsBlob().then((modified) => {
211+
const linkReader = new FileReader();
212+
linkReader.onload = function () {
213+
const a = document.createElement('a');
214+
a.href = linkReader.result;
215+
a.download = 'without-annotations.pdf';
216+
a.click();
217+
};
218+
linkReader.readAsDataURL(modified.blobData);
219+
});
220+
};
221+
reader.readAsDataURL(blob);
222+
}
223+
{% endraw %}
224+
{% endhighlight %}
225+
{% endtabs %}
226+
227+
[View sample in GitHub](https://github.com/SyncfusionExamples/react-pdf-viewer-examples/tree/master/How%20to)
228+
229+
## See also
230+
231+
- [Annotation Overview](../overview)
232+
- [Show or hide annotations in React PDF Viewer](./show-hide-annotation)
233+
- [Import and export annotations](../annotation/export-import/export-annotation)

0 commit comments

Comments
 (0)