Capitolo 8 · Parte II · Il mestiere
Configurazione: risorse e tabelle
I tipi di risorsa e la loro numerazione, gli stili di tabelle e didascalie, i diagrammi a inchiostro unico e i video in stampa
In breve
Questa pagina tratta le impostazioni di figure, tabelle, diagrammi e video. Postext li chiama risorse e numera ogni tipo per conto suo. Stabilisci l’aspetto delle tabelle: i filetti, i fondi, i caratteri e il modo in cui si dividono tra le pagine. Stabilisci come si scrive la didascalia di una figura o di una tabella. Puoi anche stampare i diagrammi con un solo colore d’inchiostro e scegliere come appare un video sulla carta.
#Tipi di risorsa
Un tipo di risorsa è una categoria definibile dall’utente (Figura, Tabella, Diagramma, Listato…) che determina come vengono numerate, accompagnate da didascalia e richiamate le risorse di quel tipo. L’elenco si trova in config.resourceTypes; la Sandbox lo modifica in Grafica → Figure e tabelle → Numerazione e posizione.
Quando config.resourceTypes non è impostato, Postext fornisce tre tipi predefiniti integrati: Figura, Tabella e Video, ciascuno numerato per conto suo con {h1}.{n} (che riparte a ogni titolo di livello 1) e contatori decimali. Un elenco senza un tipo video (un libro salvato prima che esistessero i video) numera comunque le risorse video di tipo video: per loro viene aggiunto il tipo Video integrato (effectiveResourceTypes(config, resources)), e defaultVideoResourceType(locale) lo restituisce da solo. I nomi sono nella lingua del documento: config.locale, altrimenti bodyText.hyphenation.locale, altrimenti l’inglese (vedi Lingua del documento).
I tipi predefiniti integrati tengono conto della lingua. La funzione esportata defaultResourceTypes(locale = 'en') localizza i nomi dei tipi, le etichette brevi e i prefissi delle didascalie nella lingua del documento: l’inglese dà Figure/Fig. e Table/Tab.; lo spagnolo dà Figura/Fig. e Tabla/Tabla; anche francese, tedesco, italiano, portoghese, catalano e olandese hanno i loro (la tabella in Lingua del documento li elenca). Le etichette regionali come es-ES si risolvono per lingua, e qualsiasi lingua senza traduzioni ripiega sull’inglese. Il comportamento della numerazione (numberingTemplate: '{h1}.{n}', resetOn: 'h1', contatori decimali) non dipende dalla lingua. Ogni chiamata restituisce oggetti nuovi, quindi puoi modificare liberamente il risultato:
import { defaultResourceTypes } from 'postext';
const types = defaultResourceTypes('es');
// => [{ id: 'figure', name: 'Figura', shortLabel: 'Fig.', captionPrefix: 'Figura',
// numberingTemplate: '{h1}.{n}', resetOn: 'h1', counterFormat: 'decimal', … },
// { id: 'table', name: 'Tabla', shortLabel: 'Tabla', captionPrefix: 'Tabla', … },
// { id: 'video', name: 'Vídeo', shortLabel: 'Vídeo', captionPrefix: 'Vídeo', … }]type ResourceCounterFormat =
| 'decimal'
| 'roman-lower'
| 'roman-upper'
| 'alpha-lower'
| 'alpha-upper';
type ResourceCounterReset = 'never' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
interface ResourcePlacement {
position?: 'auto' | 'top' | 'bottom' | 'here'; // which free slot a float may take; 'here' = inline embed at the ::resource directive
span?: 'column' | 'page' | 'side'; // one column, the full content width, or the float-only side column
rotate?: 'ccw' | 'cw'; // a quarter turn: a landscape table on a page of its own
width?: number; // fraction (0 < width < 1) of the column or page width; default: the whole width
align?: 'left' | 'center' | 'right'; // where a float narrower than its column sits; default 'left'
captionSide?: boolean; // caption beside the figure, in the side column of a oneAndHalf layout (column floats and resources set in the text)
columns?: number; // a 'column' float across this many adjacent columns (since 1.18)
wrap?: 'none' | 'left' | 'right' | 'start' | 'end'; // text beside the resource, at that side of its column (since 1.24)
wrapGap?: Dimension; // space between the wrapped resource and the text (since 1.24)
}
interface ResourceType {
id: string; // stable id, referenced by Resource.typeId
name: string; // singular display name, e.g. "Figure"
namePlural?: string; // optional plural, e.g. "Figures"
shortLabel: string; // compact label for inline refs, e.g. "Fig."
numberingTemplate: string; // "{h1}.{n}" or "{n}"
resetOn: ResourceCounterReset; // when the {n} counter resets
counterFormat: ResourceCounterFormat;// how {n} is formatted
captionPrefix: string; // prepended to the caption, e.g. "Figure"
defaultPlacement?: ResourcePlacement;// fallback placement for this type's resources
}ResourcePlacement ha la stessa forma del placement che una risorsa imposta per conto proprio. position sceglie il tipo di spazio libero che un elemento flottante può occupare: auto (il valore predefinito) prende il primo dopo il primo rimando, top / bottom lo limitano a quel tipo di fascia, here incorpora la risorsa nel testo. span imposta l’estensione dell’elemento flottante: una colonna, l’intera larghezza del contenuto o la colonna laterale riservata agli elementi flottanti di un impaginato a colonna e mezza. rotate ruota la risorsa di un quarto di giro e ne fa un elemento flottante a tutta pagina su una pagina tutta sua. width restringe un elemento flottante a una frazione della sua colonna (o della pagina, per un elemento a tutta larghezza): per esempio una tabella piccola in una colonna larga. align dice dove si colloca un elemento flottante più stretto (a sinistra per impostazione predefinita, al centro o a destra) e dove si colloca nel suo spazio un’immagine più stretta di quello spazio: una bitmap più piccola della colonna, o un’immagine ridotta da layout.fitFiguresToPage. La didascalia e la nota mantengono la giustezza dello spazio. (Fino a postext 1.4 un’immagine del genere veniva sempre allineata a sinistra.) captionSide colloca la didascalia accanto alla figura nella colonna laterale riservata agli elementi flottanti di un impaginato a colonna e mezza (layout.sideColumnRole: 'floats'), allineata alla testa della figura (al piede, per un elemento flottante in basso); si applica agli elementi flottanti di colonna e, da postext 1.28, a una risorsa composta nel testo (position: 'here', larga una colonna, senza testo che le scorra attorno), la cui didascalia e la cui nota stanno nella colonna laterale all’altezza della testa del corpo della risorsa dove quella colonna è libera accanto, e sotto il corpo dove non lo è (layout.inlineCaptionSide, attivo per impostazione predefinita; false riserva le didascalie laterali agli elementi flottanti di colonna); una pagina senza una colonna del genere tiene la didascalia sotto la figura. Quando né la risorsa né il suo tipo impostano un posizionamento, vale il predefinito integrato auto / column. shrink ('never', 'page', 'slot') e minScale (0.7 se non impostato) riducono un’immagine flottante allo spazio disponibile invece di spostarla oltre, e captionMeasure: 'body' compone la didascalia e la nota di un’immagine più stretta del suo spazio alla larghezza dell’immagine (vedi Formato del documento › Posizionamento); layout.floatShrink dà il valore predefinito del documento per i primi due. wrap colloca un’incorporazione nel testo o un elemento flottante di una colonna su un lato della sua colonna con il testo che scorre accanto, a distanza wrapGap; layout.wrap contiene i valori predefiniti (vedi Formato del documento › Testo attorno alle risorse). citingPage permette a un elemento flottante top o auto di aprire la pagina o la colonna in cui cade la riga che lo cita, invece di prendere il primo spazio libero successivo; layout.floatsAtCitingPage dà il valore predefinito del documento e layout.maxTopFraction la quota della colonna che può occupare (vedi Formato del documento › Posizionamento).
columns (da postext 1.18) estende un elemento flottante span: 'column' su quel numero di colonne adiacenti in una pagina che ne ha diverse: un’immagine su due delle cinque colonne di un quotidiano. La sua giustezza è data da quelle colonne e dagli intercolonni tra loro. Occupa la testa di una serie di colonne vuote che cominciano alla stessa altezza, oppure il piede della colonna che la cita e delle colonne vuote successive; tante colonne quante ne ha la pagina, o più, ne fanno un elemento flottante a tutta larghezza. Viene ignorato dagli span 'page' e 'side', da una risorsa ruotata (rotate) e da un’incorporazione nel testo (here), e captionSide si applica solo a un elemento flottante largo una colonna.
| Proprietà | Tipo | Descrizione |
|---|---|---|
id | string | Identificatore stabile a cui fa riferimento il typeId di ogni risorsa. Si imposta una volta, quando il tipo viene creato; eliminare un tipo a cui alcune risorse fanno ancora riferimento genera un avviso di tipo orfano. |
name | string | Nome visualizzato al singolare. Usato dal rimando in linea style="full" (ad esempio Figure 1.7). |
namePlural | string (opzionale) | Nome visualizzato al plurale, per le etichette dell’interfaccia e gli elenchi di risorse. |
shortLabel | string | Abbreviazione compatta usata dallo stile di rimando in linea predefinito (ad esempio Fig. 1.7). |
numberingTemplate | string | Modello per il numero calcolato. Vedi Token del modello più avanti. Le forme comuni sono (per capitolo, ad esempio 2.3) e (un conteggio progressivo unico). |
resetOn | ResourceCounterReset | 'never' dà un unico conteggio progressivo per tutto il documento; da 'h1' a 'h6' azzerano il contatore ogni volta che si incontra un titolo di quel livello (o di un livello superiore). Imposta il valore corrispondente al livello di titolo che compare nel modello: ad esempio con resetOn: 'h1'. |
counterFormat | ResourceCounterFormat | Come viene scritto il contatore : decimale (1, 2, 3), romano minuscolo/maiuscolo (i, ii / I, II) o alfabetico minuscolo/maiuscolo (a, b / A, B). Sono accettate anche le grafie delle pagine e degli elenchi ('lower-roman', 'arabic'…; vedi Grafie dei formati di numerazione); un valore sconosciuto conta in decimale e viene segnalato. I token dei titoli (…) sono sempre scritti in decimale. |
captionPrefix | string | Testo anteposto alla didascalia della figura o della tabella. Il numero calcolato segue il prefisso: una didascalia appare come . , ad esempio Figura 1.7. La pianta originale. Un tipo con numberingTemplate vuoto non ha numero, e la sua didascalia appare come . . Gli spazi alla fine del prefisso vengono eliminati, e un prefisso che termina già con ., :, !, ? o … (o con la loro forma a larghezza piena) non riceve un secondo punto: Tav. Righe a 0°. |
defaultPlacement | ResourcePlacement (opzionale) | Posizionamento usato dalle risorse di questo tipo che non impostano un proprio placement: position, span, rotate, width, align, captionSide e columns, ciascuno risolto in modo indipendente. Quando né la risorsa né il tipo impostano un campo, vale il predefinito integrato: auto / column, dritta, a tutta larghezza, allineata a sinistra, didascalia sotto la figura. Vedi Numerazione e rimandi più avanti per la catena di risoluzione e Formato del documento › Risorse per l’effetto di ogni valore, comprese le risorse ruotate. |
captionStyle | CaptionStyleConfig (opzionale) | Sostituzione parziale dello stile delle didascalie per le risorse di questo tipo. Solo le chiavi che imposti sostituiscono il captionStyle globale; tutto il resto viene ereditato (un color sostituito determina anche i colori dell’etichetta e della nota, a meno che questi non siano impostati esplicitamente). Uso tipico: tabelle con didascalia sopra su una barra colorata, mentre le figure tengono la didascalia sotto. I riferimenti alla tavolozza si risolvono come qualsiasi altro colore. |
#Token del modello
numberingTemplate viene elaborato dallo stesso motore della numerazione dei titoli (vedi Titoli). Riconosce due tipi di token:
{n}: il contatore del tipo, formattato secondocounterFormat. È il valore che aumenta a ogni risorsa e si azzera secondoresetOn.- da
{h1}a{h6}: i numeri dei titoli in vigore nel punto del primo rimando, in decimale a meno che il token non indichi uno stile.{h1}è il numero del titolo di livello 1 corrente,{h2}quello di livello 2, e così via. {h1:A},{h2:i},{n:i}: un token seguito da due punti e da uno stile viene scritto in quello stile, uno qualsiasi di quelli accettati da un modello di titolo:1,01,A,a,I,i, il nome di un formato di numerazione comeupper-roman, un token di un carattere come一o①, o uno stile in lettere comeWords. Su{n}lo stile prevale sucounterFormatper quel token. Uno stile sconosciuto conta in decimale. Da postext 1.28.
Qualsiasi altro testo è letterale. Una barra rovesciata rende letterale un {, un } o un \. Quando un token di titolo non ha un valore disponibile (ad esempio {h1} prima di qualsiasi titolo di livello 1), sparisce insieme al separatore adiacente: così {h1}.{n} si riduce senza problemi al semplice contatore.
Un modello vuoto ('') non stampa alcun numero, anche se il tipo continua a contare le sue risorse: una didascalia appare come Do. Lines at 0° e un :ref stampa la sola etichetta (Do). Fino a postext 1.4 una didascalia del genere appariva come Do .Lines at 0° e il rimando terminava con uno spazio indivisibile.
| Modello | Con h1 = 2, contatore = 3 | Note |
|---|---|---|
| 3 | Un conteggio progressivo unico. Da abbinare a resetOn: 'never'. |
| 2.3 | Per capitolo. Da abbinare a resetOn: 'h1'. |
| 2.0.3 | Per sezione. Da abbinare a resetOn: 'h2'. |
| B.3 | Con lettere, per le appendici. Da abbinare a resetOn: 'h1'. |
Appendici. Un libro le cui appendici sono contrassegnate da lettere tramite uno stile di titolo (numberingTemplate: 'Appendix {1:A}', con startAt=1 sulla prima) dà alle loro figure e tabelle un tipo a sé, { ...table, id: 'table-a', numberingTemplate: '{h1:A}.{n}', resetOn: 'h1' }. Un solo tipo serve tutte le appendici: stampa Table A.1, A.2 e B.1, nella didascalia e nel :ref. Un semplice {h1} in quella posizione stampa il numero del titolo in decimale (Table 1.1).
#Numerazione e rimandi
Il numero prodotto da un tipo di risorsa è ciò che :ref stampa e ciò che il prefisso della didascalia precede. :ref{id} è la forma principale: il primo rimando in ordine di lettura incorpora la risorsa, che flotta nel primo spazio libero dopo quel rimando, cioè il piede della colonna che la richiama, la testa o il piede della colonna vuota successiva, o una fascia della pagina successiva (secondo il posizionamento risolto: position: 'auto' | 'top' | 'bottom' | 'here' e span: 'column' | 'page' | 'side', più rotate, width, align e captionSide, risolti per ogni risorsa, poi con il defaultPlacement del tipo, poi con il predefinito integrato auto / column; 'top' / 'bottom' limitano la ricerca a quel tipo di spazio). L’incorporazione a blocco ::resource{id} è facoltativa e serve solo per placement.position: 'here', un’incorporazione nel testo, non flottante, in un punto preciso del flusso. La grammatica completa lato documento (entrambe le forme più le opzioni style e text di :ref) è documentata in Formato del documento › Risorse, compreso il modo in cui l’ordine dei primi rimandi determina il conteggio.
È lo stesso schema della numerazione dei titoli: come un livello di titolo porta un numberingTemplate, ne porta uno anche un tipo di risorsa; ma il contatore delle risorse ({n}) avanza a ogni primo rimando anziché a ogni titolo, e resetOn lo ricollega alla gerarchia dei titoli.
#Che cosa viene numerato
Una risorsa viene numerata quando il testo la richiama (con :ref o con un’incorporazione ::resource), nell’ordine di quei primi rimandi, qualunque sia il suo posizionamento: flottante, nel testo (here), nella colonna laterale o ruotata. Una risorsa disegnata solo da un elemento grafico (un elemento image di un’apertura di capitolo, di una testatina o di una pagina di parte) o che nessuno richiama non riceve un numero e non fa avanzare il contatore del suo tipo. Così, in un saggio fotografico le cui tavole al vivo sono immagini delle aperture e la cui unica tavola più piccola è un elemento flottante citato, quell’elemento è la tavola I, per quante tavole abbiano mostrato prima le aperture; numera le tavole delle aperture nel loro progetto grafico (con un attributo come {attr.plate}) e lascia il contatore alle tavole citate dal testo.
In un libro impaginato capitolo per capitolo (la Sandbox, buildBundle o buildDocument con i contatori passati da continuationAfter()), conta il primo rimando dell’intero libro: una risorsa mantiene il numero ricevuto nel capitolo che la richiama per primo, e solo quel capitolo la colloca. Il :ref di un capitolo successivo stampa quel numero e non colloca nulla, e lì un’incorporazione ::resource di una risorsa flottante è solo un altro rimando (un’incorporazione nel testo here viene comunque composta dove è scritta). Un rimando del genere è un link alla figura quando la figura si trova nello stesso output: un PDF dell’intero libro lo collega alla pagina del capitolo precedente. Un capitolo generato da solo, in HTML o PDF, lo compone come testo semplice nel colore dei link, perché la sua figura non è in quel documento. Un host che riunisce l’HTML dei capitoli in una sola pagina passa a renderToHtml le risorse ancorate dai capitoli, come refTargets, e un rimando del genere torna a essere un link alla figura del capitolo precedente:
import { anchoredResourceIds, buildBundle, renderToHtml } from 'postext';
const docs = buildBundle(bundle);
const refTargets = new Set(docs.flatMap((d) => [...anchoredResourceIds(d)]));
const html = docs.map((d) => renderToHtml(d, { refTargets })).join('');Cambiato in postext 1.5: fino alla 1.4 ogni capitolo che richiamava una figura la faceva flottare di nuovo, e l’HTML di ogni :ref era un link, che la sua figura fosse nella pagina oppure no.
{h1} è il conteggio progressivo dei titoli di livello 1: ogni H1 lo fa avanzare, a meno che il suo stile di titolo non imposti numbered: false; un numberingTemplate vuoto nasconde il numero del titolo, ma non ferma il conteggio. Un articolo il cui unico H1 è il titolo numera quindi le sue figure 1.1, 1.2… con i tipi integrati {h1}.{n}. Due modi per stampare Figura 1, 2…:
- un tipo numerato
{n}conresetOn: 'never'(conresetOn: 'h1'il conteggio ripartirebbe a ogni H1); - uno stile di titolo con
numbered: falsesul titolo, e su ogni altro H1 che non deve contare: un titolo del genere non fa avanzare{h1}ma lo lascia com’era (vuoto prima del primo H1 contato, dove{h1}.{n}si riduce al semplice contatore) e non fa mai scattareresetOn: 'h1', così il conteggio prosegue oltre. Dopo# Introductione la sua Figura 1.1, la prima figura sotto un# Appendixnon numerato è la 1.2, non la 2.1.
// Figure 1, 2, 3… in a single-article document
resourceTypes: defaultResourceTypes('en').map((t) => ({ ...t, numberingTemplate: '{n}', resetOn: 'never' })),#Stile delle tabelle
La proprietà tableStyle controlla la tipografia e la decorazione delle risorse di tipo tabella: di tutte le tabelle, a meno che una non scelga uno stile di tabella con nome. Le celle del corpo e quelle d’intestazione hanno stili indipendenti. Famiglia, corpo e colori del carattere ereditano quelli risolti del testo corrente quando non sono impostati, così un documento senza tableStyle compone le tabelle con la tipografia del testo corrente.
const config: PostextConfig = {
tableStyle: {
headerBold: true,
headerBackground: { hex: '#f0f0f0', model: 'hex' },
borders: true,
borderWidth: { value: 0.75, unit: 'pt' },
},
};| Proprietà | Tipo | Predefinito | Descrizione |
|---|---|---|---|
bodyFontFamily | string | font del testo corrente | Famiglia di caratteri delle celle del corpo. |
bodyFontSize | Dimension | corpo del testo corrente | Corpo del carattere delle celle del corpo. |
bodyColor | ColorValue | colore del testo corrente | Colore del testo delle celle del corpo. |
headerFontFamily | string | font del testo corrente | Famiglia di caratteri delle celle d’intestazione. |
headerFontSize | Dimension | corpo del testo corrente | Corpo del carattere delle celle d’intestazione. |
headerColor | ColorValue | colore del testo corrente | Colore del testo delle celle d’intestazione. |
headerBold | boolean | true | Compone le celle d’intestazione in grassetto. |
headerItalic | boolean | false | Compone le celle d’intestazione in corsivo. |
headerLetterSpacing | Dimension | 0pt | Tracking dopo ogni carattere di una cella d’intestazione, spazi compresi, come il letter-spacing del CSS. I valori positivi distanziano le lettere (un’intestazione in maiuscolo di solito vuole da 0.05em a 0.1em), quelli negativi le stringono. Un em è il corpo dell’intestazione. Le righe d’intestazione vengono misurate con questo valore, quindi vanno a capo, si centrano e si allineano tenendo conto del tracking, e canvas, HTML e PDF lo disegnano allo stesso modo. Si applica a ogni cella d’intestazione: le righe d’intestazione e qualsiasi cella marcata isHeader. |
headerTextTransform | 'none' | 'uppercase' | 'none' | Compone le celle d’intestazione in maiuscolo. Il testo mantiene la sua lunghezza, così la Sandbox continua a far corrispondere ogni lettera al sorgente: una lettera la cui maiuscola è più lunga (ß) resta com’è. I rimandi alle risorse mantengono la loro etichetta. |
headerBackgroundEnabled | boolean | true | Disegna un fondo dietro la riga d’intestazione. |
headerBackground | ColorValue | #f0f0f0 | Colore di fondo della riga d’intestazione. |
bodyBackgroundEnabled | boolean | false | Disegna un fondo dietro le righe del corpo. |
bodyBackground | ColorValue | #ffffff | Colore di fondo delle righe del corpo (disegnato solo se attivo). |
bodyAlternateBackgroundEnabled | boolean | false | Righe alternate: riempie una riga del corpo su due con bodyAlternateBackground. Vedi righe alternate. |
bodyAlternateBackground | ColorValue | #f2f2f2 | Fondo delle righe alternate del corpo (disegnato solo se attivo). |
borders | boolean | true | Traccia i bordi delle celle. |
borderColor | ColorValue | colore del testo corrente | Colore del tratto dei bordi. |
borderWidth | Dimension | 0.75pt | Spessore del tratto dei bordi (≈1px a 96 DPI; scala con i DPI della pagina). Non usato da 'booktabs', che ha spessori propri. |
cellPadding | Dimension | 0.375em | Spaziatura interna di ogni cella. |
rules | 'grid' | 'horizontal' | 'outer' | 'none' | 'booktabs' | 'grid' | Quali filetti tracciare quando borders è attivo: la griglia completa delle celle, solo i filetti orizzontali (bordo superiore e inferiore di ogni riga, nessun verticale), solo la cornice esterna, nessuno, oppure i tre filetti di una tabella da rivista scientifica (vedi filetti booktabs). |
borderRadius | Dimension | 0 | Raggio degli angoli della cornice esterna della tabella. La cornice viene tracciata arrotondata (con i filetti grid o outer), i fondi delle celle e il fondo dell’intestazione vengono ritagliati su di essa (anche con rules: 'none' o con i bordi disattivati) e i filetti orizzontali vengono accorciati sul suo contorno esterno; i filetti interni restano dritti. Una tabella divisa tra più pagine arrotonda gli angoli superiori della prima parte e quelli inferiori dell’ultima. Limitato a metà della larghezza e dell’altezza della tabella. I filetti 'booktabs' restano dritti (i fondi vengono comunque ritagliati). |
heavyRuleWidth | Dimension | 0.08em | Booktabs: i filetti sopra la tabella e sotto la sua ultima riga. Un em è il corpo delle celle del corpo. |
lightRuleWidth | Dimension | 0.05em | Booktabs: il filetto sotto le righe d’intestazione e i filetti di gruppo. |
spanRuleWidth | Dimension | 0.03em | Booktabs: i filetti sotto le celle d’intestazione che coprono più colonne. |
spanRules | 'trimmed' | 'full' | 'none' | 'trimmed' | Booktabs: i filetti sotto le celle d’intestazione che coprono più colonne, sopra l’ultima riga d’intestazione: accorciati alle due estremità di spanRuleTrim, estesi a tutta la cella, oppure assenti. |
spanRuleTrim | Dimension | 0.5em | Booktabs: di quanto viene accorciato a ciascuna estremità un filetto di raggruppamento accorciato. |
groupRules | boolean | false | Booktabs: un filetto sottile sopra ogni riga del corpo che apre un gruppo. |
continuedFootRule | 'bottom' | 'light' | 'none' | 'light' | Booktabs: che cosa chiude una parte di una tabella divisa che prosegue a pagina seguente. |
overflow | 'split' | 'clip' | 'hide' | 'split' | Che cosa succede a una tabella più alta della pagina: prosegue nelle pagine successive, tiene solo le righe che entrano, oppure viene omessa. Con splitInline, vale anche per una tabella composta nel testo che non entra nel resto della sua colonna. Vedi più avanti. |
splitInline | boolean | true | Applica overflow anche alle tabelle collocate here: una tabella nel testo che non entra nello spazio rimasto nella sua colonna viene tagliata tra due righe e prosegue in testa alla colonna successiva. false sposta una tabella del genere intera alla colonna successiva, come fino a postext 1.24; le configurazioni salvate da versioni precedenti i cui capitoli incorporano una risorsa vengono lette con false. Vedi Tabelle più alte della pagina. Da postext 1.25. |
continuedSuffix | string | '(cont.)' | Aggiunto, in corsivo, alla didascalia di ogni parte successiva di una tabella divisa, dopo uno spazio; un suffisso che comincia con un carattere cinese o a larghezza piena ('(续)') viene composto attaccato alla didascalia. |
continuesMarkerEnabled | boolean | true | Compone un indicatore sotto ogni parte che prosegue nella pagina successiva. |
continuesMarker | string | 'Continued' / 'Continúa' | Testo di quell’indicatore, allineato a destra sotto la parte con il carattere delle note (vedi stile delle didascalie). Il valore predefinito segue la lingua del documento (vedi Lingua del documento per le otto lingue). |
Gli spessori dei bordi restano frazionari: un filetto di 0.5pt viene tracciato come filetto sottilissimo nel PDF e sullo schermo invece di essere arrotondato a un pixel intero (il minimo è 0,25 px).
#Righe alternate
Le tabelle di dati lunghe si seguono meglio in orizzontale quando una riga su due è colorata. bodyAlternateBackgroundEnabled attiva le strisce e bodyAlternateBackground ne imposta il colore:
const config: PostextConfig = {
tableStyle: {
bodyBackgroundEnabled: true,
bodyBackground: { hex: '#ffffff', model: 'hex' },
bodyAlternateBackgroundEnabled: true,
bodyAlternateBackground: { hex: '#eef3fa', model: 'hex' },
},
};Le righe si contano dalla prima riga dopo le righe d’intestazione (TableModel.headerRowCount, o le righe iniziali fatte di celle d’intestazione): quella riga mantiene bodyBackground (o nessun fondo finché bodyBackgroundEnabled è disattivato), la successiva prende il fondo alternato, e così via. Il conteggio segue il modello della tabella, non la pagina, così una tabella divisa tra più pagine mantiene la striscia di ogni riga su ogni pagina, e una cella unita su più righe prende la striscia della sua prima riga. Le celle d’intestazione mantengono il fondo dell’intestazione, il background proprio di una cella prevale su entrambi, e un colore collegato alla tavolozza segue la tavolozza. Uno stile di tabella con nome imposta i due campi come qualsiasi altro, così uno stile può essere a strisce mentre le altre tabelle del documento no; nella Sandbox sono l’interruttore Righe alternate e il suo colore, sotto Celle del corpo.
Nel VDT le celle delle righe alternate portano alternate: true, e l’impaginazione della tabella porta bodyAlternateBackground. tableCellFill(table, cell) restituisce il fondo con cui viene disegnata una cella (il suo, quello dell’intestazione, quello alternato o quello del corpo), ed è ciò che disegnano tutti i backend canvas, HTML e PDF. I fondi vicini si incontrano senza giunture: un browser con un rapporto di pixel frazionario o un visualizzatore PDF applica l’antialiasing a ogni fondo separatamente e lascerebbe trasparire la pagina in un filo sottilissimo tra due celle, per questo i backend HTML e PDF disegnano tableCellFillRects(table) (il fondo di ogni cella con una striscia su ogni lato che condivide con una cella disegnata dopo, che quella cella poi copre) e il canvas allinea i suoi fondi ai pixel del dispositivo.
#Filetti booktabs
Le tabelle delle riviste scientifiche e dei manuali si compongono di solito con tre filetti e nessuna linea verticale: un filetto spesso sopra la tabella, uno sottile sotto l’intestazione e uno spesso sotto l’ultima riga, con filetti corti sotto le intestazioni che raggruppano più colonne (il pacchetto LaTeX booktabs: \toprule, \midrule, \cmidrule, \bottomrule). rules: 'booktabs' traccia questo schema:
const config: PostextConfig = {
tableStyle: {
rules: 'booktabs',
borderColor: { hex: '#000000', model: 'hex' },
headerBackgroundEnabled: false,
},
};- Il filetto sopra la tabella e quello sotto la sua ultima riga sono spessi
heavyRuleWidth(0.08em), il filetto sotto le righe d’intestazionelightRuleWidth(0.05em). Una tabella senza righe d’intestazione non ha il filetto d’intestazione. - Una cella d’intestazione che copre più colonne sopra l’ultima riga d’intestazione riceve sotto di sé un filetto spesso
spanRuleWidth(0.03em). ConspanRules: 'trimmed'(il valore predefinito) il filetto viene accorciato dispanRuleTrim(0.5em) alle due estremità, così i filetti sotto due intestazioni vicine non si toccano;'full'lo estende a tutta la cella e'none'lo omette. groupRules: trueaggiunge un filetto sottile sopra ogni riga del corpo che apre un gruppo (una cella unica su tutta la tabella, o una riga di celle d’intestazione), a meno che la riga non apra la tabella o una pagina, dove c’è già il filetto d’intestazione.- Gli spessori si risolvono rispetto al corpo delle celle del corpo (
bodyFontSize), così un corpo d’intestazione più grande non ingrossa il filetto d’intestazione. Uno spessore di0elimina quel filetto. - I filetti prendono
borderColor(un colore collegato alla tavolozza segue la tavolozza e:::part palette), eborders: falseli disattiva.borderWidthnon si applica, e nemmenoborderRadius: i filetti restano dritti, mentre i fondi delle celle vengono comunque ritagliati sulla cornice arrotondata. I fondi dell’intestazione, le righe alternate e ilbackgroundproprio di una cella funzionano come con gli altri schemi, sotto i filetti. - Una tabella divisa tra più pagine ripete le righe d’intestazione su ogni parte, così ogni parte si apre con il filetto spesso e il filetto d’intestazione. Il filetto spesso sotto l’ultima riga chiude solo l’ultima parte; una parte che prosegue a pagina seguente termina con
continuedFootRule: un filetto sottile ('light', il valore predefinito), il filetto spesso ('bottom') o niente ('none').
L’impaginazione calcola i filetti una volta sola. La tabella del VDT li porta come strokes ({ x1, y1, x2, y2, widthPx }, relativi all’angolo superiore sinistro del corpo della tabella), e il canvas, il visualizzatore HTML, il PDF e l’EPUB a layout fisso tracciano esattamente quelli; in un PDF con tag sono artefatti di impaginazione. L’EPUB a layout fluido li scrive come bordi CSS sulla tabella e sulla sua intestazione, con i filetti accorciati disegnati come linee di sfondo. Nella Sandbox, Booktabs nel menu Filetti mostra questi campi e nasconde Spessore del bordo e Raggio degli angoli.
#Stili di tabella con nome
Raramente un documento compone tutte le tabelle allo stesso modo: una lista di controllo in una griglia blu notte con la cornice arrotondata, una riga di opzioni racchiusa solo dalla cornice esterna, una tabella di dati con semplici filetti orizzontali. tableStyles dichiara varianti con nome, e una risorsa tabella ne sceglie una con table.styleId. Ogni campo che uno stile non imposta viene letto prima da tableStyle e poi dal testo corrente, così uno stile dichiara solo ciò che distingue le sue tabelle. Una tabella senza styleId, o con un id che nessuno stile dichiara, mantiene tableStyle: un documento senza tableStyles viene composto esattamente come prima.
const config: PostextConfig = {
tableStyle: {
borderColor: { hex: '#163a76', model: 'hex' },
borderWidth: { value: 1.3, unit: 'pt' },
borderRadius: { value: 10, unit: 'pt' },
},
tableStyles: [
{
id: 'option',
name: 'Option row',
rules: 'outer',
borderColor: { hex: '#7a9cc6', model: 'hex' },
borderWidth: { value: 1, unit: 'pt' },
borderRadius: { value: 8, unit: 'pt' },
headerBackgroundEnabled: false,
},
],
};
// In the resources: this table is set in the "option" style.
const resource: Resource = {
id: 'choices', typeId: 'table', kind: 'table', createdAt: 0, updatedAt: 0,
table: { model: { rows: [/* … */] }, styleId: 'option' },
};Ogni voce accetta tutti i campi di tableStyle più id (ciò a cui fa riferimento table.styleId) e un name facoltativo per l’editor (per impostazione predefinita è l’id). Tutto ciò che uno stile può impostare si applica tabella per tabella: tipografia, fondi, bordi, filetti, raggio degli angoli, spaziatura interna e il comportamento in caso di sbordamento con le sue stringhe di continuazione. resolveTableStylesConfig(styles, tableStyle, resolvedBodyText, locale?) restituisce l’elenco risolto, pickTableStyle(resolved, styleId) lo stile con cui viene composta una tabella, e stripTableStylesDefaults elimina i campi non impostati (mantiene un campo uguale al suo predefinito integrato, che continua a sostituire un valore diverso di tableStyle). In un EPUB a layout fluido uno stile con nome è una classe sulla tabella (pt-table-<id>), a cui dà stile il foglio di stile del libro.
#Tabelle più alte della pagina
Una tabella flottante che non entra nella pagina nuova che le viene offerta non viene schiacciata né lasciata sbordare: con overflow: 'split' (il valore predefinito) il motore la taglia tra due righe all’ultimo bordo che entra nella pagina e la prosegue nelle pagine successive, quante ne servono. Ogni parte successiva ripete le righe d’intestazione della tabella (TableModel.headerRowCount, o le righe iniziali fatte di celle d’intestazione quando non è impostato) e riporta la didascalia con continuedSuffix dopo la descrizione: «Tabella 6-4. Titolo (cont.)». Ogni parte che prosegue riceve sotto di sé continuesMarker, allineato a destra, con il carattere delle note; la nota della tabella viene tenuta per l’ultima parte. Un taglio non attraversa mai una cella unita (un rowspan passa intero alla parte successiva), e una riga che fa da intestazione alle righe sottostanti (una cella unica su tutta la tabella) viene portata alla parte successiva invece di restare isolata al piede di una pagina. Una tabella booktabs chiude ogni parte che prosegue con continuedFootRule (vedi filetti booktabs).
Dove finisce la prima parte. Una tabella a cui viene offerta la testa di una colonna vuota dopo il suo rimando prende le righe che entrano lì e prosegue nello spazio successivo. Riempie la colonna fino al piede quando ha la colonna tutta per sé: una parte che lascerebbe sotto di sé meno di tre righe di testo prende anche quelle, piuttosto che lasciare un moncone di testo. Quando la colonna contiene già un’altra fascia di elementi flottanti (per esempio una figura a tutta larghezza in testa alla pagina), la parte si ferma invece almeno tre righe di testo prima del piede, lo spazio per il testo che ogni elemento flottante lascia quando condivide una colonna con un altro, così la colonna termina con un po’ di testo sotto la tabella invece che con soli elementi flottanti. Perché una tabella lunga arrivi fino al piede della sua colonna, citala dove la pagina in cui comincia non porta altri elementi flottanti (dopo la pagina di una figura a tutta larghezza, per esempio), oppure adatta le sue righe alla colonna.
'clip' tiene le righe iniziali che entrano nella pagina e scarta il resto in silenzio (la nota chiude comunque la parte); 'hide' omette del tutto la tabella. Entrambi si applicano solo quando la tabella è più alta di una pagina: una tabella che entra viene collocata intera in qualsiasi modalità.
Tabelle composte nel testo. Una tabella collocata here (incorporata con ::resource) segue le stesse regole da postext 1.25 (splitInline, attivo per impostazione predefinita). Quando non entra nello spazio rimasto nella sua colonna, viene tagliata tra due righe: la prima parte mantiene sopra di sé lo spazio degli elementi flottanti e almeno le righe d’intestazione e due righe del corpo (se sono meno, l’intera tabella comincia nella colonna successiva, come prima), ogni parte successiva apre la colonna seguente senza spazio sopra, impaginata alla larghezza di quella colonna (un impaginato a colonna e mezza compone una parte nella colonna stretta alla sua larghezza), e il testo dopo la riga ::resource segue l’ultima parte. La ripetizione dell’intestazione, la didascalia con il suffisso, l’indicatore, la nota sull’ultima parte, la coda di tre righe e i tagli che rispettano le celle unite e le intestazioni di gruppo sono quelli di una tabella flottante. Una tabella con meno di cinque righe del corpo non viene mai tagliata. 'clip' tiene le righe iniziali di una tabella nel testo più alta di una colonna, composte in testa a una colonna; 'hide' omette una tabella del genere; una più bassa passa intera alla colonna successiva in entrambe le modalità. Una pagina aperta da una tabella nel testo tiene libero dagli elementi flottanti solo lo spazio della prima parte, così una figura a tutta larghezza in attesa apre quella pagina e la tabella prosegue sotto. Le tabelle nel testo su una pagina verticale, e le tabelle dentro un riquadro, non vengono tagliate. splitInline: false sposta una tabella nel testo intera alla colonna successiva, come fino a postext 1.24.
Le stringhe di continuazione hanno valori predefiniti per lingua del documento (locale, altrimenti la lingua di sillabazione): inglese (cont.) / Continued, spagnolo (cont.) / Continúa, e analogamente in francese, tedesco, italiano, portoghese, catalano e olandese (elencate in Lingua del documento).
Il contenuto delle celle è Markdown in linea, e un a capo dentro una cella (un carattere di nuova riga, o \\ come nelle didascalie e nelle note) comincia un nuovo paragrafo. Un paragrafo che si apre con un punto elenco o un trattino (•, -, *, –) o con un numero (1., 1)) seguito da uno spazio viene composto come voce di elenco: il segno viene disegnato come è scritto, il testo rientra rispetto al segno di unorderedLists.gap del documento, le righe a capo si allineano al testo, e due spazi iniziali annidano un livello. Così una cella scritta come • Ofrece elección\n• Acomoda a personas diestras y zurdas diventa un elenco di due voci. Una riga fatta solo di spazi normali non aggiunge nulla; una riga che contiene uno spazio indivisibile (U+00A0) è una riga della cella, come in CommonMark, quindi 1\n seguito da uno spazio indivisibile rende la riga alta due righe di testo. Uno spazio indivisibile alla fine del testo di una cella mantiene la sua larghezza: 760 e uno spazio indivisibile, allineati a destra sopra (231), terminano uno spazio prima del bordo, il che avvicina lo 0 all’1. Le cifre si allineano esattamente solo dove lo spazio è largo quanto la parentesi, e nella maggior parte dei caratteri è più stretto. (Fino a postext 1.4 venivano eliminati entrambi.)
Le larghezze delle colonne appartengono al modello della tabella, non allo stile: TableModel.columnWidths è un array facoltativo di pesi relativi, uno per colonna, normalizzati al momento dell’impaginazione; [2, 1, 1] dà alla prima colonna metà della larghezza. Un array mancante, di lunghezza sbagliata o con un peso non positivo ripiega su una divisione in parti uguali. L’editor di tabelle mantiene l’array allineato quando si aggiungono o si rimuovono colonne.
#Costruire i modelli di tabella
Un TableModel è una griglia ordinata per righe, e ogni cella viene impaginata in base alla sua posizione: rows[r][c] sta nella colonna c. Una cella unita mantiene quindi nella griglia le celle che copre, ciascuna marcata hiddenBy con la sua cella principale, a differenza di una tabella HTML, che le omette. Le funzioni di supporto per il modello esportate da postext mantengono questa forma; sono funzioni pure che restituiscono un nuovo modello: mergeCells(model, { start, end }) e unmergeCell(model, at), addRow, addColumn, removeRow, removeColumn, setCellContent, setCellImage, setCellBackground e setAlignment. Le quattro funzioni per righe e colonne mantengono intere le unioni: una riga o una colonna aggiunta dentro un blocco unito lo allarga, una aggiunta prima lo sposta, una rimossa da esso lo restringe (un blocco che perde la sua prima riga o colonna mantiene il contenuto nella sua nuova cella in alto a sinistra) e ogni hiddenBy continua a puntare alla sua cella principale.
parseTSV(text, options?) costruisce un modello da un testo separato da tabulazioni, come un intervallo incollato da un foglio di calcolo: le righe si separano agli a capo, le celle alle tabulazioni, e le righe corte vengono completate perché la griglia sia rettangolare. headerRows trasforma le righe iniziali in righe d’intestazione: le loro celle ricevono isHeader e il modello headerRowCount, così una tabella divisa tra più pagine le ripete.
import { parseTSV, mergeCells } from 'postext';
let model = parseTSV('Part\tQty\tNote\nBolt\t4\tM6\nNut\t8\t', { headerRows: 1 });
// model.headerRowCount === 1; model.rows[0][0] is { content: 'Part', isHeader: true }
model = mergeCells(model, { start: { row: 2, col: 1 }, end: { row: 2, col: 2 } });
// rows[2][1] gets colSpan: 2; rows[2][2] stays in the grid with hiddenBy: { row: 2, col: 1 }tableGridIssues(model) controlla la griglia. Restituisce un elenco vuoto per un modello corretto, e altrimenti ogni punto in cui la griglia si rompe, in ordine di riga: spanOverlap, una cella visibile sotto il colSpan / rowSpan di un’altra cella (coveredBy indica quella cella), che è ciò che produce una cella coperta omessa alla maniera dell’HTML, perché ogni cella successiva slitta sull’unione; e missingCells, una riga che termina prima dell’ultima colonna senza un’unione che copra il resto, il che lascia un buco.
import { tableGridIssues } from 'postext';
tableGridIssues({
rows: [
[{ content: 'A', colSpan: 2 }, { content: 'C' }],
[{ content: '1' }, { content: '2' }, { content: '3' }],
],
});
// => [{ kind: 'spanOverlap', row: 0, col: 1, coveredBy: { row: 0, col: 0 } },
// { kind: 'missingCells', row: 0, col: 2 }]Una tabella usata dal documento la cui griglia presenta problemi del genere viene segnalata in doc.contentWarnings come raggedTableGrid (vedi Avvisi nel documento).
#Stile delle didascalie
La proprietà captionStyle controlla le didascalie delle risorse (la riga Figure 1 — … sotto, o sopra, immagini, SVG e tabelle). L’etichetta numerata e la descrizione condividono lo stesso carattere e lo stesso corpo (un vincolo del motore), ma l’etichetta può avere peso, corsivo e colore propri. Famiglia, corpo e colore del carattere ereditano quelli del testo corrente quando non sono impostati. La didascalia può stare sopra la risorsa (la convenzione abituale per le tabelle) ed essere composta su una barra colorata larga quanto il blocco; una nota facoltativa più piccola (riga della fonte, crediti: Resource.note) ha il suo stile nel sotto-oggetto note. Un tipo di risorsa può sostituire uno qualsiasi di questi campi per le proprie risorse tramite ResourceType.captionStyle (vedi Tipi di risorsa).
const config: PostextConfig = {
captionStyle: {
align: 'center',
labelBold: true,
labelColor: { hex: '#295AA3', model: 'hex' },
descriptionItalic: true,
position: 'above',
backgroundEnabled: true,
padding: { value: 0.35, unit: 'em' },
note: { italic: true, align: 'left' },
},
};| Proprietà | Tipo | Predefinito | Descrizione |
|---|---|---|---|
fontFamily | string | font del testo corrente | Famiglia di caratteri della didascalia (etichetta e descrizione). |
fontSize | Dimension | corpo del testo corrente | Corpo del carattere della didascalia (etichetta e descrizione). |
color | ColorValue | colore del testo corrente | Colore del testo della descrizione. |
align | 'left' | 'center' | 'right' | 'justify' | 'start' | 'end' | 'left' | Allineamento orizzontale delle righe della didascalia. 'justify' allarga ogni riga tranne l’ultima a tutta la larghezza. Su una barra di didascalia le righe si allineano dentro la sua spaziatura interna; una didascalia laterale si allinea entro la propria larghezza. |
gap | Dimension | 0.75em | Spazio verticale tra la risorsa e la sua didascalia. |
labelBold | boolean | true | Compone l’etichetta numerata (ad esempio Figure 1) in grassetto. |
labelItalic | boolean | false | Compone l’etichetta numerata in corsivo. |
labelColor | ColorValue | color della didascalia | Colore dell’etichetta numerata. |
descriptionItalic | boolean | false | Compone il testo della descrizione in corsivo. |
position | 'above' | 'below' | 'below' | Dove si trova la didascalia. Con 'above' la didascalia (e la sua barra) viene prima e il corpo della risorsa scende dell’altezza della didascalia più gap; la nota va allora sotto il corpo. |
backgroundEnabled | boolean | false | Disegna una barra dietro la didascalia. La barra è larga quanto il blocco e racchiude le righe della didascalia più padding su ogni lato. |
background | ColorValue | colore principale della tavolozza | Colore di riempimento della barra (disegnata solo se attiva). |
padding | Dimension | 0.35em | Spaziatura interna tra il bordo della barra e il testo della didascalia. Ignorata quando la barra è disattivata. |
note | object | — | Stile della nota della risorsa: vedi la sotto-tabella più avanti. |
labelNumberGap | string | spazio indivisibile; '' in un documento giapponese | Che cosa sta tra l’etichetta e il numero, nella didascalia e in un :ref in linea: Figura 1.7, Fig. 1.7. Cinese e giapponese li compongono attaccati: '' dà 图1-1, ed è il valore predefinito in un documento giapponese (図1-1). |
labelSeparator | string | '. '; ' ' in un documento giapponese | Che cosa segue il numero, prima della descrizione: Figura 1.7. Una didascalia. Le didascalie cinesi prendono uno spazio ideografico, ' ' (图1-1 标题), e così quelle giapponesi, per impostazione predefinita (図1-1 東京の地図, JLReq §4.3). Un’etichetta senza numero segue la propria regola: un punto, a meno che il prefisso non termini già con uno. |
Il sotto-oggetto note dà stile a Resource.note, un breve testo (fonte, crediti, un’osservazione) composto sotto la risorsa in un corpo più piccolo. Accetta la stessa formattazione in linea e gli stessi segni :ref della didascalia ed eredita il carattere della didascalia. Viene collocato sotto la didascalia quando la didascalia è sotto, e sotto il corpo della risorsa quando la didascalia è sopra; la sua altezza conta nel blocco, così una risorsa con una nota flotta come un’unità sola.
| Proprietà | Tipo | Predefinito | Descrizione |
|---|---|---|---|
note.fontSize | Dimension | 0,85 × corpo della didascalia | Corpo del carattere della nota. |
note.color | ColorValue | color della didascalia | Colore del testo della nota. |
note.italic | boolean | false | Compone la nota in corsivo. |
note.gap | Dimension | 0.35em | Spazio tra la nota e ciò che la precede (didascalia o corpo). |
note.align | 'left' | 'center' | 'right' | 'justify' | 'start' | 'end' | 'left' | Allineamento orizzontale delle righe della nota, come align per la didascalia. |
Le sostituzioni per tipo si fondono con mergeCaptionStyle(resolvedCaptionStyle, override, palette?), esportata per gli host che hanno bisogno della stessa risoluzione fuori dalla pipeline.
#Stile dei diagrammi
La proprietà diagramStyle controlla come vengono colorati i diagrammi SVG incorporati (risorse kind: 'svg') e in quali font è composto il loro testo. La modalità a inchiostro unico è un passaggio di ricolorazione che trasforma ogni colore di un diagramma in una tinta di un solo inchiostro, così le figure vengono riprodotte fedelmente quando il documento è stampato con un unico colore spot. I font incorporati inseriscono in ogni SVG i caratteri che il suo testo nomina, così le sue etichette sono composte nei font del documento sul canvas, in HTML e in EPUB (vedi Font nel testo SVG).
const config: PostextConfig = {
diagramStyle: {
singleInk: true,
inkColor: { hex: '#295AA3', model: 'hex' },
},
};| Proprietà | Tipo | Predefinito | Descrizione |
|---|---|---|---|
singleInk | boolean | false | Ricolora ogni diagramma SVG incorporato in tinte di un solo inchiostro. |
inkColor | ColorValue | Colore principale (#295AA3) | L’inchiostro. Il valore predefinito è il colore principale della palette del documento (collegato alla palette tramite paletteId: 'main-color'), quindi cambiando il campione di colore della palette si ritingono i diagrammi insieme ai titoli e ai tratti in grassetto. |
inlineFonts | boolean | true | Incorpora in ogni SVG i caratteri che il suo testo nomina (font-family) come data URI @font-face prima che venga mostrato come immagine: sul canvas, in HTML, in EPUB e nel ripiego raster del PDF. Non viene mai scritto nel file salvato. Una risorsa vi rinuncia con svg.inlineFonts: false (da postext 1.25). |
#Come funziona l’inchiostro unico
Quando singleInk è attivo, ogni colore nel markup SVG viene riscritto come una tinta di inkColor la cui intensità è 1 − luminanza relativa (coefficienti Rec. 709 applicati ai canali con codifica gamma: un’approssimazione percettiva più che sufficiente per la mappatura delle tinte). La mappatura conserva il valore percepito: il bianco diventa il bianco della carta, il nero diventa l’inchiostro pieno, e i riempimenti chiari restano chiari qualunque sia la loro tonalità originale. Uno sfondo giallo pallido diventa una tinta pallida dell’inchiostro; un tratto scuro si avvicina all’inchiostro pieno.
La ricolorazione viene eseguita dalla funzione esportata applySingleInkToSvg(svgText, inkHex), che lavora senza DOM sul markup SVG come testo:
- I valori esadecimali
#rgb/#rgba/#rrggbb/#rrggbbaa, le funzionirgb()/rgba()e le funzionihsl()/hsla()vengono riscritti ovunque compaiano: attributi di presentazione,stylein linea, gradienti,<defs>. Le funzioni possono usare canali interi, decimali o in percentuale e la sintassi con virgole o con spazi, quindi vengono ricolorati anchergb(11.37%, 20%, 50.59%)(come lo scrive Cairo) ergb(51 102 153 / 50%). Una funzione tinta viene riscritta comergb(…)orgba(…). - Le parole chiave
whiteeblackvengono sostituite solo dove compaiono come valori di colorazione (fill,stroke,stop-color,flood-color,color, come attributi o proprietà di stile in linea), mai dentro il contenuto testuale o le etichette. none,transparentecurrentColorrestano invariati, come pure gli altri colori con nome (red,steelblue…) e il nero predefinito di una forma o di un testo che non imposta alcun riempimento. Assegna a questi elementi un colore esplicito perché vengano ricolorati.- I canali alfa vengono conservati (i nibble di
#rgba/#rrggbbaae le componenti alfa dirgba(…)passano invariati; un’alfa in percentuale viene scritta come numero). - Quando
inkHexnon si può interpretare, l’input viene restituito invariato. - Il risultato porta
data-postext-single-ink="#…"(l’inchiostro) sul suo<svg>radice, e un markup che lo porta già viene restituito così com’è, qualunque inchiostro indichi. La mappatura non è idempotente (un secondo passaggio schiarisce ogni colore, e il nero esce a circa due terzi dell’inchiostro), quindi un’immagine viene ricolorata una sola volta, da chiunque arrivi prima tra il tuo codice e i backend. (Il marcatore è nuovo in postext 1.5; il markup ricolorato dalla 1.4 non lo ha.)
import { applySingleInkToSvg } from 'postext';
const recoloured = applySingleInkToSvg(svgText, '#295AA3');
applySingleInkToSvg(recoloured, '#295AA3') === recoloured; // true: never twiceL’inchiostro unico si applica in tutti e tre i backend: il backend PDF ricolora i byte SVG che resourceBytes gli passa prima di disegnarli come vettori, e i backend canvas e HTML tingono le immagini SVG che dipingono quando lo chiedi (vedi Inchiostro unico sul canvas e in HTML), così il PDF esportato corrisponde all’anteprima a schermo.
Resolver e stripper seguono le altre sezioni, accanto ai tipi DiagramStyleConfig / ResolvedDiagramStyleConfig:
import {
DEFAULT_DIAGRAM_STYLE_CONFIG,
resolveDiagramStyleConfig,
stripDiagramStyleDefaults,
applySingleInkToSvg,
} from 'postext';
import type { DiagramStyleConfig, ResolvedDiagramStyleConfig } from 'postext';
const resolved = resolveDiagramStyleConfig(config.diagramStyle);
// => { singleInk: false, inkColor: { hex: '#295AA3', model: 'hex', paletteId: 'main-color' } }
const minimal = stripDiagramStyleDefaults(config.diagramStyle);
// => undefined when everything matches the defaults#Inchiostro unico sul canvas e in HTML
I backend canvas e HTML ricevono le immagini già decodificate (registerResourceImage) o come URL (resourceImageUrl), non come markup SVG. Su richiesta applicano la stessa mappatura a ciò che disegnano:
- Canvas (
renderPage,renderPageToCanvas,renderToCanvas). Ogni immagine SVG a cui si applica la tinta (una figura, l’immagine di una cella di tabella, un’immagine di progetto grafico, l’icona o il marcatore di un riquadro) viene rasterizzata alla dimensione con cui è collocata e i suoi pixel vengono tinti con l’inchiostro, sia che sia stata registrata come<img>sia comeImageBitmap. La bitmap tinta finisce in cache come qualsiasi raster vettoriale. Un’immagine bitmap non viene mai tinta. - HTML (
renderToHtml,renderToHtmlIndexed). Ogni<img>SVG ricevefilter: url(#pt-ink-…), che punta a unfeColorMatrixcontenuto nella sua pagina: un<svg>di dimensione zero che contiene il<filter>, collocato per primo nella pagina, e parte deldecorationHtmldella pagina nell’output indicizzato. Ogni pagina lo porta mentre l’inchiostro unico è attivo, che contenga o no un’immagine, così un host che aggiorna i blocchi uno per uno non introduce mai un’immagine a cui manca il filtro.
Mai tinta due volte. Fino a postext 1.4 i backend canvas e HTML dipingevano le immagini così come le ricevevano, quindi gli host ricoloravano da sé il markup con applySingleInkToSvg prima di passarlo. Gli adattatori dei pacchetti e la Sandbox lo fanno ancora, perché il passaggio sul markup dà esattamente i colori del PDF (vedi l’ultimo paragrafo qui sotto). Un’immagine viene quindi tinta una volta sola, secondo tre regole valide in tutti e tre i backend:
- Il markup marcato non viene toccato. Il backend PDF ricolora
resourceBytesconapplySingleInkToSvg, quindi i byte SVG già ricolorati vengono disegnati così come sono. Sul canvas e in HTML, nemmeno un’immagine caricata da un data URI SVG il cui markup porta il marcatore viene mai tinta. - Disattivato salvo richiesta, in postext 1.x. Il canvas tinge un’immagine SVG registrata senza un proprio flag solo quando il rendering passa
singleInk: true(RenderPageOptions), e una registrata conregisterResourceImage(id, img, { singleInk: true })in qualsiasi rendering. Il backend HTML tinge quandorenderToHtmlricevesingleInk: true, o quando il suo resolverresourceImageUrlportasingleInk: true. Un host scritto per la 1.4, che ricolora il markup e registra l’immagine decodificata senza flag, mantiene il suo output. La prossima versione major tingerà per impostazione predefinita. singleInk: falsenon viene mai tinto. Dietro un URL blob o di rete il markup non si può rileggere, quindi un’immagine che hai ricolorato da te e decodifichi in quel modo va registrata consingleInk: false, come fannoregisterBundleImagese la Sandbox.bundleImageUrl(bundle)restituisce un resolver che portasingleInk: false, ebundleResourceBytespassa al PDF i byte originali del pacchetto, che il backend PDF ricolora una volta.
Entrambi i backend conoscono il tipo di ogni immagine dal VDT: una figura e un’immagine di cella portano il tipo della loro risorsa, e un blocco immagine di progetto grafico porta imageKind ('svg' o 'bitmap'), preso dalla sua risorsa durante l’impaginazione. Per un VDT costruito prima che esistesse imageKind, il canvas tratta come SVG un’immagine di progetto grafico registrata come sorgente vettoriale, e il backend HTML una il cui URL è un data URI SVG o termina in .svg.
O ricolori da te il markup o lasci che i backend tingano l’immagine grezza, non entrambe le cose:
import { applySingleInkToSvg, registerResourceImage, renderPage, renderToHtml } from 'postext';
// Raw SVG: tinted while diagramStyle.singleInk is on…
registerResourceImage('diagram.svg', rawImg, { singleInk: true });
// …or register it plainly and ask on each render.
registerResourceImage('diagram.svg', rawImg);
const canvas = renderPage(doc.pages[0], doc, { singleInk: true });
// Recoloured before decoding (as postext 1.4 hosts do): painted as given.
const inked = applySingleInkToSvg(svgText, ink);
registerResourceImage('diagram.svg', await decode(inked), { singleInk: false });
// The HTML backend with URLs to the raw markup.
const html = renderToHtml(doc, { resourceImageUrl: urlFor, singleInk: true });renderToHtml prende il valore predefinito di singleInk dal flag del resolver, quindi bundleImageUrl(bundle) non ha bisogno di opzioni.
Per ogni colore che il passaggio sul markup riscrive (valori esadecimali, rgb() e hsl(), white e black; vedi Come funziona l’inchiostro unico), la mappatura sui pixel dà lo stesso risultato, bordi antialiasing e gradienti compresi. I due differiscono dove il passaggio sul markup lascia stare un colore: i colori con nome diversi da white e black, currentColor, forme e testi senza riempimento (disegnati nel nero predefinito) e le bitmap incorporate nell’SVG vengono tinti a schermo ma mantengono il loro colore nel PDF. Assegna a ogni elemento di un diagramma un colore esplicito esadecimale, rgb() o hsl() per ottenere un output identico. Quando il canvas non può rileggere i pixel (un <img> di un’altra origine, caricato senza CORS), l’immagine viene dipinta senza tinta.
#Font nel testo SVG
Un’immagine SVG viene mostrata tramite un’immagine: un <img> sul canvas, in HTML e in EPUB. Un documento immagine non vede i web font della pagina, quindi <text font-family="IBM Plex Sans"> ripiegherebbe su un carattere di sistema. Da postext 1.25 il motore incorpora nel markup i caratteri che il testo nomina prima che l’immagine venga decodificata o fornita come URL: una regola @font-face per ogni file di font, con i suoi byte come data URI, in uno <style> subito dopo il tag <svg> radice. Il PDF non ha bisogno di nulla di tutto questo: compone il testo SVG come testo vero nei suoi font incorporati (vedi Byte delle risorse e master di stampa).
Ciò che il testo richiede viene letto da font-family, font-weight, font-style e font, come attributi, negli attributi style ed ereditato dai gruppi che lo contengono; conta anche una regola <style> che nomina una famiglia. Le famiglie generiche (serif, sans-serif…) e il testo in <title> o <desc> vengono esclusi, e una famiglia che l’SVG dichiara da sé con @font-face non viene toccata. Un tratto di testo scorre il suo elenco font-family fino alla prima famiglia che ha un carattere disponibile. Prima viene la ricolorazione per l’inchiostro unico, poi i font.
I caratteri provengono da un provider con il contratto del PdfFontProvider di postext-pdf, così un solo provider serve entrambi: viene chiamato con la famiglia, il peso e lo stile, e i caratteri che l’SVG compone in quel carattere, e risponde con uno o più file. A una famiglia servita come porzioni per unicode-range (Fontsource, Google Fonts) risponde con le porzioni di cui quei caratteri hanno bisogno, così un SVG con etichette latine porta solo il file latin. Il provider predefinito legge il registro dei font del motore: loadBundleFonts vi registra i caratteri di un pacchetto, e un host registra i propri con registerFontBytes(family, weight, style, bytes, { unicodeRange }), o registerFontUrl(…) per un file scaricato la prima volta che un SVG ne ha bisogno. Una famiglia che nessuno ha registrato viene cercata nelle regole @font-face dei fogli di stile leggibili della pagina. Un FontFace aggiunto a document.fonts da byte non conserva i byte, quindi il motore non può rileggerlo: registra anche quei caratteri.
import { registerFontBytes, registerSvgImage, renderPage } from 'postext';
registerFontBytes('IBM Plex Sans', 700, 'normal', plexBoldWoff2);
await registerSvgImage('chart.svg', svgText); // recoloured, fonts inlined, decoded, registered
const canvas = renderPage(doc.pages[0], doc);Dove avviene:
- Canvas.
registerSvgImage(fileId, svgText, options)ricolora (inkHex), incorpora i font (fonts, un provider;inlineFonts: falsesalta il passaggio), decodifica e registra l’immagine come sorgente vettoriale, e restituisce che cosa è stato di ogni carattere.registerBundleImages(bundle)fa lo stesso per gli SVG di un pacchetto, cominciando dai caratteri del pacchetto stesso.prepareSvgMarkup(svgText, options)restituisce il markup preparato per un host che decodifica per conto proprio. - HTML.
bundleImageUrl(bundle)serve il markup SVG con i caratteri del pacchetto incorporati.renderToHtml(doc, { inlineSvgFonts: true })incorpora i font nei data URIdata:SVG cheresourceImageUrlrestituisce, a partire dai caratteri che il registro tiene in memoria (oppureinlineSvgFonts: { fonts, maxBytes, withhold }). Un object URL non si può leggere in modo sincrono, quindi un host che serve URL blob incorpora i font prima di crearli. - EPUB.
postext-epubincorpora i font daifontsdel libro, poi dasvgFonts.provider, prima di scrivere un SVG (vedi Libri EPUB). - PDF. Il testo SVG viene composto come testo vero nei font incorporati. Uno
<style>che contiene solo regole@font-face(caratteri incorporati dall’autore) non fa più ripiegare la figura su un raster. Quando una figura ripiega comunque (un filtro, un gradiente), il suo raster viene creato con i caratteri incorporati dalfontProviderdel PDF.
Sono esportate anche le funzioni di livello più basso: svgFontRequests(svgText) elenca per ogni tratto le famiglie, il peso, lo stile e i caratteri; inlineSvgFonts(svgText, provider, options) e inlineSvgFontsSync(svgText, syncProvider, options) restituiscono il markup; inlineSvgFontsDetailed aggiunge un resoconto di ogni carattere (inlined, declared, unavailable, withheld, tooLarge).
| Opzione | Tipo | Predefinito | Descrizione |
|---|---|---|---|
maxBytes | number | 2 MiB | Massimo di byte di font incorporati in un SVG (prima del base64, che aggiunge un terzo). I caratteri che insieme lo superano non vengono incorporati affatto, e viene segnalato svgFontsTooLarge. |
formats | ('woff2' | 'woff' | 'ttf' | 'otf')[] | tutti e quattro | I formati di file da incorporare; i file di altri formati vengono saltati. |
withhold | (family) => boolean | nessuno | Famiglie da escludere da un file che esce dall’app (non ridistribuibili). Il loro riferimento resta e il lettore ripiega; onWithheld(family) viene informato di ciascuna. |
onWarning | (warning) => void | nessuno | Viene informato di una famiglia senza caratteri (svgFontUnavailable) e del limite di dimensione (svgFontsTooLarge). |
Rinunciare. diagramStyle.inlineFonts: false lascia ogni SVG come è salvato; svg.inlineFonts: false su una risorsa la lascia identica byte per byte, per un SVG che porta i propri caratteri o che non deve cambiare. Un pacchetto scrive la rinuncia della risorsa come "inlineFonts": false nel suo preset.json.
Licenze. Incorporare i font mette file di font dentro immagini che possono uscire dall’app (un’esportazione HTML, un EPUB). Avviene quando un’immagine viene mostrata o esportata, mai nei byte salvati della risorsa, e withhold esclude le famiglie che una licenza non ti permette di cedere: chi scrive l’EPUB trattiene i caratteri marcati redistributable: false, e la Sandbox le famiglie personalizzate marcate allo stesso modo.
#Stile dei video
La proprietà videoStyle stabilisce come vengono stampate le risorse video (il simbolo di riproduzione e il codice QR sopra la locandina, e se la locandina rimanda al video) e che cosa offrono i loro player nel visualizzatore HTML e in EPUB.
const config: PostextConfig = {
videoStyle: {
playMark: { shape: 'rounded', position: 'top-left', size: { value: 10, unit: 'mm' } },
qr: { position: 'bottom-right', size: { value: 20, unit: 'mm' }, errorCorrection: 'Q' },
player: { download: false, privacy: true },
},
};| Proprietà | Tipo | Predefinito | Descrizione |
|---|---|---|---|
playMark | VideoPlayMarkConfig | vedi sotto | Il simbolo stampato sulla locandina per indicare che si può riprodurre. |
qr | VideoQrConfig | vedi sotto | Il codice QR stampato sulla locandina: apre la pagina YouTube o Vimeo del video, o l’indirizzo di produzione di un file. |
linkPoster | boolean | true | Fa della locandina un link al video: un’annotazione di link sopra di essa nel PDF, e un <a> che la racchiude in HTML e in EPUB ovunque la locandina venga mostrata. |
html | 'player' · 'poster' | 'player' | Che cosa l’output HTML compone per un video: il suo player, o la locandina stampata con le sue sovrapposizioni. |
player | VideoPlayerOptions | vedi sotto | Le opzioni del player di ogni video; il video.player proprio di un video vi si sovrappone. |
#Simbolo di riproduzione
| Proprietà | Tipo | Predefinito | Descrizione |
|---|---|---|---|
enabled | boolean | true | Stampa il simbolo. |
shape | 'circle' · 'rounded' · 'triangle' | 'circle' | Un disco con un triangolo, un rettangolo arrotondato con un triangolo (largo 1,45 volte la sua altezza), o il solo triangolo, contornato con il colore di sfondo. |
position | VideoOverlayPosition | 'center' | 'center', un angolo ('top-left', 'top-right', 'bottom-left', 'bottom-right') o il centro di un lato ('top', 'bottom', 'left', 'right'). Le posizioni sono fisiche: l’angolo in alto a destra è l’angolo in alto a destra anche in un libro da destra a sinistra. |
size | Dimension | 12mm | Altezza del simbolo; mai più del 40% del lato più corto della locandina. |
inset | Dimension | 4mm | Distanza dai bordi della locandina in un angolo o su un lato. |
color | ColorValue | bianco | Il triangolo. |
background | ColorValue | Colore principale | Il disco o il rettangolo dietro di esso; il contorno del triangolo quando è da solo. Collegato alla palette per impostazione predefinita. |
backgroundOpacity | number | 0.9 | Opacità dello sfondo, 0–1. |
#Codice QR
| Proprietà | Tipo | Predefinito | Descrizione |
|---|---|---|---|
enabled | boolean | true | Stampa il codice. Un file senza indirizzo di produzione non ne riceve alcuno. |
position | VideoOverlayPosition | 'bottom-right' | Come per il simbolo di riproduzione. Assegna ai due posizioni diverse. |
size | Dimension | 18mm | Lato del codice con la sua zona di rispetto; mai più del 45% del lato più corto della locandina. Le fotocamere dei telefoni leggono moduli da un terzo di millimetro in su: un indirizzo di 30 caratteri produce un codice di 29 moduli, quindi 18 mm con una zona di rispetto di 2 danno moduli di 0,55 mm. |
inset | Dimension | 3mm | Distanza dai bordi della locandina. |
errorCorrection | 'L' · 'M' · 'Q' · 'H' | 'M' | Quanta parte del codice può essere danneggiata o coperta restando leggibile: circa il 7%, 15%, 25% o 30%. Viene alzata automaticamente finché il codice mantiene lo stesso numero di moduli. |
quietZone | number | 2 | Moduli chiari attorno al codice, sulla sua piastrina (0–8). La piastrina si stacca dalla locandina, quindi i quattro moduli che lo standard richiede sulla carta libera non servono. |
color | ColorValue | nero | I moduli scuri. Tienili scuri su una piastrina chiara: la maggior parte dei lettori non legge i codici invertiti. |
background | ColorValue | bianco | La piastrina. |
radius | Dimension | 1mm | Raggio degli angoli della piastrina. |
Il codice viene codificato dal motore stesso (encodeQr(text, level): modalità byte, UTF-8, versioni da 1 a 40, la maschera con la penalità più bassa) e disegnato come vettori: il canvas riempie un unico tracciato di sequenze di moduli, il PDF un solo drawSvgPath, e l’HTML un solo <path> con shape-rendering="crispEdges", così resta nitido a qualunque dimensione di stampa.
#Opzioni del player
VideoPlayerOptions, in videoStyle.player e nel video.player di ogni video. Il player HTML5 di un file le rispetta tutte; i player di YouTube e Vimeo rispettano ciò che i loro parametri di incorporamento consentono.
| Proprietà | Predefinito | Rispettata da | Descrizione |
|---|---|---|---|
controls | true | YouTube, Vimeo, file | Mostra i controlli del player. |
download | true | file | Offre il pulsante di download del browser (controlslist="nodownload" quando è disattivato). Nasconde il pulsante; non protegge il file. YouTube e Vimeo non offrono mai il download. |
fullscreen | true | YouTube, Vimeo, file | Offre lo schermo intero (fs=0, l’allowfullscreen dell’iframe, nofullscreen). |
playbackRate | true | Vimeo, file | Offre il menu della velocità (speed=0, noplaybackrate). |
pictureInPicture | true | Vimeo, file | Offre il picture-in-picture (pip=0, disablepictureinpicture). |
remotePlayback | true | file | Offre la trasmissione a un altro schermo (disableremoteplayback). |
autoplay | false | YouTube, Vimeo, file | Avvia la riproduzione da sola, sempre senza audio, come richiedono i browser. |
muted | false | YouTube, Vimeo, file | Comincia con l’audio disattivato. |
loop | false | YouTube, Vimeo, file | Alla fine riprende dall’inizio. |
exclusive | true | Folio, visualizzatore HTML, EPUB (dove gli script vengono eseguiti); file | Avviare questo video mette in pausa gli altri visibili, così ne suona uno alla volta. false lo lascia suonare insieme agli altri: le brevi clip mute in loop di una pagina, più d’una in contemporanea. Da postext 1.18. |
preload | 'metadata' | file | Quanto carica il browser prima della riproduzione: 'none', 'metadata' o 'auto'. |
privacy | true | YouTube, Vimeo | Incorporamenti con privacy avanzata: YouTube da youtube-nocookie.com, Vimeo con dnt=1. |
Un EPUB conserva solo gli attributi che il suo schema conosce: un file si riproduce con controls, autoplay, muted, loop, playsinline e preload, più il marcatore data-pt-alongside, e il resto lo decide il sistema di lettura.
Un video non esclusivo porta data-pt-alongside nell’output HTML. coordinateVideoPlayback(root) fa rispettare la regola ai player sotto root (l’elemento che contiene l’output di renderToHtml): avviare un video esclusivo mette in pausa tutti gli altri in riproduzione, e avviarne uno che suona insieme agli altri mette in pausa solo quelli esclusivi. Restituisce una funzione che smette di ascoltare. playsAlongside(el) e videosToPause(started, videos, alongside) offrono la stessa regola a un host con player propri. In Folio un video che parte da solo e suona insieme agli altri (autoplay con exclusive: false) si avvia, senza audio, ogni volta che la sua pagina entra in vista e si ferma quando la pagina viene girata, più d’uno in contemporanea, e loop lo fa ripartire dall’inizio. Una pagina o un capitolo EPUB con video da coordinare (due o più, di cui uno esclusivo) collega la stessa regola come piccolo script, scripts/videos.js (l’esportazione VIDEO_PLAYBACK_SCRIPT), e il pacchetto dichiara quel documento scripted. Un sistema di lettura che esegue gli script fa rispettare la regola ai video di quel documento, ma non a quelli della pagina a fronte, che è un altro documento; uno che non esegue script riproduce ogni video secondo le sue regole, come fanno sempre i player di YouTube e Vimeo.
Resolver e stripper seguono le altre sezioni, accanto ai tipi VideoStyleConfig / ResolvedVideoStyleConfig:
import {
DEFAULT_VIDEO_STYLE_CONFIG,
DEFAULT_VIDEO_PLAYER_OPTIONS,
resolveVideoStyleConfig,
resolveVideoPlayerOptions,
stripVideoStyleDefaults,
} from 'postext';
const resolved = resolveVideoStyleConfig(config.videoStyle);
const player = resolveVideoPlayerOptions(resource.video?.player, resolved.player);
const minimal = stripVideoStyleDefaults(config.videoStyle); // undefined when all defaults