Skip to content

Commit 9d9f7e7

Browse files
Merge pull request #995 from dynamsoft-docs/preview
New FAQ, link fixes and build scripts
2 parents 4de127b + 03334fc commit 9d9f7e7

7 files changed

Lines changed: 505 additions & 2 deletions

File tree

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,9 @@ sitemap.xml
4646
Hide_Tree_Page.md
4747
Gemfile.lock
4848
Gemfile
49+
50+
# Local development workspace
51+
.dev/
52+
.bundle/
53+
.jekyll-cache/
54+
.sass-cache/

_articles/faq/index.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ description: Dynamic Web TWAIN SDK Documentation FAQ
142142
8. [How can I separate my documents automatically by barcode?](/_articles/faq/separate-documents-by-barcode.md)
143143
9. [Is there any way to speed up the barcode reading process?](/_articles/faq/speed-up-barcode-reading-process.md)
144144
10. [What types of webcams does the Webcam Capture addon support](/_articles/faq/webcam-supported-by-webcam-capture-addon.md)
145+
11. [How can I prompt users for a password when loading a password-protected PDF?](/_articles/faq/prompt-password-for-protected-pdf.md)
145146

146147
## Project Deployment and End-user Installation
147148

@@ -228,4 +229,4 @@ description: Dynamic Web TWAIN SDK Documentation FAQ
228229

229230
## Annotation
230231

