pdfservice
X-Api-Key; restano aperti
/health, la pagina di test e i link condivisi. Il servizio è raggiungibile da Internet
tramite HTTPS. Gli URL pubblici delle guide non espongono le porte applicative interne.1) In ingresso il contenuto (immagine, PDF, XML) va sempre referenziato da una variabile —
--data-binary @$var — mai incollato dentro la stringa delle opzioni
cURL. FileMaker spezza le opzioni sugli spazi, e un < subito dopo un =
viene letto come "leggi da file". Un XML o un binario incollati inline non arriveranno mai interi.
2) In uscita, se il Target dell'Insert from URL è una variabile (non un campo container), va aggiunta l'opzione
--FM-return-container-variable: senza,
FileMaker tratta la risposta binaria come testo e dà errore 507. Con un campo container
come Target non serve. Vale per tutti gli endpoint che restituiscono un file (immagini, PDF, PNG).pdfservice
Manipolazione e compressione PDF (porta 3003) · https://pdf.cmisolutions.it
Estrae, unisce, inserisce, elimina e ruota pagine; comprime; crea PDF da
immagini; legge e compila moduli fillable; estrae testo; protegge con password. Usa pypdf
(+ Pillow, cryptography): pure-Python, niente Ghostscript, che su appbox sarebbe effimero.
Le operazioni sulle pagine non rasterizzano nulla: la qualità non viene toccata.
PDF protetti: qualunque operazione accetta un PDF con password se mandi l'header
X-Pdf-Password.
Fuori tema ma qui: /xls/fix ripara i .xls che FileMaker importa
saltando la prima colonna — un endpoint solo, non valeva un servizio a parte.
POST/html/pdfX-Api-Key
HTML → PDF impaginato (per stampare)
Trasforma l'HTML in un PDF pronto da stampare. Nasce da un
problema concreto: da un Web Viewer di FileMaker stampare e' pressoche' impossibile, mentre
un PDF dentro un contenitore si stampa, si archivia e si allega con gli strumenti di FileMaker.
Il motore e' WeasyPrint, non un browser: il testo resta testo vero e selezionabile
(non un'immagine), tabelle e stili in linea sono resi bene, e ci sono formato pagina, margini,
intestazione, piede e numeri di pagina. Va benissimo per l'HTML di TinyMCE e delle email.
⚠️ Di default non scarica niente dalla rete: le immagini vanno incorporate come
data: URI (per esempio quelle che restituisce replykit con
immagini_cid: incorpora). Con immagini_remote=1 si permettono http e
https, ma solo verso indirizzi pubblici.
| parametro | valori | default | note |
|---|---|---|---|
html | testo | obbligatorio | l'HTML da impaginare. In JSON nel campo html, oppure il corpo grezzo con Content-Type: text/html, oppure multipart nel campo file |
formato | a3 | a4 | a5 | letter | legal | default a4 | formato della pagina |
orientamento | verticale | orizzontale | default verticale | verso della pagina |
margini | mm | default 15 | uno, due o quattro valori: 20, 20,15 (verticale, orizzontale), 20,15,25,15 (alto, destra, basso, sinistra) |
intestazione | testo | facoltativo | riga ripetuta in cima a ogni pagina; {n} e {tot} come in /pdf/numera |
piede | testo | facoltativo | riga ripetuta in fondo, es. Pag. {n} di {tot} |
intestazione_pos | sinistra | centro | destra | default centro | dove va l'intestazione |
piede_pos | sinistra | centro | destra | default destra | dove va il piede |
titolo | testo | facoltativo | titolo nelle proprieta' del PDF, se l'HTML non ha gia' un <title> |
css | testo | facoltativo | CSS aggiuntivo, applicato dopo quello predefinito: interruzioni di pagina, caratteri, spaziature |
immagini_remote | 0 | 1 | default 0 | permette di scaricare immagini e fogli di stile via http/https, solo da indirizzi pubblici |
nome | testo | default documento.pdf | nome del file nella risposta |
cURL
curl -X POST \
-H "X-Api-Key: LA_TUA_CHIAVE" \
-H "Content-Type: application/json" \
-d '{"html":"<h1>Preventivo 1180</h1><p>Buongiorno, ecco il dettaglio.</p>",
"formato":"a4","margini":"20,15",
"intestazione":"CMI Solutions",
"piede":"Pag. {n} di {tot}",
"titolo":"Preventivo 1180"}' \
--output preventivo.pdf \
"https://pdf.cmisolutions.it/html/pdf"
# oppure l'HTML grezzo, con le opzioni nella query:
curl -X POST -H "X-Api-Key: LA_TUA_CHIAVE" -H "Content-Type: text/html" \
--data-binary @pagina.html --output stampa.pdf \
"https://pdf.cmisolutions.it/html/pdf?formato=a4&orientamento=orizzontale"
FileMaker
# L'HTML ce l'hai gia' nel campo: non serve passare dal Web Viewer.
Set Variable [ $json ; value:
JSONSetElement ( "{}" ;
[ "html" ; DOC::Corpo_HTML ; JSONString ] ;
[ "formato" ; "a4" ; JSONString ] ;
[ "margini" ; "20,15" ; JSONString ] ;
[ "intestazione" ; DOC::Titolo ; JSONString ] ;
[ "piede" ; "Pag. {n} di {tot}" ; JSONString ] ;
[ "nome" ; "preventivo.pdf" ; JSONString ]
) ]
Insert from URL [
Select ; With dialog: Off ;
Target: $pdf ; // BINARIO: qui serve --FM-return-container-variable
"https://pdf.cmisolutions.it/html/pdf" ;
cURL options:
"--data @$json "
& "-H \"Content-Type: application/json\" "
& "-H \"X-Api-Key: " & $$API_KEY & "\" "
& "--FM-return-container-variable "
& "--dump-header $intestazioni"
]
If [ PatternCount ( $intestazioni ; "X-Esito: ok" ) ]
Set Field [ DOC::Stampa ; $pdf ] // contenitore: si stampa e si allega da FileMaker
Else
Show Custom Dialog [ "PDF non generato: " & $pdf ]
End If
Risposta
Il PDF binario (application/pdf), con
X-Pages (numero di pagine), X-Bytes, X-Html-Bytes,
X-Seconds e Content-Disposition col nome del file. Su errore un JSON
{"error": "…"} con HTTP 400/413/500 e X-Esito: errore.
Da sapere
- Il testo resta testo. Non e' una fotografia della pagina: nel PDF si seleziona, si cerca e si copia, e il file pesa pochi KB. Una pagina tipo viene resa in circa due decimi di secondo.
- Le immagini vanno dentro l'HTML. Il modo consigliato e' il
data:URI: nessuna rete, nessuna sorpresa, e funziona anche per le immagini che erano allegati della mail (replykit le restituisce gia' cosi' conimmagini_cid: incorpora). Conimmagini_remote=1si scaricano quelle pubbliche, ma i redirect sono spenti e gli indirizzi non pubblici (localhost, 10.x, 172.16-31.x, 192.168.x) restano bloccati: dall'appbox porterebbero ai servizi interni. - Niente
file://, mai. Un HTML che arriva da fuori non deve poter far leggere al server i propri file. Se una risorsa viene bloccata il PDF si genera lo stesso, semplicemente senza quella risorsa. - Caratteri: sul server ci sono DejaVu e la serie URW, e
Arial,HelveticaeTimes New Romanvengono sostituiti automaticamente con equivalenti metrici. Un carattere non installato (per esempio Calibri) ripiega su DejaVu Sans: leggibile, ma con spaziature diverse. Per un risultato prevedibile conviene indicare nel CSS una famiglia presente. - Interruzioni di pagina: si governano dal CSS, con
page-break-before: always(obreak-before: page) sugli elementi, passato nel parametrocsso gia' dentro l'HTML. - Limiti: HTML fino a 5 MB, PDF fino a 1000 pagine (oltre, errore 400 invece di un file ingestibile) e una sola conversione per volta per chiave, come gli altri endpoint pesanti.
POST/oauth/pkceX-Api-Key
OAuth Vetinfo — aiuti al setup
Aiuti per il flusso OAuth 2 (Authorization Code + PKCE) di CAS
Vetinfo (REV/FAR) da FileMaker. Solo il setup: il refresh a regime lo fa FileMaker in
diretta verso auth.izs.it (HTTPS), così refresh_token e
client_secret non passano mai da qui.
Input accettati. nessun corpo per /oauth/pkce.
cURL
# 1) genera i parametri PKCE curl -X POST -H "X-Api-Key: LA_TUA_CHIAVE" "https://pdf.cmisolutions.it/oauth/pkce" # 2) (il browser, dopo il login Vetinfo, viene rimandato a /oauth/callback che cattura il code) # 3) recupera il code catturato, dato lo state curl -X POST -H "X-Api-Key: LA_TUA_CHIAVE" "https://pdf.cmisolutions.it/oauth/result?state=LO_STATE"
Risposta
/oauth/pkce → code_verifier (da tenere in
FileMaker), code_challenge + code_challenge_method (nell'URL authorize),
state. /oauth/result → {pronto:true, code:"…"} quando la
callback ha ricevuto il code, altrimenti {pronto:false}.
Da sapere
GET /oauth/callbackè PUBBLICO (senza chiave): è lì che Vetinfo rimanda il browser col?code=&state=, e il browser non ha la X-Api-Key. Salva il code per lo state e mostra una pagina di cortesia.- Il code è protetto da PKCE: anche se transita qui in chiaro (HTTP), è inutile senza il
code_verifier(solo in FileMaker) e ilclient_secret. Per questo il setup può passare dall'appbox in sicurezza. /oauth/resultè monouso: il code viene cancellato appena letto e comunque scade dopo 10 minuti.- ⚠️ La
redirect_urihttps://pdf.cmisolutions.it/oauth/callbackva registrata presso Vetinfo (mail a farmacows@izs.it). Alcuni provider accettano solohttpsper le redirect pubbliche: verificalo prima. - Lo scambio
code→tokene il refresh NON passano da qui: li fa FileMaker in HTTPS diretto a Vetinfo. Vedi la procedura completa invetinfo_oauth_filemaker.md.
POST/pdf/campiX-Api-Key
Aggiungi campi compilabili (precompilati)
Aggiunge campi fillable a un PDF che non ne ha, già precompilati.
Serve a partire da un PDF "ignorante" e ottenerne uno che il cliente può confermare o integrare:
i valori sono dentro ma modificabili. I campi creati sono veri campi AcroForm, quindi si
rileggono e si riempiono con gli endpoint form come qualsiasi altro modulo. Tipi: testo,
multiriga, checkbox, scelta (menu a tendina).
Le coordinate sono precise: l'unità nativa del PDF è il punto (1 pt = 1/72 di pollice).
Puoi però indicare le misure in millimetri con unita:"mm". Dimensioni piena pagina
dei formati più comuni (larghezza × altezza):
| Formato | Punti (pt) | Millimetri (mm) |
|---|---|---|
| A4 | 595 × 842 | 210 × 297 |
| A5 | 420 × 595 | 148 × 210 |
| A3 | 842 × 1191 | 297 × 420 |
| Letter (US) | 612 × 792 | 216 × 279 |
| Legal (US) | 612 × 1008 | 216 × 356 |
| parametro | valori | default | note |
|---|---|---|---|
pdf | base64 | — | il PDF di partenza, codificato base64 (nel corpo JSON) |
origine | alto / basso | alto | sistema di coordinate: alto = la y è contata dall'alto della pagina (come sullo schermo); basso = origine PDF nativa in basso a sinistra |
unita | pt / mm | pt | unità di x, y, larghezza, altezza, lato: pt = punti PDF (default); mm = millimetri. Il fontSize resta sempre in punti. |
campi | lista di oggetti | — | i campi da creare. Ogni campo: tipo (testo/multiriga/checkbox/scelta), nome (univoco), pagina (da 1), x/y, larghezza/altezza (per il checkbox: lato), valore (per il checkbox true/false), opzioni (lista, solo per scelta), soloLettura (precompilato non modificabile), fontSize (0 = automatico, in punti), bordo (true/false). Misure nell'unità scelta con unita (default punti PDF). |
cURL
curl -X POST -H "X-Api-Key: LA_TUA_CHIAVE" -H "Content-Type: application/json" \
-d '{"pdf":"<BASE64>","origine":"alto","campi":[
{"tipo":"testo","nome":"nome_cliente","pagina":1,"x":120,"y":95,"larghezza":200,"altezza":20,"valore":"Mario Rossi"},
{"tipo":"checkbox","nome":"conferma","pagina":1,"x":160,"y":135,"lato":16,"valore":true}
]}' \
"https://pdf.cmisolutions.it/pdf/campi" -o compilabile.pdf
FileMaker
Set Variable [ $body ; value: JSONSetElement ( "{}" ;
[ "pdf" ; Base64Encode ( Documenti::PDF ) ; JSONString ] ;
[ "origine" ; "alto" ; JSONString ] ;
[ "campi" ; $campiJSON ; JSONArray ] ) ]
Insert from URL [ Select ; With dialog: Off ; Target: Documenti::PDF_Compilabile ;
"https://pdf.cmisolutions.it/pdf/campi" ;
cURL options: "-X POST -d @$body -H \"Content-Type: application/json\" -H \"X-Api-Key: " & $$API_KEY & "\"" ]
Risposta
Il PDF con i campi compilabili precompilati. Header X-Pages, X-Bytes.
Da sapere
- I valori sono modificabili dal destinatario; usa
soloLettura:trueper bloccarne uno. - Per rileggere i valori confermati dal cliente usa gli endpoint di lettura dei campi form (i campi creati sono AcroForm standard).
- Vuoi ragionare in mm? Aggiungi
unita:"mm"e dai x/y/larghezza/altezza in millimetri (1 mm = 72/25,4 ≈ 2,83 pt). Conorigine:"alto"parti dall'angolo in alto a sinistra della pagina.
POST/pdf/compressX-Api-Key
Comprimi un PDF
Due leve indipendenti. images=1 ricomprime le immagini
dentro il PDF con Pillow: è lossy ed è il guadagno vero sui documenti scansionati.
lossless=1 comprime gli stream e deduplica gli oggetti: è sempre sicuro ma rende poco.
Su un PDF di solo testo l'unica leva utile è la seconda, e il guadagno resta piccolo.
| parametro | valori | default | note |
|---|---|---|---|
quality | 1-100 | 70 | qualità JPEG delle immagini ricompresse |
maxw | px | — | ridimensiona le immagini più larghe: è la leva più potente |
images | 0 | 1 | 1 | ricomprimi le immagini interne (lossy) |
lossless | 0 | 1 | 1 | comprimi stream e deduplica oggetti (sicuro) |
cURL
curl -X POST -H "X-Api-Key: LA_TUA_CHIAVE" \ --data-binary @scansione.pdf \ "https://pdf.cmisolutions.it/pdf/compress?quality=60&maxw=1600" -o compresso.pdf
Risposta
Il PDF. Header: X-Original-Bytes,
X-Compressed-Bytes, X-Ratio, X-Images-Found,
X-Images-Recompressed, X-Images-Skipped,
X-Fallback-Original.
Da sapere
- Non restituisce mai un PDF più grande dell'originale: se non si guadagna torna l'originale intatto con
X-Fallback-Original: 1. - Misurato su una scansione da 1,18 MB:
quality=60→ −73%; conmaxw=1000→ −94%; solo lossless → 0% (fallback). - Salta le immagini sotto gli 8 KB, quelle con trasparenza e i formati esotici (CCITT, JBIG2): li conta in
X-Images-Skippedinvece di fallire. - Non fa subsetting dei font né ottimizzazioni strutturali: per quelle servirebbe Ghostscript nell'immagine Docker.
POST/pdf/deleteX-Api-Key
Elimina pagine
Restituisce il PDF senza le pagine indicate. Non si possono eliminare tutte.
| parametro | valori | default | note |
|---|---|---|---|
pages | es. 2,4-6 | obbligatorio | stessa sintassi di extract |
cURL
curl -X POST -H "X-Api-Key: LA_TUA_CHIAVE" \ --data-binary @documento.pdf "https://pdf.cmisolutions.it/pdf/delete?pages=2,4" -o ridotto.pdf
Risposta
Il PDF. Header X-Pages.
POST/pdf/encryptX-Api-Key
Proteggi con password
Cifra il PDF con una password (AES-256). La password si passa
nell'header X-Pdf-Password — non in query, così non finisce in log o
cronologie. Owner password opzionale in X-Pdf-Owner-Password.
Input accettati. PDF nel corpo grezzo. Password nell'header X-Pdf-Password.
cURL
curl -X POST -H "X-Api-Key: LA_TUA_CHIAVE" \ -H "X-Pdf-Password: la-mia-password" \ --data-binary @documento.pdf "https://pdf.cmisolutions.it/pdf/encrypt" -o protetto.pdf
FileMaker
Set Variable [ $pdf ; value: Documenti::PDF ]
Insert from URL [
Select ; With dialog: Off ; Target: Documenti::PDF_Protetto ;
"https://pdf.cmisolutions.it/pdf/encrypt" ;
cURL options: "--data-binary @$pdf "
& "-H \"X-Api-Key: " & $$API_KEY & "\" "
& "-H \"X-Pdf-Password: " & Documenti::Password & "\""
]
Risposta
Il PDF cifrato.
Da sapere
- Su appbox il traffico è in chiaro (no TLS): la password nell'header è protetta dai log, ma non dalla rete. Per dati molto sensibili, cifra a valle su rete fidata.
- Il complemento è
POST /pdf/decrypt: rimuove la password (quella attuale inX-Pdf-Password) e restituisce il PDF in chiaro.
POST/pdf/extractX-Api-Key
Estrai pagine in un nuovo PDF
Costruisce un PDF con solo le pagine indicate, nell'ordine in
cui le scrivi. Quindi serve anche a riordinare (pages=3,1,2) o a duplicare una
pagina (pages=1,1,2).
| parametro | valori | default | note |
|---|---|---|---|
pages | es. 1,3,5-8 | obbligatorio | 1-based; i range discendenti invertono (5-3 → 5,4,3); duplicati ammessi |
cURL
curl -X POST -H "X-Api-Key: LA_TUA_CHIAVE" \ --data-binary @documento.pdf \ "https://pdf.cmisolutions.it/pdf/extract?pages=1,3,5-8" -o estratto.pdf
FileMaker
Set Variable [ $pdf ; value: Documenti::PDF ]
Insert from URL [
Select ; With dialog: Off ; Target: Documenti::PDF_Estratto ;
"https://pdf.cmisolutions.it/pdf/extract?pages=1,3,5-8" ;
cURL options: "--data-binary @$pdf -H \"X-Api-Key: " & $$API_KEY & "\""
]
Risposta
Il PDF. Header X-Pages, X-Bytes.
Da sapere
- Se chiedi una pagina fuori range ricevi 400 con il numero di pagine reale: usa
/pdf/infoper saperlo prima.
POST/pdf/fieldsX-Api-Key
Leggi i campi di un modulo fillable
Elenca i campi di un AcroForm: nome, tipo, valore attuale e — per
checkbox, radio e menu — i valori ammessi. Usalo sempre prima di compilare: senza i
nomi esatti e i valori ammessi, /pdf/fill scrive nel vuoto.
cURL
curl -X POST -H "X-Api-Key: LA_TUA_CHIAVE" \ --data-binary @modulo.pdf "https://pdf.cmisolutions.it/pdf/fields"
Risposta
JSON: campi[] con nome, tipo
(testo/checkbox/radio/menu/lista/firma/pulsante), valore, opzioni,
solo_lettura, obbligatorio, max_len; più
totale, pagine e xfa.
Da sapere
- Se
xfaè true il modulo è un form dinamico Adobe: non è compilabile per questa via (vedi /pdf/fill).
POST/pdf/fillX-Api-Key
Compila un modulo fillable
Scrive i valori nei campi e restituisce il PDF compilato. I nomi devono
corrispondere esattamente a quelli di /pdf/fields; per checkbox e menu vanno usati
i valori ammessi (es. /Yes, non true).
Input accettati. JSON: {"pdf_base64": "…", "values": {"Cognome": "Puppo"}}
• multipart: file + values (JSON come stringa)
| parametro | valori | default | note |
|---|---|---|---|
flatten | 0 | 1 | 0 | prova a rendere i valori definitivi (vedi note) |
cURL
curl -X POST -H "X-Api-Key: LA_TUA_CHIAVE" \
-F file=@modulo.pdf \
-F 'values={"Cognome":"Puppo","Accetto":"/Yes"}' \
"https://pdf.cmisolutions.it/pdf/fill" -o compilato.pdf
FileMaker
Set Variable [ $j ; value:
JSONSetElement ( "{}" ;
[ "pdf_base64" ; Base64EncodeRFC ( 4648 ; Documenti::Modulo ) ; JSONString ] ;
[ "values.Cognome" ; Anagrafica::Cognome ; JSONString ] ;
[ "values.Accetto" ; "/Yes" ; JSONString ] ) ]
Insert from URL [
Select ; With dialog: Off ; Target: Documenti::Compilato ;
"https://pdf.cmisolutions.it/pdf/fill" ;
cURL options: "-H \"Content-Type: application/json\" "
& "-H \"X-Api-Key: " & $$API_KEY & "\" --data @$j"
]
Risposta
Il PDF compilato. Header: X-Fields-Filled,
X-Fields-Unknown, X-Flattened, X-Flatten-Warning.
Da sapere
- Se il risultato sembra vuoto, guarda
X-Fields-Unknown: elenca i nomi che hai mandato ma che nel PDF non esistono. Se nessun nome esiste ricevi 400 con l'elenco di quelli veri. flatten=1non è affidabile: nei collaudi pypdf non ha appiattito nulla. L'endpoint quindi verifica il risultato e dichiara la verità: se i campi restano editabili troviX-Flattened: 0e un avviso. I valori sono comunque scritti.- I form XFA vengono respinti con 400: pypdf scriverebbe il guscio AcroForm ma il viewer mostra il livello XFA e i valori non si vedrebbero. Meglio un errore che un PDF finto-compilato.
- Compilare un PDF firmato digitalmente ne invalida la firma.
POST/pdf/from-imagesX-Api-Key
Più immagini → un PDF multipagina
Di default una immagine per pagina, nell'ordine di invio; con
per_pagina=N le dispone in griglia, N per foglio. Applica l'orientamento EXIF
(le foto da smartphone vengono raddrizzate) e appiattisce la trasparenza su bianco, perché il PDF
non ha canale alfa. Sta fra gli endpoint PDF perché l'uscita è un PDF: puoi concatenare con
/pdf/compress o /pdf/merge senza cambiare servizio né chiave.
Input accettati. multipart: più campi file •
JSON: {"images": ["<base64>", "<base64>"]}
| parametro | valori | default | note |
|---|---|---|---|
per_pagina | 1-50 | 1 | quante immagini per pagina. Con 1 il comportamento è quello storico (una per foglio, che ruota per seguire l'immagine); con >1 le dispone in griglia riempiendo per righe |
colonne | 1-50 | auto | colonne della griglia (le righe si ricavano). Se omesso è automatico, il più quadrato possibile per l'orientamento. Solo con per_pagina>1 |
orientamento | verticale | orizzontale | verticale | orientamento del foglio in modalità griglia. In orizzontale la griglia automatica preferisce più colonne (es. per_pagina=2 → affiancate invece che impilate) |
gutter | mm (0-50) | 6 | spazio fra le celle della griglia |
page | a4 | letter | auto | a4 | auto = pagina su misura dell'immagine (solo con per_pagina=1); con a4/letter l'orientamento del foglio segue l'immagine (in griglia il foglio è in verticale) |
dpi | 36-600 | 150 | risoluzione con cui viene calcolata la pagina |
quality | 1-100 | 85 | qualità JPEG delle immagini incorporate |
maxw | px | — | rimpicciolisce le immagini più larghe prima di impaginarle |
margin | mm (0-50) | 10 | margine bianco esterno; ignorato con page=auto |
cURL
curl -X POST -H "X-Api-Key: LA_TUA_CHIAVE" \ -F file=@pagina1.jpg -F file=@pagina2.jpg -F file=@pagina3.jpg \ "https://pdf.cmisolutions.it/pdf/from-images?page=a4&dpi=150" -o documento.pdf
FileMaker
Set Variable [ $j ; value:
JSONSetElement ( "{}" ;
[ "images[0]" ; Base64EncodeRFC ( 4648 ; Scansioni::Foto1 ) ; JSONString ] ;
[ "images[1]" ; Base64EncodeRFC ( 4648 ; Scansioni::Foto2 ) ; JSONString ] ) ]
Insert from URL [
Select ; With dialog: Off ; Target: Documenti::PDF ;
"https://pdf.cmisolutions.it/pdf/from-images?page=a4" ;
cURL options: "-H \"Content-Type: application/json\" "
& "-H \"X-Api-Key: " & $$API_KEY & "\" --data @$j"
]
Risposta
Il PDF. Header X-Pages, X-Images, X-Per-Page, X-Grid (es. 2x2, vuoto se 1/pagina), X-Bytes.
Da sapere
- Il caso tipico: fotografi più pagine col telefono e ottieni un unico PDF. FileMaker da solo non lo fa.
- Più immagini per pagina:
?per_pagina=4mette 4 immagini per foglio (griglia 2×2),?per_pagina=2due impilate. Per decidere tu la disposizione aggiungicolonne, es.?per_pagina=6&colonne=3→ 3 colonne × 2 righe. L'ultima pagina può restare parzialmente riempita. - In modalità griglia (
per_pagina>1) il foglio è verticale di default, o orizzontale conorientamento=orizzontale;page=autonon è ammesso: sceglia4oletter. - Con
page=a4l'immagine viene adattata al foglio (anche ingrandita, se piccola). Conpage=autola pagina prende la misura esatta dell'immagine (solo una per pagina). - Per un risultato più leggero, concatena con
/pdf/compress?quality=60&maxw=1600, oppure passa giàmaxwqui. - Limiti anti-esaurimento risorse. Default: 50 immagini, 40 milioni di pixel per immagine, 120 milioni complessivi, 25 milioni per pagina raster e 100 MB di output. Sono configurabili con le variabili
PDF_MAX_*; il servizio prepara una pagina alla volta, non l'intero lotto.
POST/pdf/infoX-Api-Key
Informazioni su un PDF
Numero di pagine, dimensioni di ogni pagina in punti, metadata, se è cifrato — e l'analisi delle immagini incorporate, che dice se conviene comprimere prima di provarci.
cURL
curl -X POST -H "X-Api-Key: LA_TUA_CHIAVE" \ --data-binary @documento.pdf "https://pdf.cmisolutions.it/pdf/info"
Risposta
JSON: pages, sizes, metadata,
encrypted, contenuto_leggibile, bytes, più:
immagini: n, oltre_8kb, bytes,
quota_pct, larghezza_max_px, con_trasparenza
comprimere: conviene (si | forse |
no | sconosciuto), perche,
guadagno_massimo_pct.
Da sapere
- Comodo prima di
/pdf/extractper sapere quante pagine ci sono. - Verifica preventiva della compressione.
quota_pctè la quota di byte occupata dalle immagini, ed è il tetto massimo del guadagno: se le immagini sono il 3% del file, comprimerle non può farti risparmiare di più.oltre_8kbè la condizione necessaria:/pdf/compresssalta apposta le immagini sotto gli 8 KB (ricomprimere un'icona la peggiora), quindi se è 0 il guadagno è nullo anche con quota alta. Selarghezza_max_pxsupera ~1500, aggiungeremaxwraddoppia quasi il risparmio. - Riscontro misurato: PDF di solo testo → 0% (e
/pdf/compressrestituisce l'originale); testo con logo (quota 2,6%) → 1,2%; quota 26% ma nessuna immagine sopra 8 KB → 2,0%; quota 55,6% → 28% (42% conmaxw); scansione 300 dpi (quota 99,9%) → 75,9%, e 93,0% conmaxw=1000. - Su un PDF protetto, se mandi l'header
X-Pdf-Passwordl'analisi è completa; senza, risponde comunque 200 concontenuto_leggibile: falseeconviene: "sconosciuto"— sapere che un file è cifrato è già un'informazione utile.
POST/pdf/insertX-Api-Key
Inserisci un PDF dentro un altro
Infila tutte le pagine dell'inserto dentro il PDF base, in un punto qualunque.
Input accettati. multipart: campi base e insert •
JSON: {"base": "<b64>", "insert": "<b64>"}
| parametro | valori | default | note |
|---|---|---|---|
at | numero di pagina | in coda | inserisce PRIMA di quella pagina: at=1 mette in testa, omesso mette in fondo |
cURL
curl -X POST -H "X-Api-Key: LA_TUA_CHIAVE" \ -F base=@documento.pdf -F insert=@allegato.pdf \ "https://pdf.cmisolutions.it/pdf/insert?at=3" -o risultato.pdf
FileMaker
Set Variable [ $j ; value:
JSONSetElement ( "{}" ;
[ "base" ; Base64EncodeRFC ( 4648 ; Documenti::Base ) ; JSONString ] ;
[ "insert" ; Base64EncodeRFC ( 4648 ; Documenti::Inserto ) ; JSONString ] ) ]
Insert from URL [
Select ; With dialog: Off ; Target: Documenti::Risultato ;
"https://pdf.cmisolutions.it/pdf/insert?at=3" ;
cURL options: "-H \"Content-Type: application/json\" "
& "-H \"X-Api-Key: " & $$API_KEY & "\" --data @$j"
]
Risposta
Il PDF risultante. Header X-Pages.
POST/pdf/mergeX-Api-Key
Unisci più PDF in uno
Concatena i PDF nell'ordine di invio. Servono almeno due file. A differenza degli altri endpoint accetta più documenti, quindi non può usare il corpo grezzo: o multipart, o JSON con i PDF in base64.
Input accettati. multipart: più campi file •
JSON: {"pdfs": ["<base64>", "<base64>"]}
cURL
curl -X POST -H "X-Api-Key: LA_TUA_CHIAVE" \ -F file=@primo.pdf -F file=@secondo.pdf \ "https://pdf.cmisolutions.it/pdf/merge" -o unito.pdf
FileMaker
Set Variable [ $j ; value:
JSONSetElement ( "{}" ;
[ "pdfs[0]" ; Base64EncodeRFC ( 4648 ; Documenti::PDF_A ) ; JSONString ] ;
[ "pdfs[1]" ; Base64EncodeRFC ( 4648 ; Documenti::PDF_B ) ; JSONString ] ) ]
Insert from URL [
Select ; With dialog: Off ; Target: Documenti::PDF_Unito ;
"https://pdf.cmisolutions.it/pdf/merge" ;
cURL options: "-H \"Content-Type: application/json\" "
& "-H \"X-Api-Key: " & $$API_KEY & "\" --data @$j"
]
Risposta
Il PDF unito. Header X-Pages.
Da sapere
- Il base64 gonfia del ~33%: su PDF grossi il multipart è più leggero. Entrambi sono supportati, scegli tu.
POST/pdf/numeraX-Api-Key
Numera le pagine
Stampa il numero di pagina su ogni pagina di un PDF. Il segnaposto
{n} è il numero corrente, {tot} il totale; {tot} è calcolato
in modo che l'ultima pagina numerata legga sempre «N di N». Le pagine ruotate a 90/270°
ricevono il numero dritto e nell'angolo giusto.
| parametro | valori | default | note |
|---|---|---|---|
formato | testo con {n} e {tot} | {n} / {tot} | il testo da stampare: {n} diventa il numero di pagina, {tot} il totale; tutto il resto è scritto tale e quale. {tot} è facoltativo: per la numerazione SENZA totale usa {n} da solo. Esempi: {n} → «1», Pag. {n} di {tot} → «Pag. 1 di 8». Puoi stilizzare SOLO una parte con <u>…</u> (sottolineato) e <b>…</b> (grassetto): es. Pag. <u>{n}</u> di {tot} sottolinea solo il numero |
posizione | basso/alto-destra/centro/sinistra | basso-destra | dove mettere il numero |
da | n° pagina | 1 | prima pagina da numerare: le precedenti restano intatte (salta una copertina) |
inizio | numero | 1 | primo numero stampato |
dim | 4-72 | 9 | corpo del carattere in punti |
margine | mm (0-100) | 12 | distanza dal bordo |
colore | esadecimale | #444444 | colore del testo |
font | 14 font standard oppure un font del pannello | Helvetica | i 14 font PDF standard (Helvetica, Times-Roman, Courier…) o il nome di un font caricato in /admin (gli stessi TTF di squish, es. anton). Ignoto → Helvetica; l'header X-Font-Used dice quale è stato applicato |
url | http/https/mailto | — | se presente, il numero diventa un link cliccabile verso questo indirizzo. {n} e {tot} sono sostituiti anche qui, così ogni pagina può puntare a un URL diverso (es. https://…/pagina/{n}). Per farlo sembrare un link aggiungi colore=#0000ee |
cURL
curl -X POST -H "X-Api-Key: LA_TUA_CHIAVE" \
--data-binary @documento.pdf \
"https://pdf.cmisolutions.it/pdf/numera?formato=Pag.%20{n}%20di%20{tot}&posizione=basso-centro" -o numerato.pdf
FileMaker
Set Variable [ $pdf ; value: Documenti::PDF ]
Insert from URL [
Select ; With dialog: Off ; Target: Documenti::PDF_Numerato ;
"https://pdf.cmisolutions.it/pdf/numera?da=2&formato=Pag.%20{n}%20di%20{tot}" ;
cURL options: "--data-binary @$pdf -H \"X-Api-Key: " & $$API_KEY & "\""
]
Risposta
Il PDF numerato. Header X-Pages, X-Pages-Numbered (quante numerate), X-Font-Used (font applicato), X-Linked (1 se il numero è un link), X-Bytes.
Da sapere
- Solo il numero, senza totale:
?formato={n}(o?formato=Pagina%20{n}). Il{tot}si mette solo se lo vuoi. - Stile su una parte del testo — dentro
formatodue tag delimitano il tratto da stilizzare:
·<u>…</u>= sottolineato
·<b>…</b>= grassetto (solo font standard; su un font del pannello resta regolare)
Come concatenarli. In fila su parti diverse:<u>Vai</u> a pag. <b>{n}</b>. Insieme sullo stesso tratto, annidandoli (l'ordine non conta):<b><u>{n}</u></b>→ grassetto e sottolineato. Ogni altro< >è testo normale, non serve alcun escaping. - Numero cliccabile: con
?url=https://…il numero diventa un link. L'area cliccabile copre tutto il testo diformato, anche sulle pagine ruotate. Solo schemi http/https/mailto. Conda=2la copertina resta senza numero e senza link. - Il numero viene fuso sopra il contenuto esistente, non lo sostituisce.
- I font sono gli stessi del pannello /admin (quelli di squish): scrivi in
font=il nome del preset (es.font=anton) oppure uno dei 14 standard (Helvetica,Times-Roman…). Il font scelto viene incorporato nel PDF, che resta autonomo. I preset sono TTF; un eventuale OTF non incorporabile ripiega su Helvetica. L'headerX-Font-Usedconferma quale è stato applicato. - Con
da=2la copertina resta senza numero e la numerazione parte dalla pagina 2 (che diventa «1 di …»). - Un PDF protetto si apre passando l'header
X-Pdf-Password.
POST/pdf/relazioneX-Api-Key
Da JSON a PDF impaginato
Genera un PDF formattato a partire da un JSON di relazione: testata, sezioni numerate, testo giustificato, piè di pagina con «pag. X di Y». Utile per trasformare l'esito di un'analisi (adeguata verifica, perizia, referto discorsivo) in un documento presentabile e archiviabile.
Input accettati. JSON (Content-Type: application/json). Corpo:
riepilogo, relazione_completa, motivazioni_esito,
sintesi_operatore — tutti facoltativi: si stampano solo quelli presenti, in
quest'ordine, numerati. Testata facoltativa: titolo, sottotitolo,
riferimento, piede.
cURL
curl -X POST -H "X-Api-Key: LA_TUA_CHIAVE" \ -H "Content-Type: application/json" --data @relazione.json \ "https://pdf.cmisolutions.it/pdf/relazione?filename=Relazione%20Rossi.pdf" -o relazione.pdf
FileMaker
Set Variable [ $j ; value: JSONSetElement ( "{}" ;
[ "titolo" ; "Relazione di adeguata verifica" ; JSONString ] ;
[ "sottotitolo" ; Pratica::Soggetto ; JSONString ] ;
[ "riferimento" ; "Prot. " & Pratica::Protocollo ; JSONString ] ;
[ "riepilogo" ; Pratica::Riepilogo ; JSONString ] ;
[ "relazione_completa" ; Pratica::Relazione ; JSONString ] ) ]
Insert from URL [
Select ; With dialog: Off ; Target: Pratica::PDF ;
"https://pdf.cmisolutions.it/pdf/relazione" ;
cURL options: "--data @$j -H \"Content-Type: application/json\" "
& "-H \"X-Api-Key: " & $$API_KEY & "\""
]
Risposta
Il PDF impaginato (A4), con X-Filename e X-Bytes.
Da sapere
- Le righe che iniziano con
-,•o*diventano elenco puntato; le altre paragrafi giustificati. Il testo si spezza su più pagine da solo e la numerazione del piede si aggiorna. - I caratteri tipografici (apostrofi curvi, virgolette basse, trattini lunghi) e gli accenti sono resi correttamente. Eventuale HTML nel testo viene neutralizzato, non interpretato.
- Il titolo di una sezione si stampa solo se il campo ha contenuto: niente intestazioni vuote. Se non c'è alcun campo utile la risposta è 400 con l'elenco dei campi attesi.
- Con
?filename=si sceglie il nome del file restituito, come per gli altri endpoint. - Layout unico e sobrio, adatto a un fascicolo. Per una prima pagina a cruscotto (esito a colori, indicatori, checklist) servirebbero campi strutturati in più (esito, soggetto, importo…): oggi il JSON porta solo testo.
POST/pdf/rotateX-Api-Key
Ruota pagine
Ruota le pagine indicate, o tutte se ometti pages.
| parametro | valori | default | note |
|---|---|---|---|
deg | multiplo di 90 | 90 | in senso orario; 270 = 90 antiorario |
pages | es. 1,3 | tutte | quali pagine ruotare |
cURL
curl -X POST -H "X-Api-Key: LA_TUA_CHIAVE" \ --data-binary @documento.pdf "https://pdf.cmisolutions.it/pdf/rotate?deg=90&pages=1" -o ruotato.pdf
Risposta
Il PDF. Header X-Pages.
POST/pdf/splitX-Api-Key
Dividi in tanti PDF di una pagina
Spezza un PDF multipagina in tanti PDF da una pagina. Di default
li nomina come l'originale con _numeropagina (Referto_1.pdf, Referto_2.pdf…):
l'"originale" viene da ?filename= o dal nome del file multipart, altrimenti documento.
Puoi anche passare una lista di nomi con ?nomi=, usati nell'ordine delle pagine.
Input accettati. 1 PDF: corpo grezzo • multipart file •
JSON {"pdf_base64":"…", "nomi":["A","B"]}
| parametro | valori | default | note |
|---|---|---|---|
nomi | Nome1,Nome2,… | — | lista di nomi separati da virgola, applicati alle pagine nell'ordine; se sono meno delle pagine, le restanti tornano al nome di default. In JSON si usa il campo nomi (array). L'estensione .pdf è aggiunta da sé |
format | json | zip | json | json → i file in base64 (comodo da FileMaker: cicli e salvi ognuno); zip → un unico archivio .zip con dentro i PDF già nominati |
pages | es. 1,3-5 | tutte | limita quali pagine estrarre |
filename | nome | — | base del nome di default (<base>_<n>.pdf) quando mandi il PDF come corpo grezzo |
cURL
# tanti PDF in JSON base64, nomi di default (Referto_1.pdf, Referto_2.pdf, …) curl -X POST -H "X-Api-Key: LA_TUA_CHIAVE" --data-binary @referto.pdf \ "https://pdf.cmisolutions.it/pdf/split?filename=Referto" # un unico .zip con nomi tuoi curl -X POST -H "X-Api-Key: LA_TUA_CHIAVE" --data-binary @referto.pdf \ "https://pdf.cmisolutions.it/pdf/split?format=zip&nomi=Fronte,Retro" -o pagine.zip
FileMaker
Set Variable [ $pdf ; value: Documenti::PDF ]
Insert from URL [
Select ; With dialog: Off ; Target: $risposta ;
"https://pdf.cmisolutions.it/pdf/split?filename=" & Documenti::Nome ;
cURL options: "--data-binary @$pdf -H \"X-Api-Key: " & $$API_KEY & "\""
]
# poi cicli sui file e salvi ognuno:
# Count ( ... ) su files[i]
# JSONGetElement ( $risposta ; "files[0].nome" ) -> Referto_1.pdf
# Base64Decode ( JSONGetElement ( $risposta ; "files[0].pdf_base64" ) ; "Referto_1.pdf" )
Risposta
JSON: n_pagine, n_file, files[] con {pagina, nome, bytes, pdf_base64}. Con format=zip: l'archivio, header X-Files, X-Filename.
Da sapere
- Ogni file di uscita è un PDF di una sola pagina.
- Con
nomipiù corti delle pagine, le pagine in eccesso usano il nome di default; inzip, due nomi uguali non si sovrascrivono (viene aggiunto_2,_3…). - Per FileMaker conviene
format=json(cicli sufiles[]e salvi ognipdf_base64con il suonome); lozipè comodo per scaricare tutto in un colpo.
POST/pdf/textX-Api-Key
Estrai il testo
Restituisce il testo del PDF, per cercarlo, indicizzarlo o popolare campi da FileMaker (es. leggere i valori da un referto). Funziona sui PDF con un vero livello di testo; su un PDF scansionato (immagini) torna vuoto, con un avviso.
| parametro | valori | default | note |
|---|---|---|---|
mode | plain | layout | columns | table | stream | plain | per parse=1: table copre i referti a colonne allineate (la maggioranza), plain quelli con più misure per riga separate da spazi singoli, stream quelli con etichetta e valore su righe diverse. columns separa le colonne affiancate → una voce per riga; layout preserva l'allineamento a griglia |
normalize | 0 | 1 | 0 | "Etichetta: 12mm" → "Etichetta 12 mm" (due punti via, unità staccata). Dà il meglio con mode=columns. Non influenza parse=1: le misure si estraggono sempre dal testo grezzo |
parse | 0 | 1 | 0 | aggiunge misure[] già divise in {etichetta, valore, unita} — così non devi parsare il testo. Usare con mode=table |
pages | es. 1,3 | tutte | limita a certe pagine |
format | json | text | json | text = testo semplice invece del JSON |
cURL
# una misura per riga (separa le colonne) — es. referti Mindray a due colonne curl -X POST -H "X-Api-Key: LA_TUA_CHIAVE" --data-binary @referto.pdf \ "https://pdf.cmisolutions.it/pdf/text?mode=columns&format=text" # misure GIA' STRUTTURATE in JSON (niente parsing lato FileMaker) curl -X POST -H "X-Api-Key: LA_TUA_CHIAVE" --data-binary @referto.pdf \ "https://pdf.cmisolutions.it/pdf/text?mode=table&parse=1"
FileMaker
Set Variable [ $pdf ; value: Referti::PDF ]
Insert from URL [
Select ; With dialog: Off ; Target: $risposta ;
"https://pdf.cmisolutions.it/pdf/text?mode=table&parse=1" ;
cURL options: "--data-binary @$pdf -H \"X-Api-Key: " & $$API_KEY & "\""
]
# poi, senza parsare il testo:
# JSONGetElement ( $risposta ; "misure[0].etichetta" ) -> IVSd
# JSONGetElement ( $risposta ; "misure[0].valore" ) -> 5.8
# Count ( ... ) loop su misure[i] per popolare un portale
Risposta
JSON: righe[] (ogni riga come elemento),
caratteri, n_pagine, mode, normalize,
dati (anagrafica utile, vedi sotto). Con parse=1 anche misure[]
([{"etichetta":"IVSd","valore":"5.8","unita":"mm","pagina":1}, …]), chiavi_duplicate
(quante etichette DISTINTE si ripetono: una ripetuta 5 volte conta 1) e chiavi_duplicate_lista
(l'elenco di quelle etichette). Nel PDF la chiave è l'etichetta: una misura ripetuta su più pagine è un
duplicato → deduplica su etichetta. Il testo completo
si ottiene con ?format=text (testo puro). Nota: le vecchie chiavi
testo e pagine[] non sono più in risposta.
Da sapere
- mode=columns risolve il caso dei referti in cui due valori stanno sulla stessa riga fisica (es.
IVSd:5.8mm IVSs:8.1mm): li mette su righe separate. Ma perparse=1usamode=table: con columns un'etichetta e il suo valore incolonnati finiscono su righe diverse e la misura si perde. - parse=1 riconosce QUATTRO impaginazioni: con i due punti (
IVSd:5.8mm); a tabella con le colonne separate da spazi (Diam. Ao 1.52 cm, anche con una colonna 'Metodo' in mezzo); più misure sulla stessa riga separate da spazi singoli (FVI MIT : 0.39 m V PICCO-E : -0.82 m/s); e a flusso verticale, un elemento per riga (etichetta / valore / marcatore / unità). - I referti a DUE COLONNE (etichetta valore unità | etichetta valore unità sulla stessa riga, es. alcuni Esaote) vengono letti per intero: con
mode=tablesi prendono entrambe le misure di ogni riga, non solo la prima. - Un ecografo = una chiamata fissa. La modalità giusta dipende da come quel modello impagina, non dal contenuto: individuala una volta con un referto di prova e poi riusala sempre per quel modello. Riscontro sui referti di collaudo:
mode=table→ GE 105 misure, Mindray M9 40, Mindray Vetus 9 64, Siemens Acuson Juniper 32;mode=plain→ Esaote 85;mode=stream→ SonoScape 57. - Se una modalità restituisce 0 misure su un modello nuovo, provale tutte prima di concludere che il PDF non è leggibile: su alcuni referti l'estrazione 'layout' torna vuota o sbriciolata mentre quella grezza è pulita, e viceversa.
- Un filtro scarta date e ID (l'unità deve iniziare con lettera o simbolo, non con
/o-) e le righe con punteggiatura fra etichetta e numero (Cardiology - 1 / 2 Pagenon è una misura). Le intestazioni di sezione (LV, Misure 2D…) non finiscono fra le misure. Resta euristico: verifica sui tuoi modelli. - Se il PDF posiziona i glifi uno per uno, il testo esce a pezzi (
2 4 .8 0) e le misure sarebbero spazzatura: in quel casomisuretorna vuoto conavviso_parse, e resta il testo grezzo. Attenzione: capita contablesu PDF che inplainostreamsi leggono benissimo — è un limite dell'estrazione 'layout', non del PDF. - Il footer/intestazione a volte si frammenta con
columns: sono righe non-misura, ignorabili. - L'OCR (testo da scansioni) richiede tesseract, di sistema → non disponibile su appbox.
- Chiave
dati(anagrafica utile):nome_proprietario,nome_animale,nome_completo,peso,clinica,ecografo. Dal PDF è best-effort — ogni ecografo impagina l'anagrafica a modo suo: cerca le etichette note (COGNOME,NOME,PESO,ISTITUTO… e gli equivalenti inglesi) e la marca dai metadati/testo se citata. I campi non trovati restano vuoti (nessun valore inventato). Per gli XML gli stessi campi sono più affidabili: vedi/xml/misure.
POST/text/misureX-Api-Key
Misure da un referto in testo (TAB)
Referti in testo: TXT con colonne separate da TAB
(etichetta ⇥ valore ⇥ unità) o CSV a virgole. Il separatore è
riconosciuto da solo. Niente euristica sull'impaginazione. Stesse chiavi di /pdf/text?parse=1 più la
sezione (presa dalle righe-titolo senza TAB, es. Mitrale, Aorta):
serve perché misure omonime in sezioni diverse (HR in più valvole) sarebbero
altrimenti indistinguibili.
Input accettati. testo grezzo nel corpo, multipart file, o JSON {"text": "..."}.
cURL
curl -X POST -H "X-Api-Key: LA_TUA_CHIAVE" \ --data-binary @referto.txt "https://pdf.cmisolutions.it/text/misure"
FileMaker
Set Variable [ $txt ; value: Referti::TestoReferto ]
Insert from URL [
Select ; With dialog: Off ; Target: $risposta ;
"https://pdf.cmisolutions.it/text/misure" ;
cURL options: "--data-binary @$txt -H \"X-Api-Key: " & $$API_KEY & "\""
]
Risposta
JSON: n_misure, caratteri,
sezioni[], chiavi_duplicate (coppie sezione+etichetta DISTINTE che si
ripetono: una ripetuta 5 volte conta 1) + chiavi_duplicate_lista (l'elenco) e
misure[] con etichetta, valore, unita, sezione.
Da sapere
- L'anagrafica e le intestazioni (righe senza TAB) non entrano nelle misure: o diventano la
sezionecorrente (se sono titoli senza cifre) o vengono ignorate. - La coppia
sezione + etichettaè la chiave: su un referto GE di collaudo 123 misure, 123 chiavi distinte, zero collisioni. - Riconosce da solo il separatore: TAB (referti GE, VividIQ, Samsung — anche lo stile
etichetta : valore unità), CSV a virgole (VINNO, con colonne Name/Valore/Unit), oppure colonne allineate a spazi (copia-incolla). Riscontro: GE 123 misure, VividIQ 93, Samsung 102, VINNO 21. - Il valore è reso com'è nel testo (punto decimale): la conversione a numero, e l'eventuale virgola, le fa FileMaker.
POST/xls/fixX-Api-Key
Correggi un .xls che FileMaker importa senza la prima colonna
Non è un'operazione su PDF, ma vive qui per non aggiungere un processo sull'appbox. Certi programmi generano .xls in cui il record DIMENSIONS del foglio — l'area dati dichiarata — è scritto con indici 1-based invece che 0-based. Excel se ne infischia e ricalcola l'area dalle celle (per questo apri e risalva guarisce il file), FileMaker invece si fida e comincia dalla colonna B: la prima colonna sparisce dall'import. Qui l'area vera viene ricalcolata dalle celle presenti e quei 14 byte riscritti: stessa lunghezza, nessuna ricostruzione del file, testi, formati e date restano identici. Un file già sano torna tale e quale, quindi puoi metterlo nel flusso di import senza chiederti se serve.
Input accettati. 1 .xls: corpo grezzo • multipart file •
JSON {"xls_base64":"…"}
| parametro | valori | default | note |
|---|---|---|---|
check | 1 | — | solo diagnosi: risponde JSON e non tocca il file |
format | bin | json | bin | bin → il .xls corretto; json → diagnosi + xls_base64 |
filename | nome | — | nome del file restituito (l'estensione è sempre .xls); col multipart si eredita dal file inviato |
cURL
# correggi curl -X POST -H "X-Api-Key: LA_TUA_CHIAVE" \ --data-binary @export.xls "https://pdf.cmisolutions.it/xls/fix" -o export_ok.xls # solo diagnosi, senza modificare nulla curl -X POST -H "X-Api-Key: LA_TUA_CHIAVE" \ --data-binary @export.xls "https://pdf.cmisolutions.it/xls/fix?check=1"
FileMaker
Set Variable [ $xls ; value: Import::File ]
Insert from URL [
Select ; With dialog: Off ; Target: Import::File_OK ;
"https://pdf.cmisolutions.it/xls/fix" ;
cURL options: "--data-binary @$xls -H \"X-Api-Key: " & $$API_KEY & "\""
]
# poi il file corretto va su disco e si importa come sempre:
# Export Field Contents [ Import::File_OK ; "$temp/export_ok.xls" ]
# Import Records [ "$temp/export_ok.xls" ]
Risposta
Il .xls (corretto o identico). Header:
X-Corretto (1 = qualcosa è stato riscritto), X-Fogli,
X-Fogli-Corretti, X-Filename, X-Bytes e
X-Diagnosi (JSON con area dichiarata e area reale, foglio per foglio).
Con check=1 o format=json: JSON con corretto,
da_correggere, n_fogli, fogli[]
(dichiarato / reale / da_correggere) e, con
format=json, xls_base64.
Da sapere
- Corregge tutti i fogli della cartella, non solo il primo.
- È idempotente: rilanciarlo su un file già corretto non fa nulla (
X-Corretto: 0) e restituisce gli stessi byte. Mettilo pure sempre prima dell'import. - Vale solo per i .xls binari (BIFF8, firma OLE). Un
.xlsxo un HTML travestito da.xlsricevono 400 con la spiegazione: quelli vanno convertiti, non corretti. - Se il difetto viene da un gestionale che esporta, vale la pena segnalarlo a chi lo sviluppa: è un off-by-one nel loro writer. Questo endpoint è la stampella, non la cura.
POST/xml/jsonX-Api-Key
Converti un XML in JSON
Converte un XML qualunque in JSON fedele alla sua struttura —
un documento causale, un tracciato, qualsiasi cosa: non serve che sia un referto o una
fattura. Utile per leggerlo in FileMaker con JSONGetElement senza plugin.
Input accettati. XML nel corpo grezzo, multipart file, o JSON {"xml_base64": "..."}.
| parametro | valori | default | note |
|---|---|---|---|
attr | prefisso | @ | prefisso delle chiavi che vengono dagli attributi XML (id="7" → @id) |
text | chiave | #text | chiave che porta il testo di un elemento che ha anche attributi o figli |
compat | simplexml | — | modalità compatibile col convertitore precedente: attributi raccolti in @attributes ed elementi vuoti come stringhe vuote |
cURL
curl -X POST -H "X-Api-Key: LA_TUA_CHIAVE" \ --data-binary @documento.xml "https://pdf.cmisolutions.it/xml/json"
FileMaker
Set Variable [ $xml ; value: Documenti::XML ]
Insert from URL [
Select ; With dialog: Off ; Target: $risposta ;
"https://pdf.cmisolutions.it/xml/json" ;
cURL options: "--data-binary @$xml -H \"X-Api-Key: " & $$API_KEY & "\""
]
# JSONGetElement ( $risposta ; "documento.@id" )
# JSONGetElement ( $risposta ; "documento.righe.riga[0].@qta" )
Risposta
Il JSON con un'unica chiave di primo livello (il nome del nodo
radice). Attributi → chiavi con prefisso @; elementi ripetuti → array;
elemento vuoto → null; una foglia di solo testo → la stringa direttamente.
Da sapere
- Per conservare i vecchi percorsi FileMaker usa
?compat=simplexml: per esempioBids.@attributes.Number. Senza il parametro l'output corrente non cambia. - Il parametro
compat=simplexmlè stato aggiunto per Cambi, per convertire in JSON l'XML ricevuto da BidInside mantenendo la struttura che in precedenza veniva prodotta tramite MBS. In questo modo gli script e i percorsi JSON già usati in FileMaker continuano a funzionare senza modifiche. - Il namespace viene tolto (si tiene il nome locale del nodo):
x:nodo→nodo. Se due nodi hanno lo stesso nome locale in namespace diversi si sovrascrivono — raro, ma possibile. - Elemento singolo vs array: se un nodo compare una sola volta diventa un oggetto, se compare più volte un array. Senza uno schema è inevitabile; in FileMaker gestisci entrambi i casi o forza il conteggio con
JSONListKeys. - Il testo dopo un tag interno (
<p>a <b>x</b> c</p>, il « c ») non viene conservato: conta per la prosa con tag in mezzo, non per i documenti-dati. - XML con
<!DOCTYPE>o<!ENTITY>è rifiutato (400): blocca gli attacchi XXE. Nessun documento dati legittimo usa un DOCTYPE. - Con
?attr=vuoto gli attributi perdono il prefisso e si mescolano ai figli: comodo se sai che non collidono, rischioso in generale.
POST/xml/misureX-Api-Key
Misure da un XML di ecografo
Molti ecografi esportano un XML oltre al PDF: quando c'è, è
sempre la fonte migliore. Niente euristica sull'impaginazione — etichetta, valore e unità
sono campi espliciti, quindi l'estrazione è deterministica. L'uscita ha le stesse chiavi
di /pdf/text?parse=1, così il codice FileMaker non cambia a seconda della fonte.
La chiave dati porta l'anagrafica utile del referto (nome proprietario e
animale, peso, clinica, marca ecografo); i campi che la fonte non contiene restano vuoti.
Input accettati. XML nel corpo grezzo, multipart file, o JSON {"xml_base64": "..."}.
| parametro | valori | default | note |
|---|---|---|---|
formato | esaote | samsung | philips | — (obbligatorio) | un adattatore per costruttore: gli schemi XML non si somigliano affatto (Esaote <ExamExport>, Samsung Medison <UltraSound>, Philips HL7 CDA <ClinicalDocument>). L'elenco aggiornato è su GET /xml/formati |
cURL
curl -X POST -H "X-Api-Key: LA_TUA_CHIAVE" \ --data-binary @referto.xml "https://pdf.cmisolutions.it/xml/misure?formato=esaote"
FileMaker
Set Variable [ $xml ; value: Referti::XML ]
Insert from URL [
Select ; With dialog: Off ; Target: $risposta ;
"https://pdf.cmisolutions.it/xml/misure?formato=esaote" ;
cURL options: "--data-binary @$xml -H \"X-Api-Key: " & $$API_KEY & "\""
]
# JSONGetElement ( $risposta ; "misure[0].chiave" )
# -> Ventricolo Sinistro - Ventricolo Sx - SIVs - IDS_CARDIO_IVSS
Risposta
JSON: formato, n_misure, bytes,
chiavi_duplicate (chiavi DISTINTE che si ripetono: una ripetuta 5 volte conta 1) +
chiavi_duplicate_lista (l'elenco), misure[] e dati (anagrafica:
nome_proprietario, nome_animale, nome_completo, peso,
clinica, ecografo; campi vuoti se assenti nella fonte). Ogni misura ha le chiavi compatibili col PDF
(etichetta, valore, unita), la gerarchia per l'identificazione
univoca (paragrafo, capitolo, abbreviazione, id e
chiave già composta) e il contesto (pagina_xml, sigla,
id_univoco, campo_db, modo, esame,
calcolata).
Da sapere
- Nessun campo è univoco da solo, nemmeno quello che si chiama
UniqueID(misurato: 75 valori distinti su 92 misure;id73;Abbreviation74). Solo la combinazioneparagrafo - capitolo - abbreviazione - idlo è — ed è quella che trovi già pronta inchiave. Il motivo: la stessa grandezza viene riusata da più calcoli (il diametro LVOT compare 5 volte, identico) e a volte lo stessoidvale su vasi diversi (IDS_CARDIO_PVè −1.54 m/s in Aorta e −0.80 m/s in A Polmonare: lì il contesto È il significato). - Vengono restituite solo le misure con
isValid="true", cioè quelle davvero eseguite: nel referto di collaudo 92 valide su 679 slot previsti dal modello. Dal PDF questa distinzione non è ricavabile (uno 0.00 non si distingue da un 'non misurato'). calcolatadistingue il valore misurato dall'operatore da quello derivato dall'ecografo con una formula (nel collaudo: 41 misurate, 51 calcolate). I derivati non aggiungono informazione e dipendono dalla formula del costruttore: per confrontare apparecchi diversi conviene basarsi sui misurati.- XML con
<!DOCTYPE>o<!ENTITY>vengono rifiutati (HTTP 400). Senza DTD non esistono entità personalizzate, quindi cadono in blocco gli attacchi XXE (leggere/etc/passwdo raggiungere host interni) e le bombe a espansione ricorsiva. Nessun referto legittimo usa un DOCTYPE. - Se l'XML non è del formato dichiarato torna 200 con
misurevuoto e un avviso, non un errore: così distingui 'formato sbagliato' da 'esame senza misure'. - Samsung Medison (
formato=samsung, radice<UltraSound>): la sezione è la caption dell'Exam (LV (M), MV, AV, Aorta…), il valore èresult(già col segno corretto: es. AV Vmax vale 1.72 anche se il campione grezzom1è −1.72), la chiave univoca è l'id del Label (es.CARD_IVSD_M_ITEM). Oltre aetichetta/valore/unitaci sonosezione,tipo_risultato,m1,side,location. I campi Esaote (paragrafo,capitolo,calcolata…) valgono solo performato=esaote. - Philips (
formato=philips, radice<ClinicalDocument>, standard HL7 CDA): le misure stanno nelle sezioni 2D, M-mode, Doppler e Altre misurazioni. Oltre aetichetta/valore/unitatornanosezioneegruppo; la chiave univoca èsezione - gruppo - etichetta. Il gruppo è indispensabile: la stessa etichetta ricorre sotto contesti diversi (Vol LVsta sottoA4CdeA4Cs,Area/Lunghezzasotto molti gruppi). Attenzione: alcuni export Philips sono solo il 'guscio' del referto (intestazione, firma) senza misure — lì tornan_misure=0con avviso, va riesportato includendo le misure. - Chiave
dati— cosa aspettarsi: il nome è quasi sempre un unico campo mescolato (MARCHESE,LOLA,COCKER,7KGPhilips;ROVELLI,AXEL,CAVALIEREsaote;ZANNA, PIPPOSamsung): torna grezzo innome_completoe diviso best-effort innome_proprietario(1º campo) /nome_animale(2º; un nome senza virgole va tutto innome_animale). Il peso come campo a sé è raro: si recupera dal nome quando c'è (7KG). La clinica c'è su Philips (custodian) ed Esaote2 (InstitutionName), non sugli altri. Il modello ecografo non è nei file, quindiecografoporta la marca (Philips/Esaote/Samsung). Esaote4 e simili non hanno anagrafica → solo la marca.
Comuni
Autenticazione e stato del servizio
Valgono per ogni endpoint di questo servizio.
GET/healthsenza chiave
Stato del servizio
Sonda di vita, aperta e non contabilizzata: un check ogni minuto non sporca le statistiche.
cURL
curl https://pdf.cmisolutions.it/health
Risposta
JSON con status, service, la versione della libreria e auth (se l'obbligo di chiave è attivo).
—Autenticazionesenza chiave
Come funzionano le chiavi
Una chiave = un soggetto (un database FileMaker, uno script, n8n). Ogni chiave porta l'elenco dei servizi su cui è abilitata, quindi la stessa chiave può valere su più servizi — ed è quello che vuoi: così le statistiche attribuiscono tutto il consumo a un'unica riga. Non condividere una chiave fra due soggetti: perderesti proprio l'informazione per cui l'hai creata. Le chiavi le gestisce l'amministratore: se te ne serve una, o la tua non funziona più, chiedila a lui.
cURL
# su ogni POST di lavoro -H "X-Api-Key: cmi_xxxxxxxxxxxx" # in FileMaker, tenendo la chiave in una variabile globale sola: "-H \"X-Api-Key: " & $$API_KEY & "\""
Risposta
Se la chiave manca o è errata: 401 con un messaggio esplicito (mancante, non riconosciuta, revocata, non abilitata per questo servizio).
Da sapere
- Restano aperti senza chiave:
/healthe la pagina di test su/. - Revoche e nuove chiavi hanno effetto immediato, senza riavviare i servizi.
- Oltre ai servizi, una chiave può essere ristretta ai singoli endpoint. Se è abilitata al servizio ma non a quella rotta la risposta è 403 (non 401: la chiave è valida, manca il permesso). Le restrizioni si impostano dal pannello di amministrazione e valgono subito.
- Ogni risposta porta
X-Esito: ok|erroreeX-Status. Servono da FileMaker:Insert from URLNON fallisce sugli errori HTTP —Get(LastError)resta 0 e il corpo dell'errore finisce nel contenitore, sovrascrivendo l'allegato buono. Con--dump-header $hsi controlla l'esito prima di scrivere sul campo. Regola d'oro: mandare il risultato in una variabile, verificare, e solo allora fareSet Field. - Nome del file restituito: gli endpoint che restituiscono un file accettano
?filename=. Senza, si usa il nome originale se l'invio è multipart (che lo porta con sé); con--data-binaryil nome si perde, quindi lì il parametro serve. L'estensione è sempre quella d'uscita, sostituita e non appesa. Il nome è anche nell'headerX-Filename.