diff --git a/playground/redaction/apply-reject-redactions/index.ts b/playground/redaction/apply-reject-redactions/index.ts new file mode 100644 index 0000000..ad01a0f --- /dev/null +++ b/playground/redaction/apply-reject-redactions/index.ts @@ -0,0 +1,74 @@ +import type { + AnnotationTooltipCallback, + Instance, + RedactionAnnotation, +} from "@nutrient-sdk/viewer"; +import { baseOptions } from "../../shared/base-options"; + +let instance: Instance | null = null; + +const getAllAnnotations = async () => { + if (!instance) + return window.NutrientViewer.Immutable.List(); + + let annotationsList = + window.NutrientViewer.Immutable.List(); + + for (let i = 0; i < instance.totalPageCount; i++) { + const anns = (await instance.getAnnotations(i)).filter( + (a): a is RedactionAnnotation => + a instanceof window.NutrientViewer.Annotations.RedactionAnnotation, + ); + annotationsList = annotationsList.concat(anns); + } + return annotationsList; +}; + +const redactionAnnotationsHandlerCallback: AnnotationTooltipCallback = ( + annotation, +) => + annotation instanceof window.NutrientViewer.Annotations.RedactionAnnotation + ? [ + { + type: "custom", + title: "Accept", + id: "tooltip-accept-annotation", + className: "TooltipItem-Duplication", + onPress: async () => { + if (!instance) return; + + const allRedactionAnnotations = (await getAllAnnotations()).filter( + (a) => a.id !== annotation.id, + ); + + await instance.delete(allRedactionAnnotations); + await instance.applyRedactions(); + await instance.create(allRedactionAnnotations); + }, + }, + { + type: "custom", + title: "Reject", + id: "tooltip-reject-annotation", + className: "TooltipItem-Duplication", + onPress: async () => { + if (!instance) return; + instance.delete(annotation); + }, + }, + ] + : []; + +window.NutrientViewer.load({ + ...baseOptions, + theme: window.NutrientViewer.Theme.DARK, + enableAutomaticLinkExtraction: true, + annotationTooltipCallback: redactionAnnotationsHandlerCallback, + toolbarItems: [ + ...window.NutrientViewer.defaultToolbarItems, + { type: "redact-rectangle" }, + { type: "redact-text-highlighter" }, + ], +}).then((_instance) => { + instance = _instance; +}); diff --git a/playground/redaction/apply-reject-redactions/playground.mdx b/playground/redaction/apply-reject-redactions/playground.mdx new file mode 100644 index 0000000..e97425a --- /dev/null +++ b/playground/redaction/apply-reject-redactions/playground.mdx @@ -0,0 +1,6 @@ +--- +category: redaction +title: Apply or Reject Redactions via Tooltip +description: Adds custom tooltip options to accept or reject individual redaction annotations, allowing selective redaction of document content. +keywords: [redaction, tooltip, accept, reject, annotations, custom-tooltip] +--- diff --git a/playground/redaction/apply-reject-redactions/styles.css b/playground/redaction/apply-reject-redactions/styles.css new file mode 100644 index 0000000..8fcb9b0 --- /dev/null +++ b/playground/redaction/apply-reject-redactions/styles.css @@ -0,0 +1,4 @@ +/* Add your CSS here */ +.custom-toolbar-item { + height: 100%; +} diff --git a/playground/redaction/overlay-redactions/index.ts b/playground/redaction/overlay-redactions/index.ts new file mode 100644 index 0000000..9f62329 --- /dev/null +++ b/playground/redaction/overlay-redactions/index.ts @@ -0,0 +1,43 @@ +import type { RedactionAnnotation, TextLine } from "@nutrient-sdk/viewer"; +import { baseOptions } from "../../shared/base-options"; + +const REDACTION_CONFIG = { + color: window.NutrientViewer.Color.RED, + overlayText: "REDACTED", +}; + +function createRedactionFromTextLine(textLine: TextLine) { + const { boundingBox, pageIndex } = textLine; + + return new window.NutrientViewer.Annotations.RedactionAnnotation({ + id: window.NutrientViewer.generateInstantId(), + pageIndex, + boundingBox, + rects: window.NutrientViewer.Immutable.List([boundingBox]), + ...REDACTION_CONFIG, + }); +} + +window.NutrientViewer.load({ + ...baseOptions, + theme: window.NutrientViewer.Theme.DARK, +}).then(async (instance) => { + const pageIndices = Array.from({ length: instance.totalPageCount }); + + const allPagesTextLines = await Promise.all( + pageIndices.map((_, index) => instance.textLinesForPageIndex(index)), + ); + + const redactionAnnotations = allPagesTextLines.flatMap((pageTextLines) => + pageTextLines.reduce( + (annotations, textLine) => [ + ...annotations, + createRedactionFromTextLine(textLine), + ], + [], + ), + ); + + await instance.create(redactionAnnotations); + await instance.applyRedactions(); +}); diff --git a/playground/redaction/overlay-redactions/playground.mdx b/playground/redaction/overlay-redactions/playground.mdx new file mode 100644 index 0000000..25a4d18 --- /dev/null +++ b/playground/redaction/overlay-redactions/playground.mdx @@ -0,0 +1,6 @@ +--- +category: redaction +title: Overlay Redactions on All Text +description: Creates redactions with an overlay text for every text line in the document and applies them to fully redact all text content. +keywords: [redaction, overlay, text-lines, full-document, batch-redaction] +--- diff --git a/playground/redaction/redact-keywords-programmatically/index.ts b/playground/redaction/redact-keywords-programmatically/index.ts new file mode 100644 index 0000000..490fc95 --- /dev/null +++ b/playground/redaction/redact-keywords-programmatically/index.ts @@ -0,0 +1,40 @@ +import { baseOptions } from "../../shared/base-options"; + +const SENSITIVE_DATA = { + places: ["Koram", "NomSao", "Uganda", "Ngogo", "Thailand", "Myanmar"], + names: [ + "Lydia V Luncz", + "Amanda Tan", + "Michael Haslam", + "Lars Kulik", + "Tomos Proffitt", + "Suchinda Malaivijitnond", + "Michael Gumert", + ], +}; + +const REDACTION_SEARCH_OPTIONS = { + searchType: window.NutrientViewer.SearchType.TEXT, + searchInAnnotations: false, + caseSensitive: false, + startPageIndex: 0, +} as const; + +window.NutrientViewer.load({ + ...baseOptions, + theme: window.NutrientViewer.Theme.DARK, +}).then(async (instance) => { + await Promise.all( + SENSITIVE_DATA.places.map((place) => + instance.createRedactionsBySearch(place, REDACTION_SEARCH_OPTIONS), + ), + ); + + await Promise.all( + SENSITIVE_DATA.names.map((name) => + instance.createRedactionsBySearch(name, REDACTION_SEARCH_OPTIONS), + ), + ); + + await instance.applyRedactions(); +}); diff --git a/playground/redaction/redact-keywords-programmatically/playground.mdx b/playground/redaction/redact-keywords-programmatically/playground.mdx new file mode 100644 index 0000000..169f585 --- /dev/null +++ b/playground/redaction/redact-keywords-programmatically/playground.mdx @@ -0,0 +1,6 @@ +--- +category: redaction +title: Redact Keywords Programmatically +description: Automatically searches for and redacts sensitive information like place names and personal names using the createRedactionsBySearch API. +keywords: [redaction, search, keywords, sensitive-data, batch-redaction, programmatic] +---