Skip to content

Commit 794b96e

Browse files
committed
docs: update iframe documentation
1 parent 05a29db commit 794b96e

File tree

1 file changed

+218
-69
lines changed

1 file changed

+218
-69
lines changed

documentation/IFRAME.md

Lines changed: 218 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
SimplePDF Embed [React](../react/README.md) and [Web](../web/README.md) allow you to easily integrate `SimplePDF` using a single line of code by displaying the editor in a modal.
44

5-
**If you're however interested in having more control over the way SimplePDF is displayed in your app**, such as changing the way the modal looks or dropping it altogether injecting the editor into a `div` for example, **read on:**
5+
**If you're however interested in having more control over the way SimplePDF is displayed in your app**, such as changing the way the modal looks or dropping it altogether - injecting the editor into a `div` for example, **read on:**
66

77
## [Show me an example!](https://replit.com/@bendersej/Simple-PDF-Embed-Iframe)
88

@@ -56,13 +56,9 @@ _Do not store sensitive information in the context (!!) as it is available local
5656

5757
Where `CONTEXT` is a URL safe Base64 encoded stringified JSON.
5858

59-
### Implementation example
60-
6159
```javascript
6260
const context = { customerId: "123", environment: "production" };
63-
6461
const encodedContext = encodeURIComponent(btoa(JSON.stringify(context)));
65-
6662
const url = `https://COMPANY_IDENTIFIER.simplepdf.com/editor?open=PUBLICLY_AVAILABLE_PDF_URL&context=${encodedContext}`;
6763
```
6864

@@ -88,95 +84,248 @@ _- Replace `PUBLICLY_AVAILABLE_PDF_URL` with the url of the PDF to use._
8884
</iframe>
8985
```
9086

87+
---
88+
9189
## Iframe Communication
9290

93-
_Only available with a SimplePDF account_
91+
_Programmatic control is only available with a SimplePDF account_
9492

95-
[Head over here to see the incoming and outgoing events communication](../examples/with-iframe/index.html)
93+
The iframe communicates using the `postMessage` API. All messages are JSON strings that must be parsed with `JSON.parse()`.
9694

97-
When your users interact with the editor, the Iframe sends events that can allow you to reconcile data on your side or remove the `Iframe` from your app once a submission has been successfully sent.
95+
### Implementation
9896

99-
### Events `sent by` the Iframe:
97+
```javascript
98+
const iframe = document.getElementById("simplepdf-iframe");
99+
100+
// Helper to send events and receive responses
101+
const sendEvent = (type, data = {}) => {
102+
return new Promise((resolve, reject) => {
103+
const requestId = `req_${Date.now()}_${Math.random().toString(36).slice(2)}`;
104+
105+
const handleResponse = (event) => {
106+
try {
107+
const payload = JSON.parse(event.data);
108+
if (payload.type === "REQUEST_RESULT" && payload.data.request_id === requestId) {
109+
window.removeEventListener("message", handleResponse);
110+
if (payload.data.result.success) {
111+
resolve(payload.data.result.data);
112+
} else {
113+
reject(payload.data.result.error);
114+
}
115+
}
116+
} catch {
117+
// Ignore non-JSON messages
118+
}
119+
};
120+
121+
window.addEventListener("message", handleResponse);
122+
123+
iframe.contentWindow.postMessage(
124+
JSON.stringify({ type, request_id: requestId, data }),
125+
"*"
126+
);
127+
128+
// Timeout after 30 seconds
129+
setTimeout(() => {
130+
window.removeEventListener("message", handleResponse);
131+
reject({ code: "timeout", message: "Request timed out" });
132+
}, 30000);
133+
});
134+
};
100135

101-
_Events are stringified (`JSON.stringify`) before they are sent out_
136+
// Listen for events sent by the iframe
137+
window.addEventListener("message", (event) => {
138+
try {
139+
const payload = JSON.parse(event.data);
140+
switch (payload.type) {
141+
case "EDITOR_READY":
142+
console.log("Editor is ready");
143+
break;
144+
case "DOCUMENT_LOADED":
145+
console.log("Document loaded:", payload.data.document_id);
146+
break;
147+
case "PAGE_FOCUSED":
148+
console.log("Page changed:", payload.data.current_page, "/", payload.data.total_pages);
149+
break;
150+
case "SUBMISSION_SENT":
151+
console.log("Submission sent:", payload.data.submission_id);
152+
break;
153+
}
154+
} catch {
155+
// Ignore non-JSON messages
156+
}
157+
});
158+
```
102159

103-
- `DOCUMENT_LOADED`
160+
### Usage Examples
104161

