22
33SimplePDF 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
5757Where ` CONTEXT ` is a URL safe Base64 encoded stringified JSON.
5858
59- ### Implementation example
60-
6159``` javascript
6260const context = { customerId: " 123" , environment: " production" };
63-
6461const encodedContext = encodeURIComponent (btoa (JSON .stringify (context)));
65-
6662const 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