pdfservice
X-Api-Key; restano aperti
/health, la pagina di test e i link condivisi. Il servizio è raggiungibile da Internet
senza TLS: non ci sono password o dati riservati negli URL, ma i documenti viaggiano in
chiaro.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) · http://pdf.cmisolutions.it:3003
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.
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 \ "http://pdf.cmisolutions.it:3003/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 ;
"http://pdf.cmisolutions.it:3003/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/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 \ "http://pdf.cmisolutions.it:3003/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 ;
"http://pdf.cmisolutions.it:3003/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/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 \ "http://pdf.cmisolutions.it:3003/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 ;
"http://pdf.cmisolutions.it:3003/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/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 "http://pdf.cmisolutions.it:3003/pdf/delete?pages=2,4" -o ridotto.pdf
Risposta
Il PDF. Header X-Pages.
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 "http://pdf.cmisolutions.it:3003/pdf/rotate?deg=90&pages=1" -o ruotato.pdf
Risposta
Il PDF. Header X-Pages.
POST/pdf/from-imagesX-Api-Key
Più immagini → un PDF multipagina
Una immagine per pagina, nell'ordine di invio. 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 |
|---|---|---|---|
page | a4 | letter | auto | a4 | auto = pagina su misura dell'immagine; con a4/letter l'orientamento del foglio segue quello dell'immagine |
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; 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 \ "http://pdf.cmisolutions.it:3003/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 ;
"http://pdf.cmisolutions.it:3003/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-Bytes.
Da sapere
- Il caso tipico: fotografi più pagine col telefono e ottieni un unico PDF. FileMaker da solo non lo fa.
- Con
page=a4l'immagine viene adattata al foglio (anche ingrandita, se piccola). Conpage=autola pagina prende la misura esatta dell'immagine e non c'è nessun adattamento. - Per un risultato più leggero, concatena con
/pdf/compress?quality=60&maxw=1600, oppure passa giàmaxwqui.
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 \ "http://pdf.cmisolutions.it:3003/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/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 "http://pdf.cmisolutions.it:3003/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"}' \
"http://pdf.cmisolutions.it:3003/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 ;
"http://pdf.cmisolutions.it:3003/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/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 \ "http://pdf.cmisolutions.it:3003/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 \ "http://pdf.cmisolutions.it:3003/pdf/text?mode=table&parse=1"
FileMaker
Set Variable [ $pdf ; value: Referti::PDF ]
Insert from URL [
Select ; With dialog: Off ; Target: $risposta ;
"http://pdf.cmisolutions.it:3003/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: testo (tutto), righe[] (ogni riga come
elemento), pagine[] (con righe per pagina), caratteri,
n_pagine, mode, normalize. Con parse=1 anche
misure[]: [{"etichetta":"IVSd","valore":"5.8","unita":"mm","pagina":1}, …].
Con format=text, testo puro.
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à). - 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.
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 "http://pdf.cmisolutions.it:3003/text/misure"
FileMaker
Set Variable [ $txt ; value: Referti::TestoReferto ]
Insert from URL [
Select ; With dialog: Off ; Target: $risposta ;
"http://pdf.cmisolutions.it:3003/text/misure" ;
cURL options: "--data-binary @$txt -H \"X-Api-Key: " & $$API_KEY & "\""
]
Risposta
JSON: n_misure, caratteri,
sezioni[], chiavi_duplicate 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/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 \ "http://pdf.cmisolutions.it:3003/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 ;
"http://pdf.cmisolutions.it:3003/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/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" "http://pdf.cmisolutions.it:3003/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" "http://pdf.cmisolutions.it:3003/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_uri{BASE}/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/validaX-Api-Key
Valida partita IVA / codice fiscale
Validazione formale (il carattere di controllo torna) di una
partita IVA o di un codice fiscale italiano. Intercetta gli errori di battitura —
è il controllo che serve quasi sempre. Non verifica che esistano davvero (per quello
servono VIES / Agenzia Entrate, esterni). /valida riconosce da sé il tipo; ci sono
anche /valida/piva e /valida/cf se vuoi essere esplicito.
Input accettati. valore in ?v=, nel corpo grezzo, o JSON {"v": "..."}.
| parametro | valori | default | note |
|---|---|---|---|
v | il valore | — | il codice da validare. In alternativa nel corpo grezzo o in JSON {"v": ...} |
cURL
curl -X POST -H "X-Api-Key: LA_TUA_CHIAVE" \ --data "RSSMRA80A01H501U" "http://pdf.cmisolutions.it:3003/valida"
FileMaker
Insert from URL [
Select ; With dialog: Off ; Target: $risposta ;
"http://pdf.cmisolutions.it:3003/valida" ;
cURL options: "--data " & Quote ( Anagrafica::PIVA )
& " -H \"X-Api-Key: " & $$API_KEY & "\""
]
# JSONGetElement ( $risposta ; "valido" ) -> true / false
# JSONGetElement ( $risposta ; "messaggio" )
Risposta
JSON: valido (true/false), tipo
(piva | cf), valore (normalizzato), messaggio.
Per il codice fiscale valido anche i dati codificati: sesso, anno,
mese, giorno, codice_comune.
Da sapere
- È una validazione formale: dice che il codice è ben formato, non che appartiene a un soggetto reale. Un codice inventato ma con il checksum giusto passa.
- Gli spazi vengono tolti e il codice fiscale è reso maiuscolo prima del controllo, così un incolla con spazi non fa fallire la validazione.
- Il codice fiscale gestisce l'omocodia (le cifre sostituite da lettere quando due persone avrebbero lo stesso codice): la data e il comune vengono comunque sciolti correttamente.
- La verifica di esistenza (P.IVA attiva su VIES, CF associato a una persona) è un'altra cosa e richiede chiamate a servizi esterni: non è compresa qui.
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 "http://pdf.cmisolutions.it:3003/xml/json"
FileMaker
Set Variable [ $xml ; value: Documenti::XML ]
Insert from URL [
Select ; With dialog: Off ; Target: $risposta ;
"http://pdf.cmisolutions.it:3003/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.
Anagrafica, struttura, veterinario e proprietario non vengono MAI letti: gli
adattatori non percorrono quei rami dell'XML, non è un filtro che si possa disattivare.
Input accettati. XML nel corpo grezzo, multipart file, o JSON {"xml_base64": "..."}.
| parametro | valori | default | note |
|---|---|---|---|
formato | esaote | — (obbligatorio) | un adattatore per costruttore: gli schemi XML non si somigliano affatto. L'elenco aggiornato è su GET /xml/formati |
cURL
curl -X POST -H "X-Api-Key: LA_TUA_CHIAVE" \ --data-binary @referto.xml "http://pdf.cmisolutions.it:3003/xml/misure?formato=esaote"
FileMaker
Set Variable [ $xml ; value: Referti::XML ]
Insert from URL [
Select ; With dialog: Off ; Target: $risposta ;
"http://pdf.cmisolutions.it:3003/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 e misure[]. 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'.
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 "http://pdf.cmisolutions.it:3003/pdf/encrypt" -o protetto.pdf
FileMaker
Set Variable [ $pdf ; value: Documenti::PDF ]
Insert from URL [
Select ; With dialog: Off ; Target: Documenti::PDF_Protetto ;
"http://pdf.cmisolutions.it:3003/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/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 "http://pdf.cmisolutions.it:3003/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.
Comuni
Autenticazione e stato del servizio
Valgono per ogni endpoint di questo servizio.
—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.
GET/healthsenza chiave
Stato del servizio
Sonda di vita, aperta e non contabilizzata: un check ogni minuto non sporca le statistiche.
cURL
curl http://pdf.cmisolutions.it:3003/health
Risposta
JSON con status, service, la versione della libreria e auth (se l'obbligo di chiave è attivo).