105-
```
106-
type: 'DOCUMENT_LOADED'
107-
data: { document_id: string }
108-
```
162+
```javascript
163+
// Load a document
164+
await sendEvent("LOAD_DOCUMENT", {
165+
data_url: "https://example.com/document.pdf",
166+
name: "my-document.pdf",
167+
page: 1
168+
});
169+
170+
// Navigate to a specific page
171+
await sendEvent("GO_TO", { page: 3 });
172+
173+
// Select a tool
174+
await sendEvent("SELECT_TOOL", { tool: "TEXT" }); // or "CHECKBOX", "SIGNATURE", "PICTURE", "BOXED_TEXT", null
175+
176+
// Create a field
177+
await sendEvent("CREATE_FIELD", {
178+
type: "TEXT",
179+
page: 1,
180+
x: 100,
181+
y: 700,
182+
width: 200,
183+
height: 30,
184+
value: "Hello World"
185+
});
109186

110-
Where `document_id` is the unique ID of the document that was successfully loaded.
187+
// Clear all fields (or specific ones)
188+
await sendEvent("CLEAR_FIELDS", {}); // Clear all
189+
await sendEvent("CLEAR_FIELDS", { page: 1 }); // Clear page 1 only
190+
await sendEvent("CLEAR_FIELDS", { field_ids: ["f_kj8n2hd9x3m1p", "f_q7v5c4b6a0wyz"] }); // Clear specific fields
111191

112-
- `SUBMISSION_SENT`
192+
// Extract document content
193+
const content = await sendEvent("GET_DOCUMENT_CONTENT", { extraction_mode: "auto" });
194+
console.log("Document name:", content.name);
195+
console.log("Pages:", content.pages); // [{ page: 1, content: "..." }, ...]
113196

114-
```
115-
type: 'SUBMISSION_SENT'
116-
data: { document_id: string; submission_id: string }
197+
// Submit the document
198+
await sendEvent("SUBMIT", { download_copy: true });
117199
```
118200

119-
Where the `submission_id` is the unique ID of the submission successfully sent.
201+
---
120202

121-
#### Implementation example
203+
## Events Reference
122204

123-
```javascript
124-
const eventHandler = async (event) => {
125-
const payload = (() => {
126-
try {
127-
return JSON.parse(event.data);
128-
} catch (e) {
129-
console.error("Failed to parse Iframe event payload");
130-
return null;
131-
}
132-
})();
205+
### Outgoing Events (sent by the iframe)
133206

134-
switch (payload?.type) {
135-
case "DOCUMENT_LOADED":
136-
case "SUBMISSION_SENT":
137-
console.log("Event received:", payload);
138-
return;
207+
| Event | Data | Description |
208+
|-------|------|-------------|
209+
| `EDITOR_READY` | `{}` | Editor has loaded and is ready to receive commands |
210+
| `DOCUMENT_LOADED` | `{ document_id: string }` | A document has been loaded into the editor |
211+
| `PAGE_FOCUSED` | `{ previous_page: number \| null, current_page: number, total_pages: number }` | User navigated to a different page |
212+
| `SUBMISSION_SENT` | `{ document_id: string, submission_id: string }` | Document was successfully submitted |
213+
| `REQUEST_RESULT` | `{ request_id: string, result: { success: boolean, data?: any, error?: { code: string, message: string } } }` | Response to an incoming event |
139214

140-
default:
141-
return;
142-
}
143-
};
215+
### Incoming Events (sent to the iframe)
144216

