private async Task AssertPlainTextRedactionOccurredFor(RemoteWorkFile result, string expectedLineEndings) { using (var memoryStream = new MemoryStream()) { await result.CopyToAsync(memoryStream); memoryStream.Position = 0; using (var reader = new StreamReader(memoryStream)) { string text = reader.ReadToEnd(); // Quick sanity check to verify redaction actually occurred Assert.IsTrue(text.Contains("Peter Parker"), "Hmm, text content we expected to be in the output document was not present. Did something go wrong?"); Assert.IsFalse(text.Contains("*****@*****.**"), "Content that was expected to be redacted was not actually redacted!"); Assert.IsTrue(text.Contains("<Text Redacted>"), "Expected to find an occurrence of the string \"<Text Redacted>\", but didn't!"); if (expectedLineEndings == "\r\n") { Assert.IsTrue(text.Contains("\r\n")); } else if (expectedLineEndings == "\n") { Assert.IsFalse(text.Contains("\r\n")); } else { throw new ArgumentException("expectedLineEndings must be either \"\\r\\n\" or \"\\n\".", "expectedLineEndings"); } } } }
public async Task GetInstanceWithAffinity_works() { // Arrange AffinitySession session1 = Util.RestClient.CreateAffinitySession(); AffinitySession session2 = Util.RestClient.CreateAffinitySession(); RemoteWorkFile file1 = await session1.UploadAsync("documents/confidential-contacts.pdf"); RemoteWorkFile file2 = await session2.UploadAsync("documents/confidential-contacts.pdf.markup.json"); Assert.AreNotEqual(file1.AffinityToken, file2.AffinityToken); // Act RemoteWorkFile file2Reuploaded = await file2.GetInstanceWithAffinity(session2, file1.AffinityToken); // Assert Assert.AreEqual(file2.FileExtension, file2Reuploaded.FileExtension, "The FileExtension was not set correctly after reupload!"); Assert.AreEqual(file1.AffinityToken, file2Reuploaded.AffinityToken, "The AffinityToken was not correct after reupload!"); using (var originalContent = new MemoryStream()) using (var reuploadedContent = new MemoryStream()) { await file2.CopyToAsync(originalContent); await file2Reuploaded.CopyToAsync(reuploadedContent); CollectionAssert.AreEqual(originalContent.ToArray(), reuploadedContent.ToArray()); } }
public async Task When_the_second_of_three_input_work_files_does_not_exist() { var remoteWorkFile0 = new RemoteWorkFile(null, "ML3AbF-qzIH5K9mVVxTlBX", "FCnaLL517YPRAnrcX2wlnKURpNPsp2d2pMPkcvCcpdY=", "docx"); var remoteWorkFile1 = new RemoteWorkFile(null, "S5uCdv7vnkTRzKKlTvhtaw", "FCnaLL517YPRAnrcX2wlnKURpNPsp2d2pMPkcvCcpdY=", "docx"); var remoteWorkFile2 = new RemoteWorkFile(null, "5J15gtlduA_xORR8j7ejSg", "FCnaLL517YPRAnrcX2wlnKURpNPsp2d2pMPkcvCcpdY=", "docx"); var input0 = new ConversionSourceDocument(remoteWorkFile0); var input1 = new ConversionSourceDocument(remoteWorkFile1, pages: "2-"); var input2 = new ConversionSourceDocument(remoteWorkFile2); mockServer .Given(Request.Create().WithPath("/v2/contentConverters").UsingPost()) .RespondWith(Response.Create() .WithStatusCode(480) .WithHeader("Content-Type", "application/json") .WithBody("{\"input\":{\"dest\":{\"format\":\"pdf\",\"pdfOptions\":{\"forceOneFilePerPage\":false}},\"sources\":[{\"fileId\":\"LxuuLktmmMaicAs1wMvvsQ\",\"pages\":\"\"},{\"fileId\":\"S5uCdv7vnkTRzKKlTvhtaw\",\"pages\":\"2-\"},{\"fileId\":\"5J15gtlduA_xORR8j7ejSg\",\"pages\":\"\"}]},\"minSecondsAvailable\":18000,\"errorCode\":\"WorkFileDoesNotExist\",\"errorDetails\":{\"in\":\"body\",\"at\":\"input.sources[1].fileId\"}}")); await UtilAssert.ThrowsExceptionWithMessageAsync <RestApiErrorException>( async() => { await prizmDocServer.ConvertAsync( new List <ConversionSourceDocument> { input0, input1, input2, }, new DestinationOptions(DestinationFileFormat.Pdf)); }, "ConversionSourceDocument at index 1 refers to a remote work file which does not exist. It may have expired."); }
public void ToString_omits_FileExtension_when_null() { string toStringValue = new RemoteWorkFile(null, "fileId", "affinityToken", null).ToString(); StringAssert.Contains(toStringValue, "FileId: fileId"); StringAssert.Contains(toStringValue, "AffinityToken: affinityToken"); Assert.IsFalse(toStringValue.Contains("FileExtension:")); }
public void ToString_includes_FileId_AffinityToken_and_FileExtension() { string toStringValue = new RemoteWorkFile(null, "fileId", "affinityToken", "pdf").ToString(); StringAssert.Contains(toStringValue, "FileId: fileId"); StringAssert.Contains(toStringValue, "AffinityToken: affinityToken"); StringAssert.Contains(toStringValue, "FileExtension: pdf"); }
private async Task AssertRedactionOccurredFor(RemoteWorkFile result) { // Quick sanity check to verify redaction actually occurred string[] pagesText = await TextUtil.ExtractPagesText(result); Assert.IsTrue(pagesText[0].Contains("Peter Parker"), "Hmm, text content we expected to be in the output document was not present. Did something go wrong?"); Assert.IsFalse(pagesText[0].Contains("*****@*****.**"), "Content that was expected to be redacted was not actually redacted!"); Assert.IsTrue(pagesText[0].Contains("(b)(6)"), "Expected redaction reason was not present!"); }
public async Task Can_use_local_file_paths_for_both_document_and_markup_JSON() { // Arrange PrizmDocServerClient prizmDocServer = Util.CreatePrizmDocServerClient(); // Act RemoteWorkFile result = await prizmDocServer.RedactToPlainTextAsync("documents/confidential-contacts.pdf", "documents/confidential-contacts.pdf.markup.json", "\n"); // Assert await this.AssertPlainTextRedactionOccurredFor(result, "\n"); }
public async Task Can_use_carriage_return_and_newline_for_line_endings() { // Arrange PrizmDocServerClient prizmDocServer = Util.CreatePrizmDocServerClient(); // Act RemoteWorkFile result = await prizmDocServer.RedactToPlainTextAsync("documents/confidential-contacts.pdf", "documents/confidential-contacts.pdf.markup.json", "\r\n"); // Assert await this.AssertPlainTextRedactionOccurredFor(result, "\r\n"); }
public async Task UploadAsync_with_local_file_path_followed_by_SaveAsync_roundtrip_works() { PrizmDocServerClient prizmDocServer = Util.CreatePrizmDocServerClient(); const string INPUT_FILENAME = "documents/example.docx"; const string OUTPUT_FILENAME = "downloaded.docx"; RemoteWorkFile remoteWorkFile = await prizmDocServer.UploadAsync(INPUT_FILENAME); await remoteWorkFile.SaveAsync(OUTPUT_FILENAME); CollectionAssert.AreEqual(File.ReadAllBytes(INPUT_FILENAME), File.ReadAllBytes(OUTPUT_FILENAME)); }
public async Task Can_use_RemoteWorkFile_for_document_and_local_file_path_for_markup_JSON() { // Arrange PrizmDocServerClient prizmDocServer = Util.CreatePrizmDocServerClient(); AffinitySession affinitySession = Util.RestClient.CreateAffinitySession(); RemoteWorkFile document = await affinitySession.UploadAsync("documents/confidential-contacts.pdf"); // Act RemoteWorkFile result = await prizmDocServer.RedactToPlainTextAsync(document, "documents/confidential-contacts.pdf.markup.json", "\n"); // Assert await this.AssertPlainTextRedactionOccurredFor(result, "\n"); }
/// <summary> /// Extracts text for each page, returning a string of plain text for each page in a RemoteWorkFile. /// </summary> public static async Task <string[]> ExtractPagesText(RemoteWorkFile remoteWorkFile) { AffinitySession session = Util.RestClient.CreateAffinitySession(); HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, "/v2/searchContexts"); if (remoteWorkFile.AffinityToken != null) { req.Headers.Add("Accusoft-Affinity-Token", remoteWorkFile.AffinityToken); } req.Content = new StringContent( @"{ ""input"": { ""documentIdentifier"": """ + remoteWorkFile.FileId + @""", ""source"": ""workFile"", ""fileId"": """ + remoteWorkFile.FileId + @""" } }", Encoding.UTF8, "application/json"); string json; using (HttpResponseMessage res = await session.SendAsync(req)) { res.EnsureSuccessStatusCode(); json = await res.Content.ReadAsStringAsync(); } JObject process = JObject.Parse(json); string contextId = (string)process["contextId"]; using (HttpResponseMessage res = await session.GetFinalProcessStatusAsync("/v2/searchContexts/" + contextId)) { res.EnsureSuccessStatusCode(); } using (HttpResponseMessage res = await session.GetAsync($"/v2/searchContexts/{contextId}/records?pages=0-")) { res.EnsureSuccessStatusCode(); json = await res.Content.ReadAsStringAsync(); } JObject data = JObject.Parse(json); JArray pages = (JArray)data["pages"]; return(pages.Select(x => (string)x["text"]).ToArray()); }
public async Task BurnMarkupAsync_fails_with_a_useful_error_message_when_the_source_document_cannot_be_found() { PrizmDocServerClient prizmDocServer = Util.CreatePrizmDocServerClient(); AffinitySession affinitySession = Util.RestClient.CreateAffinitySession(); RemoteWorkFile existingMarkupFile = await affinitySession.UploadAsync("documents/confidential-contacts.pdf.markup.json"); RemoteWorkFile nonExistentSourceDocument = new RemoteWorkFile(affinitySession, "non-existent-id", existingMarkupFile.AffinityToken, "pdf"); await UtilAssert.ThrowsExceptionWithMessageAsync <RestApiErrorException>( async() => { await prizmDocServer.BurnMarkupAsync(nonExistentSourceDocument, existingMarkupFile); }, "Could not use the given RemoteWorkFile as the source document: the work file resource could not be found on the remote server. It may have expired."); }
public async Task When_work_file_does_not_exist() { PrizmDocServerClient prizmDocServer = Util.CreatePrizmDocServerClient(); AffinitySession affinitySession = Util.RestClient.CreateAffinitySession(); RemoteWorkFile validWorkFile = await affinitySession.UploadAsync("documents/confidential-contacts.pdf"); RemoteWorkFile invalidWorkFile = new RemoteWorkFile(affinitySession, "non-existent-id", validWorkFile.AffinityToken, "pdf"); await UtilAssert.ThrowsExceptionWithMessageAsync <RestApiErrorException>( async() => { await prizmDocServer.CreateRedactionsAsync(invalidWorkFile, new[] { new RegexRedactionMatchRule("dummy rule") }); }, "Could not use the given RemoteWorkFile as the source document: the work file resource could not be found on the remote server. It may have expired."); }
public async Task UploadAsync_with_local_file_path_followed_by_CopyToAsync_roundtrip_works() { PrizmDocServerClient prizmDocServer = Util.CreatePrizmDocServerClient(); const string INPUT_FILENAME = "documents/example.docx"; RemoteWorkFile remoteWorkFile = await prizmDocServer.UploadAsync(INPUT_FILENAME); using (var memoryStream = new MemoryStream()) { await remoteWorkFile.CopyToAsync(memoryStream); CollectionAssert.AreEqual(File.ReadAllBytes(INPUT_FILENAME), memoryStream.ToArray()); } }
public async Task Will_reupload_an_existing_RemoteWorkFile_when_the_affinity_is_wrong() { AffinitySession session1 = Util.RestClient.CreateAffinitySession(); AffinitySession session2 = Util.RestClient.CreateAffinitySession(); RemoteWorkFile file1; using (var stream = new MemoryStream(Encoding.UTF8.GetBytes("File 1"))) { file1 = await session1.UploadAsync(stream); } RemoteWorkFile file2; using (var stream = new MemoryStream(Encoding.UTF8.GetBytes("File 2"))) { file2 = await session2.UploadAsync(stream); } Assert.AreNotEqual(file1.AffinityToken, file2.AffinityToken); var source2 = new ConversionSourceDocument(file2); RemoteWorkFile originalRemoteWorkFile = source2.RemoteWorkFile; Assert.AreEqual(file2, originalRemoteWorkFile); // Ensure file2 is re-uploaded to the same machine as file1... await source2.EnsureUsableRemoteWorkFileAsync(Util.RestClient.CreateAffinitySession(), affinityToken : file1.AffinityToken); // Verify source RemoteWorkFile assignment was changed to something new Assert.AreNotEqual(source2.RemoteWorkFile, originalRemoteWorkFile); // Verify the affinity token of file1 and source2.RemoteWorkFile now match Assert.AreEqual(file1.AffinityToken, source2.RemoteWorkFile.AffinityToken); // Verify the contents of the file are still correct using (var stream = new MemoryStream()) { await source2.RemoteWorkFile.CopyToAsync(stream); stream.Position = 0; using (var reader = new StreamReader(stream, Encoding.UTF8)) { string text = reader.ReadToEnd(); Assert.AreEqual("File 2", text); } } }
public async Task Can_use_local_file_path_for_document_and_RemoteWorkFile_for_markup_JSON() { // Arrange PrizmDocServerClient prizmDocServer = Util.CreatePrizmDocServerClient(); AffinitySession affinitySession = Util.RestClient.CreateAffinitySession(); RemoteWorkFile markupJson = await affinitySession.UploadAsync("documents/confidential-contacts.pdf.markup.json"); // Act RemoteWorkFile result = await prizmDocServer.BurnMarkupAsync("documents/confidential-contacts.pdf", markupJson); // Assert await result.SaveAsync("burned.pdf"); await this.AssertRedactionOccurredFor(result); }
public async Task When_single_input_work_file_does_not_exist() { mockServer .Given(Request.Create().WithPath("/v2/contentConverters").UsingPost()) .RespondWith(Response.Create() .WithStatusCode(480) .WithHeader("Content-Type", "application/json") .WithBody("{\"input\":{\"dest\":{\"format\":\"pdf\",\"pdfOptions\":{\"forceOneFilePerPage\":false}},\"sources\":[{\"fileId\":\"ML3AbF-qzIH5K9mVVxTlBX\",\"pages\":\"\"}]},\"minSecondsAvailable\":18000,\"errorCode\":\"WorkFileDoesNotExist\",\"errorDetails\":{\"in\":\"body\",\"at\":\"input.sources[0].fileId\"}}")); var originalRemoteWorkFile = new RemoteWorkFile(null, "ML3AbF-qzIH5K9mVVxTlBX", "FCnaLL517YPRAnrcX2wlnKURpNPsp2d2pMPkcvCcpdY=", "docx"); var originalConversionInput = new ConversionSourceDocument(originalRemoteWorkFile); await UtilAssert.ThrowsExceptionWithMessageAsync <RestApiErrorException>( async() => { await prizmDocServer.ConvertAsync(originalConversionInput, new DestinationOptions(DestinationFileFormat.Pdf)); }, "ConversionSourceDocument refers to a remote work file which does not exist. It may have expired."); }
public async Task Can_use_RemoteWorkFile_instances_with_different_affinity() { // Arrange PrizmDocServerClient prizmDocServer = Util.CreatePrizmDocServerClient(); AffinitySession session1 = Util.RestClient.CreateAffinitySession(); AffinitySession session2 = Util.RestClient.CreateAffinitySession(); RemoteWorkFile document = await session1.UploadAsync("documents/confidential-contacts.pdf"); RemoteWorkFile markupJson = await session2.UploadAsync("documents/confidential-contacts.pdf.markup.json"); Assert.AreNotEqual(document.AffinityToken, markupJson.AffinityToken); // Act RemoteWorkFile result = await prizmDocServer.RedactToPlainTextAsync(document, markupJson, "\n"); // Assert await this.AssertPlainTextRedactionOccurredFor(result, "\n"); }
public async Task Work_file_reuploading_works_correctly_and_number_of_reuploads_is_minimized() { // Arrange RemoteWorkFile wf1 = await this.UploadPlainTextAsync(Util.RestClient.CreateAffinitySession(), "File 1"); RemoteWorkFile wf2 = await this.UploadPlainTextAsync(Util.RestClient.CreateAffinitySession(), "File 2"); // Make sure we get at least one distinct affinity token int i = 2; while (wf1.AffinityToken == wf2.AffinityToken && i < 100) { wf2 = await this.UploadPlainTextAsync(Util.RestClient.CreateAffinitySession(), $"File 2"); i++; } // Try to create some files with the same affinity AffinitySession affinitySession = Util.RestClient.CreateAffinitySession(); RemoteWorkFile wf3 = await this.UploadPlainTextAsync(affinitySession, "File 3"); RemoteWorkFile wf4 = await this.UploadPlainTextAsync(affinitySession, "File 4"); RemoteWorkFile wf5 = await this.UploadPlainTextAsync(affinitySession, "File 5"); RemoteWorkFile wf6 = await this.UploadPlainTextAsync(affinitySession, "File 6"); RemoteWorkFile wf7 = await this.UploadPlainTextAsync(affinitySession, "File 7"); RemoteWorkFile wf8 = await this.UploadPlainTextAsync(affinitySession, "File 8"); RemoteWorkFile wf9 = await this.UploadPlainTextAsync(affinitySession, "File 9"); RemoteWorkFile wf10 = await this.UploadPlainTextAsync(affinitySession, "File 10"); var doc1 = new ConversionSourceDocument(wf1); var doc2 = new ConversionSourceDocument(wf2); var doc3 = new ConversionSourceDocument(wf3); var doc4 = new ConversionSourceDocument(wf4); var doc5 = new ConversionSourceDocument(wf5); var doc6 = new ConversionSourceDocument(wf6); var doc7 = new ConversionSourceDocument(wf7); var doc8 = new ConversionSourceDocument(wf8); var doc9 = new ConversionSourceDocument(wf9); var doc10 = new ConversionSourceDocument(wf10); var sourceDocuments = new[] { doc1, doc2, doc3, doc4, doc5, doc6, doc7, doc8, doc9, doc10 }; // Validate that we actually have distinct affinity IEnumerable <string> distinctAffinityTokensBefore = sourceDocuments.Select(x => x.RemoteWorkFile.AffinityToken).Distinct(); Assert.IsTrue(distinctAffinityTokensBefore.Count() > 1); string mostFrequentAffinityToken = sourceDocuments.GroupBy(x => x.RemoteWorkFile.AffinityToken).OrderByDescending(x => x.Count()).Select(x => x.Key).First(); // Act ConversionResult output = await Util.CreatePrizmDocServerClient().CombineToPdfAsync(sourceDocuments); // Assert that the ConversionSourceDocument instances all now have RemoteWorkFile instances with the same affinity token. IEnumerable <string> distinctAffinityTokensAfter = sourceDocuments.Select(x => x.RemoteWorkFile.AffinityToken).Distinct(); Assert.AreEqual(1, distinctAffinityTokensAfter.Count()); Assert.AreEqual(mostFrequentAffinityToken, distinctAffinityTokensAfter.Single()); string outputFileText = string.Join("\n", await TextUtil.ExtractPagesText(output.RemoteWorkFile)); Assert.AreEqual(@"File 1File 2File 3File 4File 5File 6File 7File 8File 9File 10", outputFileText.Replace("\r", string.Empty).Replace("\n", string.Empty)); }
internal ConversionResult(RemoteWorkFile remoteWorkFile, int pageCount, IEnumerable <ConversionSourceDocument> sources) { this.remoteWorkFile = remoteWorkFile ?? throw new ArgumentNullException("remoteWorkFile"); this.PageCount = pageCount; this.Sources = sources ?? throw new ArgumentNullException("sources"); }
public async Task Can_create_redactions() { // Arrange PrizmDocServerClient prizmDocServer = Util.CreatePrizmDocServerClient(); var ssn = new RegexRedactionMatchRule(@"\d\d\d-\d\d-\d\d\d\d") { RedactWith = new RedactionCreationOptions() { Reason = "(b)(6)", Data = new Dictionary <string, string> { { "rule", "SSN" }, }, }, }; var email = new RegexRedactionMatchRule(@"\S+@\S+\.\S+") { RedactWith = new RedactionCreationOptions() { Reason = "(b)(6)", Data = new Dictionary <string, string> { { "rule", "email" }, }, }, }; var bruceWayne = new RegexRedactionMatchRule(@"Bruce Wayne") { RedactWith = new RedactionCreationOptions() { Reason = "Not Batman", }, }; var rules = new[] { ssn, email, bruceWayne, }; // Act RemoteWorkFile result = await prizmDocServer.CreateRedactionsAsync("documents/confidential-contacts.pdf", rules); // Assert: Verify the expected redactions were created for the test document JObject markup; using (var memoryStream = new MemoryStream()) { await result.CopyToAsync(memoryStream); string markupJson = Encoding.ASCII.GetString(memoryStream.ToArray()); markup = JObject.Parse(markupJson); } List <JToken> marks = markup["marks"].Children().ToList(); List <JToken> redactions = marks.Where(x => (string)x["type"] == "RectangleRedaction").ToList(); List <JToken> firstPageRedactions = redactions.Where(x => (int)x["pageNumber"] == 1).ToList(); List <JToken> secondPageRedactions = redactions.Where(x => (int)x["pageNumber"] == 2).ToList(); List <JToken> firstPageSsnRedactions = firstPageRedactions.Where(x => x["data"] != null && (string)x["data"]["rule"] == "SSN").ToList(); List <JToken> secondPageSsnRedactions = secondPageRedactions.Where(x => x["data"] != null && (string)x["data"]["rule"] == "SSN").ToList(); List <JToken> firstPageEmailRedactions = firstPageRedactions.Where(x => x["data"] != null && (string)x["data"]["rule"] == "email").ToList(); List <JToken> secondPageEmailRedactions = secondPageRedactions.Where(x => x["data"] != null && (string)x["data"]["rule"] == "email").ToList(); List <JToken> bruceWayneRedactions = redactions.Where(x => (string)x["reason"] == "Not Batman").ToList(); Assert.AreEqual(18, marks.Count); Assert.AreEqual(18, redactions.Count); Assert.AreEqual(13, firstPageRedactions.Count); Assert.AreEqual(5, secondPageRedactions.Count); Assert.AreEqual(6, firstPageSsnRedactions.Count); Assert.AreEqual(3, secondPageSsnRedactions.Count); Assert.AreEqual(6, firstPageEmailRedactions.Count); Assert.AreEqual(2, secondPageEmailRedactions.Count); Assert.AreEqual(1, bruceWayneRedactions.Count); }
/// <summary> /// Initializes a new instance of the <see cref="ConversionSourceDocument"/> class for an existing remote work file. /// </summary> /// <param name="remoteWorkFile">Remote work file to use as a source document.</param> /// <param name="pages">When provided, causes the conversion to only use a specified set of pages from the source document. /// Page numbers are 1-indexed. /// You can think of this argument like a "pages" input text field in a typical print dialog box. /// For example, the value can be a single page like <c>"2"</c>, /// a comma-delimited list of specific pages like <c>"1, 4, 5"</c>, /// an open-ended page range like <c>"2-"</c> (page 2 through the end of the document), /// or a combination of these, like <c>"2, 4-9, 12-"</c>. /// </param> /// <param name="password">Password to open the document. Only required if the document requires a password to open.</param> public ConversionSourceDocument(RemoteWorkFile remoteWorkFile, string pages = null, string password = null) { this.RemoteWorkFile = remoteWorkFile ?? throw new ArgumentNullException("remoteWorkFile"); this.Pages = pages; this.Password = password; }
private static async Task MainAsync() { // Delete any existing output file before we get started. File.Delete("redacted.pdf"); var prizmDocServer = new PrizmDocServerClient(Environment.GetEnvironmentVariable("BASE_URL"), Environment.GetEnvironmentVariable("API_KEY")); // ----------------------------------------------------------------- // Step 1: Create markup JSON containing definitions of the areas we // want to redact. // ----------------------------------------------------------------- // Define a rule which will create a redaction for any text in a // document which looks like a social security number (###-##-####), // and use the text "(b)(6)" in the center of the redaction // rectangle as the reason for redaction. var ssnRule = new RegexRedactionMatchRule(@"\d\d\d-\d\d-\d\d\d\d") { RedactWith = new RedactionCreationOptions() { Reason = "(b)(6)", }, }; // Define a rule which will create a redaction for any text in a // document which looks like an email address (this is a very basic // regex, matching things like [email protected]) and use the // text "(b)(6)" in the center of the redaction rectangle as // the reason for redaction. var emailRule = new RegexRedactionMatchRule(@"\S+@\S+\.\S+") { RedactWith = new RedactionCreationOptions() { Reason = "(b)(6)", }, }; // Define a rule which will create a redaction for all occurrences // of "Bruce Wayne" in a document, use the text "(b)(1)" in the // center of the redaction rectangle as the reason for redaction, // customize various colors used, and attach some arbitrary // key/value string data to all redaction definitions which are // created. This arbitrary data will be present in the output markup // JSON file. var bruceWayneRule = new RegexRedactionMatchRule(@"Bruce Wayne") { RedactWith = new RedactionCreationOptions() { Reason = "(b)(1)", FontColor = "#FDE311", FillColor = "#000080", BorderColor = "#000000", BorderThickness = 2, // This arbitrary data will simply be present in the generated markup JSON. Data = new Dictionary <string, string> { { "arbitrary-key-1", "arbitrary-value-1" }, { "arbitrary-key-2", "arbitrary-value-2" }, }, }, }; var rules = new[] { ssnRule, emailRule, bruceWayneRule }; // Automatically create a markup.json file with redaction // definitions based upon regular expression rules for a given // document. Any text in the document which matches one of the regex // rules will have a redaction definition created for that portion // of the document. The output markup.json file with its redaction // definitions can later be burned into the document. RemoteWorkFile markupJson = await prizmDocServer.CreateRedactionsAsync("confidential-contacts.pdf", rules); // ----------------------------------------------------------------- // Step 2: Burn the markup JSON into the original document, // producing a new, redacted PDF. // ----------------------------------------------------------------- RemoteWorkFile redactedPdf = await prizmDocServer.BurnMarkupAsync("confidential-contacts.pdf", markupJson); // Save the result to "redacted.pdf" await redactedPdf.SaveAsync("redacted.pdf"); }