appcore-filemaker
Publié et vérifié au registre : 1.0.0-rc.2, 2026-10-17. Sélectionner =1.0.0-rc.2 explicitement. Les versions restent indépendantes ; ceci est une release candidate, sans certification de déploiement ou de matériel.
La RC exacte ci-dessus comprend les changements documentés de layout, diagnostic et Unicode PDF. Les anciennes notes d’audit et de publication pendant le développement ci-dessous conservent leur contexte historique. La migration des applications externes reste distincte. Le miroir public préserve les octets du registre ; son inventaire généré est validé séparément.
Pour découvrir le format, suivez le guide YAML pas à pas. Il part de l'en-tête minimal puis couvre données, styles, vecteurs, tables, rôles de page, images, collision, composants et preflight strict. Il se termine par la référence complète des champs et distingue les nœuds réservés non encore implémentés.
Le YAML strict filemaker: "1.0", les données typées et patches atomiques sont
compilés en IR. Polices et assets explicites sont mesurés avant layout
fixed-point, collision/reflow géométrique et construction de la
ResolvedScene immuable. Inspection, validation, preflight et export consomment
cette scène sans modifier la géométrie.
Canvas est un contrat de dessin sémantique, pas un tampon de pixels. Les
coordonnées acceptent pt, px, mm, cm, in, %, l'unité logique lu
et les valeurs norm/normalized bornées à 0..=1. Text, image, line, rect,
circle, ellipse, polygon, path et group restent des nœuds typés ; les paths
conservent move, line, courbe cubique et close. Circle exige des axes résolus
égaux. Safe area, presets, layers/z-index, transforms et collision sont des
entrées explicites et orthogonales.
Les couleurs restent indépendantes du format en RGB, RGBA, Gray ou CMYK en
millionièmes. YAML accepte noms stables, hex, notation fonctionnelle entière et
couleurs typées avec tag explicite ; fonds fill, bordures stroke et opacity
restent séparés. MemoryResolver et FileResolver à racine canonique
implémentent la résolution bornée des assets, templates et polices.
FontManager::register_from enregistre une police logique exacte sous la
limite d'octets de l'appelant sans jamais parcourir les polices de l'hôte.
L'ordre explicite de fallback fait partie du fingerprint. SVG et HTML
incorporent les familles réellement choisies dans les glyph runs résolus, y
compris les cellules de table.
La cascade complète est defaults → theme → template → style
component/nommé/inline développé → règles data conditionnelles ordonnées →
SetStyle runtime transactionnel → ExportStyleOverride. Le style runtime
change avant la mesure. La couche export expose seulement fill, stroke, opacity
et couleur texte ; elle ne peut invalider la géométrie résolue. Layer, z-index
et ordre source trient la peinture indépendamment de la collision géométrique.
Les métadonnées raster et SVG sont résolues avant export. contain et la
réduction scale_down conservent l'aspect en microunités fixed-point ; fill,
none intrinsèque, crop, cover focal et EXIF facultatif produisent des rectangles
source, destination et clip immuables. Preflight calcule le DPI raster effectif
après transform. SVG/HTML incorporent SVG ; PDF/raster signalent sa
rasterisation non prise en charge comme perte de fidélité explicite.
La politique de collision hérite dans l'ordre explicite document → page →
région → groupe → élément. Le YAML accepte collision: false, et le reflow
interroge le bound mesuré sélectionné : layout, visuel ou intrinsèque.
Les transforms fixed-point prennent en charge translation, rotation en degrés entiers, échelle, flip/mirror et origins explicites. Ils se composent dans les groupes ; PDF, SVG, PNG/JPEG et HTML consomment la même matrice résolue.
Les éléments texte déclarent le layout via text_options. L'overflow accepte
wrap, shrink, ellipsis, clip, expand et error, avec max_lines
borné, min_font_size absolu et line_height fixed-point. Mesure et expansion
précèdent la collision ; le clipping est une géométrie résolue ; SVG et HTML
rendent les runs façonnés/tronqués. writing_mode: vertical implémente des
colonnes de haut en bas progressant de droite à gauche en PDF, SVG, PNG/JPEG et
HTML. L'emoji couleur reste une perte explicite jusqu'à son implémentation.
La géométrie déclarative traverse aussi YAML et IR sans modification.
constraints porte minimum, préféré, maximum et ratio largeur/hauteur
fixed-point ; align_x et align_y choisissent début, centre ou fin dans le
conteneur actif. Les anchors ciblent les bords d'éléments antérieurs ou des
guides nommés avec guide:nom[+offset]. Coordonnées, plages et ratios
contradictoires échouent explicitement. Les patches move/resize remplacent
l'intention de position/taille antérieure.
Les conteneurs flow verticaux et horizontaux acceptent start, center,
end, space_between, space_around et space_evenly. Toute distribution
autre que start exige une taille primaire explicite, préférée ou dérivée du
ratio ; mesure auto ambiguë et overflow échouent avant la collision.
La planification des flux distribués compte les enfants visibles en deux passes
bornées, sans allouer de liste temporaire de références et en conservant le
même espacement.
Le fingerprint trie les références empruntées aux noms d'assets, évitant les
chaînes clonées pendant la résolution déterministe.
Les exclusions nommées au niveau racine définissent des rectangles relatifs à
la page, non peints et obligatoirement contenus dans la trim box. Elles se
répètent dans le budget géométrique global et initialisent l'index spatial de
chaque page physique avant le placement des éléments. Les champs optionnels
group et collides_with utilisent le même contrat de collision symétrique que
les éléments, tandis que la politique push/error/next-page/shrink du candidat
reste responsable du reflow borné. Inspection, masques de collision et requêtes
de régions libres conservent l'exclusion résolue ; les exporters ne reçoivent
aucun node à peindre.
Les pages document peuvent déclarer des layers master, first,
continuation et last, chacune divisée en bandes background, header et
footer sans collision. Les éléments master se répètent sur chaque page
physique ; une layer de rôle est choisie après la pagination bornée du corps ;
et le texte {page}/{pages} n'est résolu qu'une fois le total final connu.
Composants, styles, binding, patches, inspection et tous les exporters de scène
respectent le même contrat. Les éléments résolus conservent un flag
collidable afin que les overlays ne créent pas de fausses collisions, ne
consomment pas les régions libres et ne modifient pas la pagination.
La résolution des couches parcourt paresseusement les éléments actifs de chaque page physique ; la sélection par rôle n'alloue donc pas de liste temporaire de références.
Les streams Dataset redémarrables s'arrêtent à l'échantillon borné de colonne
auto sans parcourir le reste. Les tables résolvent largeurs fixed, auto
échantillonnées et flex pondérées ; paginent lignes fixes ou mesurées par
callback avec capacité correcte du header initial/répété ; conservent limites
de groupe et styles conditionnels ; et émettent les totaux
integer/decimal/currency vérifiés uniquement sur la dernière page. Les limites
de ligne, field, cellule, expression, échantillon et page échouent fermées.
Le YAML strict expose désormais ce contrat directement : un élément
type: table doit déclarer ses colonnes et un binding vers un tableau.
Groupement, totaux, styles conditionnels, répétition du header et taille des
lignes restent typés dans TableIr ; chaque ligne liée doit être un object.
Les limites de lignes, fields et cellules propres au template peuvent seulement
réduire les limites globales de ressources du compilateur.
Le layout transforme maintenant chaque page de table bornée en
ResolvedTableFragment immuable sur une page physique de la scène. Colonnes
exactes, headers répétés, rectangles ligne/cellule, styles data, continuité de
groupe, totaux et texte façonné sont fixés avant export. Les continuations
respectent les bornes globales de pages et collision ; les exporters ne mesurent
ni ne repaginent.
PDF éditable/flattened/hybride, SVG, PNG/JPEG et HTML sémantique/fixe rendent maintenant
directement ces fragments résolus. L'usage des polices PDF inclut chaque run de
cellule, SVG et HTML incluent les polices des styles data, et le raster trace les
mêmes glyphes façonnés. Le preflight valide structure de table, bornes des
cellules, diagnostics de texte et exigences de polices incorporées avant export.
Les éléments texte peuvent composer une ligne de style partagé avec
text_segments, des bindings de chaîne et des longueurs gap_after explicites.
Exigez overflow: error et max_lines: 1 ; toutes les parties et espaces sont
mesurés ensemble avant alignement et partagés par PDF, SVG, raster et HTML.
Les runs PDF éditables utilisant une face Standard 14 enregistrée sont émis
comme opérations de texte PDF natives. Les largeurs et le crénage AFM continuent
de déterminer le layout ; le lecteur PDF applique les avances natives de la face.
Les sorties sont PDF éditable/flattened/hybride, SVG, PNG, JPEG, HTML sémantique/fixe,
CSV streaming et masques PNG/PDF/SVG/JSON. Les modes préparés échouent
explicitement ou figurent dans ExportLossReport.
Chaque format de document écrit vers un writer fourni par l'appelant et offre aussi des octets bornés en mémoire ; le CSV de dataset streame ses lignes par les deux mêmes interfaces. Le DPI ne concerne que PNG/JPEG et la qualité uniquement JPEG. PNG préserve la transparence, tandis que JPEG enregistre l'aplatissement alpha du style ou de l'image avant une sortie stricte. Le HTML fixe ne déclare pas la capacité sémantique. PDF émet des métadonnées déterministes de titre, creator et producer ; le PDF éditable embarque les subsets exacts de glyphes et les maps Unicode. PDF Hybrid peint des contours déterministes et ajoute un texte Unicode invisible et subsetté aux coordonnées résolues des glyphes pour la recherche, la sélection et l'extraction. Liens, bookmarks, accessibilité tagged, PDF/A, WebP, XLSX, ZPL et ESC/POS restent des contrats préparés explicites.
La validation possède des étapes explicites de schéma, données typées, layout résolu et preflight conscient de l'exporter. Les warnings bornés sont first-class ; strict les rejette et la troncature du rapport échoue fermée. Le preflight détecte les écarts binding, asset, glyphe, collision, overflow, DPI effectif, vector/CMYK/alpha JPEG, police incorporée pour PDF editable/hybride et accessibilité demandée.
Les fingerprints déterministes cadrent versions schéma et engine,
template/données/patches canoniques, digests des assets référencés et des
polices enregistrées. Les champs JSON canoniques utilisent une passe de
dimensionnement suivie d'un hachage SHA-256 direct sous le budget agrégé
max_output_bytes, en conservant le framing V1 sans retenir un buffer JSON
complet. LayoutEngine::resolve_cached ne résout qu'en cas de
miss du SceneCache borné, renvoie des scènes immuables partagées pour
render-many et rejette les anciennes versions d'engine.
Le batch ordonné complet de patches a une limite globale ; remove/replace
rejettent tout subtree cible contenant un descendant locked.
Le travail sur entrée hostile possède des bornes explicites. Le binding partage un seul budget d'éléments entre racines, descendants et expansion des repeats, avec annulation/progression coopérative aux frontières d'élément. Le layout a un budget total de comparaisons spatiales en plus du reflow borné. Les lectures filesystem sous racine canonique rejettent traversal et liens sortants, ouvrent sans suivre un symlink/reparse point final substitué, appliquent la limite d'octets et revalident le sandbox autour de la lecture. L'annulation d'export précède toute sortie visible par l'appelant.
Les gates de fiabilité comprennent des snapshots exacts du SVG visuel et du masque de collision, des properties de géométrie fixed-point et des cibles fuzz séparées pour le pipeline YAML/bind/layout borné, l'Unicode arbitraire et les textes trop grands, les assets raster corrompus, tailles absurdes/overlaps/ anchors circulaires et graphes d'include malformés, circulaires ou trop profonds. Une entrée invalide peut échouer avec une erreur typée, mais ne doit jamais provoquer panic, boucle infinie ou allocation sans borne explicite.
Le debug reste une couche dérivée en lecture seule. DebugOverlay fournit des
grilles bornées de 1/5/10/20 points, règles, coordonnées, IDs, bounds distincts,
anchors, régions résolues, géométrie safe/collision, exclusions et crosshairs
sans changer layout ni ordre de peinture. Les masques
collision/layout/visual/combined dérivent leurs rectangles occupés et libres
par vue et exportent PNG, PDF, SVG ou un JSON stable
occupied/free/collisions/overflow. inspect et explain conservent une trace
structurée des x/y/width/height source, anchors, région, mesure, policy de
collision, page/reflow et provenance.
Le JSON, le SVG et le PDF du masque comptent d'abord sous max_output_bytes
sans retenir la sortie, rejettent un résultat excessif avant de toucher la
destination, puis sérialisent directement dans le writer de l'appelant. PDF
émet des objets indépendants, un stream de commandes fixed-point de taille
exacte et son xref sans buffer de page ni de fichier complet.
collision_mask_json_4m mesure une sortie exacte de 4 188 826 octets ;
collision_mask_pdf_100k mesure 100 000 rectangles et un PDF exact de 1 800 626
octets avec checkpoints RSS idle, pic et retenu.
Le macrobenchmark a4_report_export_matrix mesure le pipeline A4 maintenu de
deux pages comme une opération bornée : décodage YAML et données typées, patch
runtime, mesure/layout/collision/reflow, preflight et neuf sorties streamées. Il
couvre PDF éditable, flattened et hybride ; SVG ; HTML sémantique et fixe ;
PNG ; JPEG avec pertes best-effort explicites ; et CSV du dataset. Trois
échantillons isolés du commit propre sur Apple M1 ont mesuré 70,56 ms p50,
71,34 ms p95, 0,22 ms de MAD et 10,64 Mio de RSS de pic.
Le core déterministe ne dépend pas de l'IA. appcore-filemaker-ai est un bridge
optionnel de 20 outils sur appcore-ai; appcore-filemaker-cli est l'adaptateur
processus borné. Exemples et preuves se trouvent sur la branche Runtime beta.
Export et preflight rejettent indépendamment les scènes résolues publiques
anciennes ou malformées et appliquent les budgets pages, éléments, paths,
lignes, texte et coordonnées avant toute écriture. Overlay diagnostique,
masque de collision/JSON et régions libres consomment aussi des limites
explicites de comparaisons et géométrie conservée. Les ID validés préservent
l'invariant du constructeur lors de la désérialisation.
Les exports contrôlés observent l'annulation et signalent la progression depuis
les vraies boucles d'éléments des renderers, avant d'écrire la sortie préparée à
l'appelant.
Le pipeline de polices explicites utilise harfrust, maintenu par le projet
HarfBuzz, pour le shaping et skrifa, de Google Fonts, pour validation,
métriques et outlines ; il ne découvre aucune police du système.
Une police valide sans capital height OS/2 utilise ascent comme policy PDF
CapHeight explicite et déterministe ; les advances absentes échouent.
Les exemples Rust exécutables conservent le document dans les fichiers séparés
basic.yml
et intermediate.yml.
Les données typées restent dans des JSON correspondants séparés. Le lanceur de
base produit un SVG complet d'une page avec texte lié, dessins sémantiques,
sparkline et table stylée. L'intermédiaire produit un rapport confidentiel de
deux pages exactes avec numérotation, filigrane répété, graphiques vectoriels,
table paginée, preflight strict, PDF éditable, HTML fixe et aperçus SVG par
page. Tous deux enregistrent explicitement la police Noto Sans sous OFL fournie,
sans dépendre des polices hôte. Le code Rust n'intègre ni YAML ni JSON.
Documentation maintenue par le crate : guide, exemple de base et exemple intermédiaire.
EAN et QR Code natifs
Ajouts disponibles dans 0.1.0-beta.6 : type: barcode sélectionne EAN-8 pour exactement
huit chiffres ASCII ou EAN-13 pour treize, via text ou binding. Les deux
vérifient la clé fournie, conservent les zéros initiaux et refusent les autres
longueurs, sans compléter ni réparer. barcode::Ean8, Ean13 et ean_shape
exposent l'encodage. EAN-8 a 67 modules avec sept modules libres de chaque côté ;
EAN-13 en a 95, avec onze à gauche et sept à droite. Les chiffres lisibles restent
un texte séparé. Les suppléments de deux/cinq chiffres, UPC et GTIN-14 ne sont
pas pris en charge par cette API.
type: qr reçoit du texte UTF-8 littéral ou lié aux données.
qrcode::QrCode::encode produit un Model 2 normal avec correction moyenne,
mode octets et ECI UTF-8 explicite 26. La plus petite version suffisante est
sélectionnée. Les payloads doivent contenir 1–1024 octets UTF-8. Aucune URL n'est
consultée et le contenu n'est pas interprété. Une entrée vide/trop grande ou une
géométrie insuffisante échoue avant export ; erreurs et Debug omettent le payload.
Quatre modules libres entourent chaque côté. Le symbole carré est centré dans
une boîte rectangulaire sans déformation. Le layout compte les commandes
vectorielles dans le budget cumulatif max_path_commands, y compris les répétitions.
Les deux types deviennent des paths vectoriels au layout, partagés par PDF, SVG,
PNG/JPEG et HTML fixe/sémantique. Un ResolvedElement non préparé de kind Qr
ou Barcode n'a toujours pas d'exporter direct. Garder un fond clair libre, une
taille d'impression et un DPI adaptés ; tester l'imprimante/scanner réel.
L'encodage n'est pas une certification.
filemaker: '1.0'
model: canvas
id: symbols
page: { width: 300pt, height: 230pt }
elements:
- id: ean
type: barcode
x: 10pt
y: 10pt
width: 162pt
height: 40pt
text: '96385074'
style: { fill: black }
- id: qr
type: qr
x: 10pt
y: 70pt
width: 140pt
height: 140pt
text: 'https://example.org/local'
style: { fill: black }
tests/barcode_workflows.rs vérifie les modules EAN-8/EAN-13 du PNG réel et
décode les QR PNG/JPEG avec un decoder indépendant, dont Unicode et la borne
1024 octets. Entrées invalides, champs inconnus et budgets de paths sont testés.
Le flux multipage de 80 lignes/240 éléments en PDF/SVG/HTML reste couvert. Cela
valide l'export générique, pas la parité avec un PDF commercial externe. YAML
possède le layout ; les adaptateurs fournissent données typées et appels d'export.
Références : guide GS1 et API de l'encodeur QR.
Release publiée
0.1.0-beta.6 — publié sur crates.io ; les versions précédentes restent immuables.
Paquet publié
appcore-filemaker 0.1.0-beta.6 · docs.rs · source.
Prérelease indépendante — 2026-10-14
0.1.0-beta.7: modifications propres.
Le frontend YAML remplace le parser non maintenu par un parsing en Rust sûr (serde-saphyr). Les modèles source/fixtures YAML V1 et les booléens YAML 1.2 sont préservés. Champs inconnus, aliases récursifs et documents multiples sont rejetés ; les erreurs omettent les extraits source. Limites : profondeur 64, 1 000 000 événements, 50 000 aliases/ancres et 64 MiB de texte scalaire agrégé, en plus des limites entrée/schema existantes. Aucun fallback vers l’ancien parser distribué.
Audit du contenu variable (2026-10-17)
Audit du baseline publié (les défauts ci-dessous décrivent le paquet audité) : mesure, expand, flux, ancres, groupes, audit/preflight et overlays existent déjà. La hauteur intrinsèque des conteneurs et un corps de tableau distinct pour la dernière page n’existent pas. Expand avec max_lines peut tronquer sans diagnostic ; next_page peut repartir à y=0 malgré les marges ; l’audit n’applique pas toujours les exceptions de groupes. Zéro perte d’export ne prouve pas la sûreté du layout. Corriger avant de recommander une acceptation stricte. Le premier audit a exécuté 55 tests existants et rendu des contrôles de 1/2/5/20 pages sans réorganisation ; cela ne qualifie pas tout le corpus commercial. Dimensionnement opt-in et convergence bornée de dernière page exigent contrats/migration explicites, en préservant V1.
Le code 1.0.0-rc.2 diagnostique désormais expand avec limite et conserve cet avertissement sur les fragments, inclut le padding des groupes, respecte le corps/région de continuation avec transformations du parent et partage les filtres de groupes/ignore/overlay avec l’audit. Champs publics, YAML V1, limites explicites et bandes sont préservés. Les conteneurs intrinsèques du corps ont désormais l’API opt-in ci-dessous ; les corps adaptatifs de table ont désormais un plan opt-in séparé ; ces corrections sont incluses dans 1.0.0-rc.2.
Hauteur intrinsèque (opt-in, 1.0.0-rc.2)
Utiliser LayoutEngine::resolve_intrinsic(&document) au lieu de resolve
pour mesurer les éléments déclarant explicitement height: auto en YAML.
resolve, le parseur V1, les littéraux des structures publiques et les
politiques de dépassement à hauteur fixe conservent leur contrat. Aucune
nouvelle dépendance ni mesure manuelle côté application.
Le texte réutilise le shaping et la mesure existants avec la police enregistrée,
le style, la largeur, les marges internes et les avances de lignes effectifs.
Un flux horizontal prend la hauteur maximale ; un flux vertical additionne
les hauteurs et espacements. constraints: {min_height: 14mm} préserve le
format court. Flux et ancres reçoivent la boîte mesurée avant les collisions.
Le document d'entrée reste intact. Un contenu dépassant max_height échoue
avec son identifiant au lieu d'être coupé.
Cette première option accepte des largeurs définies et des flux du corps avec
distribution au début. Le texte automatique accepte wrap, expand ou
error, horizontal et sans max_lines. Déclarer une hauteur fixe pour clip,
ellipsis ou shrink. Les groupes automatiques sont indivisibles et doivent
tenir sur une page utile. Leurs enfants ne peuvent déclarer x/y, ancres,
région, transformations, alignement géométrique autre que début,
keep-with-next ou hauteur/contraintes de hauteur en pourcentage. Un texte
extensible enfant doit utiliser la hauteur automatique. Groupes absolus ou
distribués, écriture verticale, bandes de page automatiques, aspect ratio ou
preferred height sur un élément automatique et padding vertical en pourcentage
échouent explicitement, sans estimation circulaire implicite.
Le parcours vérifie l'annulation, les limites d'éléments/texte et une profondeur maximale de 64 avant de cloner le document lié aux données. La mesure vérifie l'annulation par élément ; le shaping d'un élément n'est pas préemptif. Copie et mesure ajoutent du travail et de la mémoire, sans gain de performance promis. Exécuter le preflight de layout sur la scène finale, y compris éléments fixes et overlays. Ne pas réutiliser les clés de cache du layout ordinaire.
Exécuter cargo run -p appcore-filemaker --example intrinsic_height --locked.
examples/intrinsic-height.yml utilise des JSON court/long séparés, deux
colonnes, un texte aligné à droite et des ancres déplaçant titre et corps.
Résultats dans target/filemaker-examples/. Rust ne remplace aucune coordonnée
YAML. L'exemple exige des diagnostics de layout valides avant l'export PDF.
Migration : sélectionner la nouvelle méthode et déclarer les hauteurs auto et
minimales en YAML. Retour arrière : conserver le dessin original et resolve.
L'API Rust est additive, sans autre parseur/version YAML. Les réserves variables
de première/pages intermédiaires/dernière page de table utilisent désormais le plan adaptatif séparé ; les flux globaux arbitraires restent hors de ce contrat.
Dans une racine participant à la mesure intrinsèque, les groupes sont structurels : les politiques de collision des descendants ignorent explicitement les identifiants des ancêtres. Les collisions entre frères et avec les éléments externes restent actives. La résolution ordinaire et les racines sans hauteur automatique conservent leurs politiques.
Le défaut d’extraction des ligatures est corrigé dans la version 1.0.0-rc.2 : clusters complets et /ActualText contextuel conservent le texte logique. Vérifier séparément extraction et rendu.
Pagination adaptative de table (opt-in, 1.0.0-rc.2)
AdaptiveLayoutPlan::parse_yaml lit un dessin YAML séparé et strict avec
filemaker_layout: '1.0'. Après le compile/bind V1 existant, appeler
LayoutEngine::resolve_adaptive(&document, &plan). Cette API additive conserve
les littéraux des structures publiques, le parseur V1, resolve et
resolve_intrinsic. Aucune dépendance, règle métier ou format persistant ajouté.
Le plan sélectionne une table racine, les corps verticaux first,
intermediate, last et single, un bloc racine final closing, son espacement
et la politique obligatoire oversized_group: error ou split_rows. Offsets
et hauteurs sont relatifs à la boîte complète, à largeur constante. Tous les
corps doivent tenir dans cette boîte, elle-même dans la région utile de page.
Les corps déclarés précèdent la réserve de clôture. Sa hauteur visible réutilise
la mesure intrinsèque et les contraintes existantes. Hauteur et espacement
sont soustraits une seule fois de single ou last. Le paginateur existant
soustrait ensuite l'en-tête une seule fois. Une clôture masquée ne réserve ni
hauteur ni espacement. Les bandes décoratives conservent leur sémantique V1,
sans réserve implicite.
Le groupe en attente existant permet de reconnaître le groupe final. Seule sa dernière ligne porte la différence entre capacité ordinaire et finale. Un changement de page recalcule réserve et mesure sensible à la page au départ suivant. Le fragment final est translaté vers le corps last/single ; les ancres placent la clôture après son bord inférieur. Ce problème vertical restreint ne nécessite ni estimation du nombre de pages ni point fixe global. Mesure de groupes et échantillonnage des colonnes peuvent toujours revisiter les lignes ; aucune garantie d'une seule lecture des données.
Les clés contiguës égales de keep_together_by restent ensemble quand groupe
et réserve finale tiennent sur une page de destination. split_rows sépare
les lignes si cette contrainte combinée dépasse la capacité ; error rejette.
Une ligne indivisible avec réserve trop haute échoue dans les deux cas. Un
groupe laissant la première page entièrement vide produit une erreur explicite,
sans page vide ni requalification implicite d'une continuation. Des données
vides peuvent produire en-tête et clôture si l'en-tête tient.
Limites explicites : une table racine visible commençant à la page physique
zéro ; un unique bloc final du corps ancré par top: table-id.bottom+gap ;
aucune transformation, région ou keep-with-next sur ces racines. La clôture
peut être un texte ou groupe de flux à hauteur automatique. Ses descendants
doivent tenir dans sa boîte mesurée sur la dernière page de table. Collision
incompatible ou croissance d'un texte fixe : erreur sans scène partielle.
Ne pas combiner table.page_bodies, reserve_after conditionnel ou
total_fields intégré avec ce propriétaire de réserve. Les totaux peuvent
être des données liées dans la clôture. Plusieurs tables, des largeurs finales
différentes ou un flux global dépendant des pages nécessitent un contrat
explicite ultérieur, sans inférence.
Les limites de taille, profondeur, éléments, lignes, pages et collisions restent
actives. Annulation vérifiée avant planification, pendant visite, mesure et
évaluation des réserves ; un shaping individuel n'est pas préemptif. L'index
spatial reste inchangé, sans gain de temps ou mémoire promis. Valider layout
et export séparément : réussite ou zéro perte déclarée ne prouve ni géométrie
ni fidélité du texte. Le défaut d’extraction des ligatures est corrigé dans la version 1.0.0-rc.2 : clusters complets et /ActualText contextuel conservent le texte logique. Vérifier séparément extraction et rendu.
Exécuter cargo run -p appcore-filemaker --example adaptive_pagination --locked.
examples/adaptive-pagination.yml, adaptive-pagination-plan.yml et les JSON
typés séparés génèrent 1/2/5/20 pages dans target/filemaker-examples/.
L'exemple exige audit de layout et preflight avant chaque PDF. La régression
de huit lignes passe de trois pages à deux avec une seule réserve finale ;
indices source uniques et ordonnés. Rust ne mesure pas le texte côté
application et ne remplace aucune coordonnée YAML.
Migration : garder le dessin original, déplacer les corps dans le plan,
retirer les réserves V1/par ligne concurrentes, déclarer l'ancre de clôture et
choisir la nouvelle méthode. Retour arrière : dessin et méthode d'origine ;
données et formats persistants inchangés. Inclus dans 1.0.0-rc.2 ;
l'application externe n'a pas migré.
first_after: {element: title, gap: 8pt} place facultativement le début de
first et single sous une boîte de layout déjà mesurée à la page physique
zéro. Leurs bords inférieurs déclarés sont conservés ; le début ne remonte
jamais au-dessus de l'offset initial. Ceci relie en-tête automatique à deux
colonnes et titre à la première région, sans calcul de coordonnées côté
application. La boîte complète de table peut recouvrir les boîtes précédentes
alors que le corps utile sélectionné les évite : déclarer leurs identifiants
dans collision.ignore local, comme dans examples/adaptive-header.yml.
Aucune désactivation globale des collisions. Cible non résolue, ultérieure,
sur une autre page ou corps initial épuisé : erreur explicite. Un jeu non vide
doit placer des lignes sur la première page ; un report initial ne laissant
que des titres est rejeté pour éviter un titre isolé.
Exécuter cargo run -p appcore-filemaker --example adaptive_header --locked
pour les en-têtes court/long. Le YAML et le plan communs utilisent des JSON
synthétiques séparés, avec audit de layout et preflight obligatoires. Les deux
exemples produisent ensemble six documents PDF pour vérification visuelle.
Preuves de pagination bornées (ajout 1.0.0-rc.2)
Utiliser LayoutEngine::diagnose_adaptive(&document, &plan, max_decisions) à la
place de resolve_adaptive pour obtenir une explication. AdaptiveLayoutReport
expose outcome(), decisions() et into_outcome(). Consulter ou enregistrer les
décisions avant de propager le résultat : les preuves précédentes restent
disponibles en cas d’échec. Chaque appel possède ses propres enregistrements.
Les API et littéraux publics existants sont conservés ; la résolution normale
ne crée aucun collecteur.
Chaque PaginationDecision contient l’ID du tableau, la page physique candidate,
les ordinaux des lignes source (base zéro, intervalle inclusif), la règle réelle
et les hauteurs numériques. capacity déduit l’en-tête du tableau une seule fois ;
used_height inclut les lignes et réserves précédentes ;
available_height = capacity - used_height ;
required_height = group_height + reserved_height. final_reservation est déjà
incluse dans reserved_height, sans addition supplémentaire. Elle combine bloc
final et espacement avec la différence entre hauteurs de corps normal et final,
et ne représente donc pas seulement la hauteur du bloc final.
Règles : fit, move_group, split_rows, move_row, skip_first_page, reject.
Un déplacement décrit le candidat source ; le fit suivant utilise les mesures
et réserves de destination. Une décision de groupe trop grand évalue une page
de continuation vide, même si elle n’est jamais émise. Ces preuves d’exécution
ne constituent ni une liste de pages émises ni une garantie de succès.
La validation préalable peut échouer sans décisions ; la validation finale peut
conserver des décisions fit sans retourner de scène. Un dataset vide ne génère
pas de décisions de lignes. L’enum des règles est non exhaustif.
La limite explicite doit être comprise entre un et ResourceLimits.max_elements.
Son épuisement retourne LimitExceeded avec le préfixe conservé, sans troncature
silencieuse ni scène partielle. Aucun texte, payload ou valeur de regroupement
n’est enregistré : seulement IDs, ordinaux et géométrie. Le collecteur optionnel
utilise de courtes sections mutex et conserve les caractéristiques Send/Sync du
moteur ; il ajoute un coût d’allocation et de sérialisation. Annulation et limites
existantes de pages, lignes et mesures restent actives.
cargo run -p appcore-filemaker --example adaptive_pagination --locked écrit
*-pagination.json, *-debug.json et les *-mask-*.svg par page, avec les exemples
de 1/2/5/20 pages. Réutilise DebugOverlay::build_bounded (zone utile, régions,
boîtes de layout/intrinsèques/visuelles et ancres) et l’exporteur SVG existant de
CollisionMask. Le masque représente l’occupation géométrique ; audit_layout
applique les politiques effectives de chevauchement.
SceneInspector::explain_layout explique la géométrie finale ; audit_layout
identifie collisions et coupures avec leur géométrie. Continuer le preflight strict
et la revue du PDF rendu : zéro perte d’export ne prouve pas un layout sûr.
Les étiquettes d’overlay peuvent contenir des expressions source ; les vérifier
avant partage externe.
Rollback : revenir à resolve_adaptive et ne plus enregistrer ces sorties.
Aucun changement YAML, persistance, dépendance, index spatial ou consommateur.
header_height indique la réserve d’en-tête appliquée ; body_height = capacity + header_height expose le corps normal avant cette déduction.
Corpus synthétique mixte et gate d’adoption du consommateur
Exécuter cargo run -p appcore-filemaker --example variable_corpus --locked.
Un dessin YAML et un plan adaptatif séparé reçoivent quatre datasets JSON typés
synthétiques. Les sorties target/filemaker-examples/variable-corpus-* incluent
PDF, scène, preflight strict, décisions, overlay et masques SVG.
Cet incrément corrige aussi la précédence de peinture des bandes dans le moteur existant. Aucune nouvelle dépendance, API publique, propriété sérialisée ou version publiée.
| Pages | Groupes | Lignes source |
|---|---|---|
| 1 | 3 | 9 |
| 2 | 12 | 36 |
| 5 | 40 | 120 |
| 20 | 190 | 570 |
Chaque groupe contient article, description variable et commentaire avec remise
détaillée. Les trois lignes partagent keep_together_by ; oversized_group: error
rejette explicitement une description dépassant une page. Le test vérifie le texte
logique après shaping et la hauteur mesurée des cellules, pas seulement les sources.
L’en-tête automatique à deux colonnes, le titre dépendant et le premier corps
partagent la mesure native. Trois taux de TVA, valeurs antérieures, remises,
totaux nets/fiscaux/finaux, conditions, signature et EAN-13 sont des données du
consommateur synthétique. FileMaker ne calcule pas les impôts et ne valide aucune
règle commerciale. Les totaux des fixtures sont cohérents, sans certification fiscale.
La première valeur antérieure est barrée par une ligne avec overlay local explicite.
Le filigrane de chaque page et le tampon initial utilisent les bandes existantes :
leur contrat de peinture sans collision rejette déjà un champ collision explicite.
La détection des collisions du corps reste active. Les ancres absentes doivent être
omises ou null, jamais des chaînes vides répétées. Les coordonnées X du corps sont
relatives à l’origine de la zone utile ; une régression vérifie que la barre reste
dans la cellule visée après cette translation. Il s’agissait d’une correction de
configuration de fixture, pas d’un défaut moteur ou de shaping.
Les tests vérifient ordre sans perte ni duplication, texte intégral, groupes
indivisibles, nombre de pages, géométrie de la barre et descendants de clôture
uniquement sur la dernière page, code et signature compris. audit_layout et
preflight strict sont exécutés. Les pages doivent aussi être rendues et inspectées :
zéro perte d’export ne valide pas le layout. Le défaut d’extraction des ligatures est corrigé dans la version 1.0.0-rc.2 : clusters complets et /ActualText contextuel conservent le texte logique. Vérifier séparément extraction et rendu. Les masques SVG montrent
l’occupation géométrique ; audit_layout applique les chevauchements intentionnels.
L’adoption exige une nouvelle RC exacte publiée au registry ; aucun patch local ou changement du consommateur externe.
Reproduire les vérifications après l’exemple : python3 crates/appcore-filemaker/wiki/audit/verify-variable-corpus.py. Nécessite Poppler, vérifie IDs ordonnés, lignes de totaux fournies, 95 modules EAN-13 rendus et zones de silence, puis rend les 28 pages. Ne remplace ni revue visuelle ni certification scanner.
Le filigrane appartient à page.master.background. z_index ordonne la peinture dans une couche ; une valeur négative dans header ne place pas le filigrane derrière les glyphes du corps. La régression a d’abord échoué avec la bande d’en-tête, puis vérifie couche de fond et ordre de peinture. Le preflight strict acceptait la configuration masquant le texte.
Le corpus a aussi révélé un défaut moteur : le tri lexical plaçait la couche vide du corps avant !page-background, et des noms Unicode pouvaient dépasser la bande de premier plan. La correction compatible donne une précédence explicite aux bandes : fond, corps, en-têtes/pieds. Les noms des couches du corps gardent leur ordre lexical, puis z-index et séquence. Les tests couvrent couches vides/personnalisées/Unicode, z-index extrêmes et pixels raster réels. Aucun remplacement d’index spatial ni nouvelle API.
Texte Unicode intégral dans les PDF (correction 1.0.0-rc.2)
L’export Editable et Hybrid associe chaque cluster à un seul glyphe à sa séquence
UTF-8 logique complète dans ToUnicode, au lieu du premier caractère seulement.
Le writer PDF existant produit les destinations UTF-16, paires de substitution
comprises. Les limites suivent les offsets logiques triés en octets, indépendamment
de l’ordre visuel RTL. Les frontières UTF-8 invalides et offsets terminaux ou hors
texte retournent ExportUnsupported, sans omission silencieuse de mappage.
Un glyphe peut représenter fi et le caractère littéral fi, ou du texte composé
et décomposé. Un seul mappage par police ne représente pas toutes les occurrences.
Les runs ambigus ou clusters à plusieurs glyphes conservent le texte logique
original dans des spans PDF /ActualText. Le texte entier d’un cluster n’est pas
répété pour chacun de ses glyphes. Aucune normalisation Unicode n’est appliquée.
Les lecteurs de ces cas contextuels doivent respecter /ActualText.
PDF 1.7, polices, IDs, avances et géométrie sont conservés. Aucune nouvelle dépendance, propriété publique, option YAML ou API. Standard 14 WinAnsi garde son parcours existant. Flattened produit toujours des contours sans texte recherchable. La correction concerne le mappage d’export et conserve layout, shaping, politiques de coupure et positionnement bidi. Les extracteurs RTL peuvent retourner l’ordre visuel avec contrôles directionnels ; l’exemple vérifie sa séquence complète sans diacritiques en ordre logique ou visuel.
Les chaînes des CMaps partagent le budget explicite
ResourceLimits.max_output_bytes, vérifié avant allocation. L’index temporaire
des clusters est linéaire dans le nombre borné de glyphes ; tri et recherche
ajoutent du travail sans refaire le shaping. Les limites de scène, texte et sortie
restent actives, sans promesse de RSS constant ou de gain de vitesse.
Exécuter cargo run -p appcore-filemaker --example pdf_unicode --locked pour créer
les échantillons Editable, Hybrid et Flattened : fi/fl/ffi/ffl, mots courants,
ligatures littérales, accents composés/décomposés, cluster isolé à plusieurs glyphes
avec sa propre ressource de police et clusters arabes.
Puis exécuter
python3 crates/appcore-filemaker/wiki/audit/verify-pdf-unicode.py.
Nécessite Poppler, rend chaque mode, compare les scalaires latins exacts sans
normalisation NFC/NFKC, vérifie l’exemple RTL et l’absence de texte Flattened,
puis compare les pixels Hybrid/Flattened. Audit, preflight, extraction et revue
visuelle restent des vérifications distinctes.
Le vérificateur du corpus mixte contrôle maintenant tous les textes des éléments et cellules, en plus des IDs et totaux. Les PDF existants doivent être réexportés. Un consommateur registry reçoit cette correction via une nouvelle RC exacte publiée et qualifiée. Aucune publication de paquet, déploiement wiki ou migration externe. Le rollback sélectionne explicitement la release qualifiée précédente de l’exporteur.