145-
window.addEventListener("message", eventHandler, false);
146-
```
217+
All incoming events require a `request_id` field and return a `REQUEST_RESULT` response.
218+
219+
#### LOAD_DOCUMENT
147220

148-
### Events `sent to` the Iframe:
221+
Load a PDF document into the editor.
149222

150-
_Events must be stringified (`JSON.stringify`) before they are sent out_
223+
| Field | Type | Required | Description |
224+
|-------|------|----------|-------------|
225+
| `data_url` | `string` | Yes | URL or data URL (base64) of the PDF |
226+
| `name` | `string` | No | Display name for the document |
227+
| `page` | `number` | No | Initial page to display (1-indexed) |
151228

152-
- `LOAD_DOCUMENT`
229+
#### GO_TO
153230

231+
Navigate to a specific page.
232+
233+
| Field | Type | Required | Description |
234+
|-------|------|----------|-------------|
235+
| `page` | `number` | Yes | Page number to navigate to (1-indexed) |
236+
237+
#### SELECT_TOOL
238+
239+
Select a drawing tool or return to cursor mode.
240+
241+
| Field | Type | Required | Description |
242+
|-------|------|----------|-------------|
243+
| `tool` | `string \| null` | Yes | `"TEXT"`, `"BOXED_TEXT"`, `"CHECKBOX"`, `"SIGNATURE"`, `"PICTURE"`, or `null` for cursor |
244+
245+
#### CREATE_FIELD
246+
247+
Create a new field on the document.
248+
249+
| Field | Type | Required | Description |
250+
|-------|------|----------|-------------|
251+
| `type` | `string` | Yes | `"TEXT"`, `"BOXED_TEXT"`, `"CHECKBOX"`, `"SIGNATURE"`, or `"PICTURE"` |
252+
| `page` | `number` | Yes | Page number (1-indexed) |
253+
| `x` | `number` | Yes | X coordinate (PDF points from left) |
254+
| `y` | `number` | Yes | Y coordinate (PDF points from bottom) |
255+
| `width` | `number` | Yes | Field width in PDF points |
256+
| `height` | `number` | Yes | Field height in PDF points |
257+
| `value` | `string` | No | Initial value (see value formats below) |
258+
259+
**Value formats by field type:**
260+
- `TEXT` / `BOXED_TEXT`: Plain text content
261+
- `CHECKBOX`: `"checked"` or `"unchecked"`
262+
- `PICTURE`: Data URL (base64)
263+
- `SIGNATURE`: Data URL (base64 image) or plain text (generates a typed signature)
264+
265+
**Response data:**
266+
```json
267+
{
268+
"field_id": "f_kj8n2hd9x3m1p"
269+
}
154270
```
155-
type: "LOAD_DOCUMENT",
156-
data: { data_url: string }
271+
272+
#### CLEAR_FIELDS
273+
274+
Remove fields from the document.
275+
276+
| Field | Type | Required | Description |
277+
|-------|------|----------|-------------|
278+
| `field_ids` | `string[]` | No | Specific field IDs to remove (omit to clear all) |
279+
| `page` | `number` | No | Only clear fields on this page |
280+
281+
**Response data:**
282+
```json
283+
{
284+
"cleared_count": 5
285+
}
157286
```
158287

159-
#### Implementation example
288+
#### GET_DOCUMENT_CONTENT
160289

161-
```javascript
162-
const iframe = document.getElementById("iframe");
163-
164-
const response = await fetch(
165-
"https://cdn.simplepdf.com/simple-pdf/assets/example_en.pdf"
166-
);
167-
const blob = await response.blob();
168-
const reader = new FileReader();
169-
await new Promise((resolve, reject) => {
170-
reader.onload = resolve;
171-
reader.onerror = reject;
172-
reader.readAsDataURL(blob);
173-
});
290+
Extract text content from the loaded document.
291+
292+
| Field | Type | Required | Description |
293+
|-------|------|----------|-------------|
294+
| `extraction_mode` | `string` | No | `"auto"` (default) or `"ocr"` to force OCR processing |
174295

175-
iframe.contentWindow.postMessage(
176-
JSON.stringify({
177-
type: "LOAD_DOCUMENT",
178-
data: { data_url: reader.result },
179-
}),
180-
"*"
181-
);
296+
**Response data:**
297+
```json
298+
{
299+
"name": "document.pdf",
300+
"pages": [
301+
{ "page": 1, "content": "Text content from page 1..." },
302+
{ "page": 2, "content": "Text content from page 2..." }
303+
]
304+
}
182305
```
306+
307+
#### SUBMIT
308+
309+
Submit the document for processing.
310+
311+
| Field | Type | Required | Description |
312+
|-------|------|----------|-------------|
313+
| `download_copy` | `boolean` | Yes | Whether to trigger a download of the filled PDF |
314+
315+
---
316+
317+
## Error Codes
318+
319+
| Code | Description |
320+
|------|-------------|
321+
| `bad_request:signup_required` | Feature requires a SimplePDF account |
322+
| `bad_request:editor_not_ready` | Editor is not ready to handle the event |
323+
| `bad_request:invalid_page` | Page must be an integer |
324+
| `bad_request:page_out_of_range` | Requested page does not exist |
325+
| `bad_request:page_not_found` | Could not get dimensions for the requested page |
326+
| `bad_request:invalid_field_type` | Unknown field type |
327+
| `bad_request:invalid_tool` | Unknown tool type |
328+
| `bad_request:event_not_allowed` | Event is not allowed for your configuration |
329+
| `forbidden:editing_not_allowed` | Editing is disabled for this portal configuration |
330+
| `forbidden:origin_not_whitelisted` | Origin is not in your allowed origins list |
331+
| `forbidden:whitelist_required` | Event requires origin whitelisting |

0 commit comments

Comments
 (0)