231-
1. [How can I add annotations to an image and then save/upload it?](/_articles/faq/dwt-with-annotation.md)
232+
1. [How can I add annotations to an image and then save/upload it?](/_articles/faq/dwt-with-annotation.md)
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
---
2+
layout: default-layout
3+
noTitleIndex: true
4+
needAutoGenerateSidebar: true
5+
title: How can I prompt users for a password when loading a password-protected PDF?
6+
keywords: Dynamic Web TWAIN, Addon, PDF, password, encrypted, LoadImageEx, OnPostLoad
7+
breadcrumbText: How can I prompt users for a password when loading a password-protected PDF?
8+
description: How can I prompt users for a password when loading a password-protected PDF?
9+
date: 2026-04-09 10:00:00 +0800
10+
last_modified: 2026-04-09 10:00:00 +0800
11+
---
12+
13+
# Addon
14+
15+
## How can I prompt users for a password when loading a password-protected PDF?
16+
17+
Dynamic Web TWAIN does not provide a built-in password prompt UI for encrypted PDFs.
18+
You need to handle the prompt and retry flow in your application code.
19+
20+
### Recommended workflow (`LoadImage()` / `LoadImageEx()`)
21+
22+
1. Call [`LoadImage()`](/_articles/info/api/WebTwain_IO.md#loadimage) or [`LoadImageEx()`](/_articles/info/api/WebTwain_IO.md#loadimageex).
23+
2. In the failure callback, treat the error as "password required" only when either condition is true:
24+
- (`errorCode` is `-1119` and `errorString` contains
25+
`"Failed to read the PDF file because it's encrypted and the correct password is not provided."`)
26+
- OR `errorCode` is `-1120`.
27+
3. Prompt the user for a password.
28+
4. Set the password via [`SetReaderOptions()`](/_articles/info/api/Addon_PDF.md#setreaderoptions).
29+
5. Retry loading the same PDF.
30+
31+
### Helper function for password-required errors
32+
33+
Use this helper in both API-based loading and drag-and-drop handling:
34+
35+
```javascript
36+
function isPasswordRequired(errorCode, errorString) {
37+
return (
38+
(errorCode === -1119 &&
39+
(errorString || "").includes(
40+
"Failed to read the PDF file because it's encrypted and the correct password is not provided.",
41+
)) ||
42+
errorCode === -1120
43+
);
44+
}
45+
```
46+
47+
### `LoadImage()` / `LoadImageEx()` example
48+
49+
```javascript
50+
function loadProtectedPdf(path, password) {
51+
DWTObject.Addon.PDF.SetReaderOptions({ password: password || "" });
52+
DWTObject.LoadImageEx(
53+
path,
54+
Dynamsoft.DWT.EnumDWT_ImageType.IT_PDF,
55+
function () {
56+
console.log("PDF loaded successfully.");
57+
},
58+
function (errorCode, errorString) {
59+
if (!isPasswordRequired(errorCode, errorString)) {
60+
console.error(errorCode, errorString);
61+
return;
62+
}
63+
64+
var userPassword = window.prompt(
65+
"This PDF is password-protected. Please enter the password:",
66+
"",
67+
);
68+
if (!userPassword) return;
69+
70+
loadProtectedPdf(path, userPassword);
71+
},
72+
);
73+
}
74+
```
75+
76+
### Drag-and-drop workflow (`OnPostLoad`)
77+
78+
Use [`OnPostLoad`](/_articles/info/api/WebTwain_IO.md#onpostload) and check `DWTObject.ErrorCode` / `DWTObject.ErrorString`.
79+
80+
```javascript
81+
DWTObject.RegisterEvent("OnPostLoad", function (path, name, type) {
82+
if (!isPasswordRequired(DWTObject.ErrorCode, DWTObject.ErrorString)) return;
83+
84+
var userPassword = window.prompt(
85+
"This PDF is password-protected. Please enter the password:",
86+
"",
87+
);
88+
if (!userPassword) return;
89+
90+
DWTObject.Addon.PDF.SetReaderOptions({ password: userPassword });
91+
92+
if (!path) {
93+
alert("Password saved. Please drag and drop the file again.");
94+
return;
95+
}
96+
97+
var fullPath = path + "\\" + name;
98+
loadProtectedPdf(fullPath, userPassword);
99+
});
100+
```
101+
102+
> [!NOTE]
103+
> `-1119` can represent multiple PDF load issues, so do not use it alone.
104+
> Starting in Dynamic Web TWAIN 19.4, encrypted-PDF load failures will use `-1120`.
105+
>
106+
> In `OnPostLoad`, `path` is empty for drag-and-drop files, so the code cannot directly retry with a file path.
107+
> In this case, prompt for password, save it with `SetReaderOptions()`, and ask the user to drag and drop the file again.

_articles/general-usage/server-side-scripting.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ description: Dynamic Web TWAIN SDK Documentation Server Scripts Page
1515

1616
As mentioned in our [image upload guide](/_articles/general-usage/image-export/server-upload.md), `Dynamic Web TWAIN` sends an HTTP POST request to the server when doing an upload. The file in the POST Form has the name `RemoteFile` by default. If you wish to change that, you can use [ `HttpFieldNameOfUploadedImage` ](/_articles/info/api/WebTwain_IO.md#httpfieldnameofuploadedimage).
1717

18-
The following assumes the default `RemoteFile` is used and that [extra Form fields](/_articles/general-usage/image-export/index.md#can-i-change-the-fields-of-the-http-form) might accompany the file.
18+
The following assumes the default `RemoteFile` is used and that [extra Form fields](/_articles/faq/additional-form-fields.md) might accompany the file.
1919

2020
### Upload via CSharp
2121

scripts/README.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Development Scripts
2+
3+
This folder contains local development setup scripts for this repository:
4+
5+
- `dev.ps1`: Windows PowerShell workflow
6+
- `dev.sh`: Linux/WSL Bash workflow
7+
8+
Both scripts do the same high-level flow:
9+
10+
1. Prepare a local `.dev/` workspace in this repo.
11+
2. Clone or sync `Docs-Template-Repo` into `.dev/Docs-Template-Repo`.
12+
3. Build a merged workspace in `.dev/DocHome` (docs + template).
13+
4. Replace `assetsPath` values in `_includes` and `_layouts`.
14+
5. Install gems with Bundler using local cache directories under `.dev/`.
15+
6. Run `jekyll serve` (or print the serve command when no-serve mode is used).
16+
17+
## Windows (`dev.ps1`)
18+
19+
Run from repo root:
20+
21+
```powershell
22+
powershell -ExecutionPolicy Bypass -File .\scripts\dev.ps1
23+
```
24+
25+
Useful options:
26+
27+
```powershell
28+
powershell -ExecutionPolicy Bypass -File .\scripts\dev.ps1 -NoServe
29+
powershell -ExecutionPolicy Bypass -File .\scripts\dev.ps1 -NoTemplateUpdate
30+
powershell -ExecutionPolicy Bypass -File .\scripts\dev.ps1 -Port 6001
31+
powershell -ExecutionPolicy Bypass -File .\scripts\dev.ps1 -BindHost 0.0.0.0
32+
```
33+
34+
## Linux / WSL (`dev.sh`)
35+
36+
Run from repo root:
37+
38+
```bash
39+
bash ./scripts/dev.sh
40+
```
41+
42+
Useful options:
43+
44+
```bash
45+
bash ./scripts/dev.sh --no-serve
46+
bash ./scripts/dev.sh --no-template-update
47+
bash ./scripts/dev.sh --port 6001
48+
bash ./scripts/dev.sh --bind-host 0.0.0.0
49+
```
50+
51+
## Output
52+
53+
Default local URL root after start:
54+
55+
```text
56+
http://localhost:5555/web-twain/docs/
57+
```

scripts/dev.ps1

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
[CmdletBinding()]
2+
param(
3+
[string]$TemplateRepoUrl = "https://github.com/dynamsoft-docs/Docs-Template-Repo.git",
4+
[string]$TemplateBranch = "preview",
5+
[int]$Port = 5555,
6+
[string]$BindHost = "localhost",
7+
[switch]$NoTemplateUpdate,
8+
[switch]$NoServe
9+
)
10+
11+
Set-StrictMode -Version Latest
12+
$ErrorActionPreference = "Stop"
13+
14+
function Write-Step {
15+
param([string]$Message)
16+
Write-Host "==> $Message"
17+
}
18+
19+
function Ensure-Command {
20+
param([string]$Name)
21+
if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) {
22+
throw "Required command '$Name' was not found in PATH."
23+
}
24+
}
25+
26+
function Invoke-Native {
27+
param(
28+
[Parameter(Mandatory = $true)]
29+
[string]$FilePath,
30+
[string[]]$Arguments = @()
31+
)
32+
33+
& $FilePath @Arguments
34+
$code = $LASTEXITCODE
35+
if ($code -ne 0) {
36+
$argText = ($Arguments -join " ")
37+
throw "Command failed with exit code ${code}: $FilePath $argText"
38+
}
39+
}
40+
41+
function Invoke-Robocopy {
42+
param(
43+
[string]$Source,
44+
[string]$Destination,
45+
[string[]]$ExtraArgs
46+
)
47+
48+
$baseArgs = @(
49+
$Source,
50+
$Destination,
51+
"/R:2",
52+
"/W:2",
53+
"/NFL",
54+
"/NDL",
55+
"/NJH",
56+
"/NJS",
57+
"/NP"
58+
)
59+
$allArgs = $baseArgs + $ExtraArgs
60+
& robocopy @allArgs | Out-Null
61+
$code = $LASTEXITCODE
62+
if ($code -ge 8) {
63+
throw "robocopy failed with exit code $code. Source: $Source Destination: $Destination"
64+
}
65+
}
66+
67+
Ensure-Command "git"
68+
Ensure-Command "robocopy"
69+
Ensure-Command "bundle"
70+
71+
$repoRoot = Split-Path -Parent $PSScriptRoot
72+
$devRoot = Join-Path $repoRoot ".dev"
73+
$templateRoot = Join-Path $devRoot "Docs-Template-Repo"
74+
$docHome = Join-Path $devRoot "DocHome"
75+
$bundlePath = Join-Path $devRoot "vendor\bundle"
76+
$bundleConfig = Join-Path $devRoot ".bundle"
77+
$bundleUserHome = Join-Path $devRoot ".bundle-user"
78+
$jekyllCache = Join-Path $devRoot ".jekyll-cache"
79+
$siteDir = Join-Path $devRoot "_site"
80+
81+
Write-Step "Preparing local workspace in $devRoot"
82+
New-Item -ItemType Directory -Force -Path $devRoot | Out-Null
83+
84+
if (-not (Test-Path $templateRoot)) {
85+
Write-Step "Cloning template repo ($TemplateBranch)"
86+
Invoke-Native -FilePath "git" -Arguments @("clone", "--depth", "1", "--branch", $TemplateBranch, $TemplateRepoUrl, $templateRoot)
87+
} elseif (-not $NoTemplateUpdate) {
88+
Write-Step "Syncing template repo ($TemplateBranch)"
89+
Invoke-Native -FilePath "git" -Arguments @("-C", $templateRoot, "fetch", "origin", $TemplateBranch, "--depth", "1")
90+
Invoke-Native -FilePath "git" -Arguments @("-C", $templateRoot, "reset", "--hard")
91+
Invoke-Native -FilePath "git" -Arguments @("-C", $templateRoot, "clean", "-fd")
92+
Invoke-Native -FilePath "git" -Arguments @("-C", $templateRoot, "checkout", "-B", $TemplateBranch, "origin/$TemplateBranch")
93+
} else {
94+
Write-Step "Skipping template update"
95+
}
96+
97+
Write-Step "Rebuilding merged site workspace"
98+
New-Item -ItemType Directory -Force -Path $docHome | Out-Null
99+
Invoke-Robocopy -Source $repoRoot -Destination $docHome -ExtraArgs @(
100+
"/MIR",
101+
"/XD", ".git", ".dev", ".vs", "node_modules", "_site", ".bundle", ".jekyll-cache", ".sass-cache", "vendor"
102+
)
103+
Invoke-Robocopy -Source $templateRoot -Destination $docHome -ExtraArgs @(
104+
"/E",
105+
"/XD", ".git", ".github", "_site", "node_modules"
106+
)
107+
108+
Write-Step "Applying local assetsPath replacements"
109+
$search = "assetsPath = '/webres/wwwroot'"
110+
$replace = "assetsPath = 'https://www.dynamsoft.com/webres/wwwroot'"
111+
foreach ($dirName in @("_includes", "_layouts")) {
112+
$dirPath = Join-Path $docHome $dirName
113+
if (-not (Test-Path $dirPath)) {
114+
continue
115+
}
116+
117+
Get-ChildItem -Path $dirPath -Recurse -File | ForEach-Object {
118+
$path = $_.FullName
119+
try {
120+
$content = [System.IO.File]::ReadAllText($path)
121+
} catch {
122+
return
123+
}
124+
if ($content.Contains($search)) {
125+
$updated = $content.Replace($search, $replace)
126+
[System.IO.File]::WriteAllText($path, $updated)
127+
}
128+
}
129+
}
130+
131+
Write-Step "Configuring Bundler and Jekyll local paths"
132+
$env:BUNDLE_PATH = $bundlePath
133+
$env:BUNDLE_APP_CONFIG = $bundleConfig
134+
$env:BUNDLE_USER_HOME = $bundleUserHome
135+
$env:BUNDLE_USER_CACHE = Join-Path $bundleUserHome "cache"
136+
$env:JEKYLL_CACHE_DIR = $jekyllCache
137+
$env:JEKYLL_ENV = "development"
138+
New-Item -ItemType Directory -Force -Path $bundlePath, $bundleConfig, $bundleUserHome, $jekyllCache, $siteDir | Out-Null
139+
140+
Push-Location $docHome
141+
try {
142+
Write-Step "Installing Ruby dependencies"
143+
Invoke-Native -FilePath "bundle" -Arguments @("install")
144+
145+
if ($NoServe) {
146+
Write-Step "Build workspace prepared. Start server with:"
147+
Write-Host "bundle exec jekyll serve -P $Port --trace --host=$BindHost --livereload --destination `"$siteDir`""
148+
} else {
149+
Write-Step "Starting Jekyll server on port $Port"
150+
Invoke-Native -FilePath "bundle" -Arguments @("exec", "jekyll", "serve", "-P", "$Port", "--trace", "--host=$BindHost", "--livereload", "--destination", "$siteDir")
151+
}
152+
} finally {
153+
Pop-Location
154+
}
155+
156+

0 commit comments

Comments
 (0)