IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Delphi Discussion :

Transformer du code en image : Projet en cours


Sujet :

Delphi

  1. #1
    Membre expérimenté
    Avatar de XeGregory
    Homme Profil pro
    Passionné par la programmation
    Inscrit en
    Janvier 2017
    Messages
    718
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : Passionné par la programmation
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Janvier 2017
    Messages : 718
    Billets dans le blog
    1
    Par défaut Transformer du code en image : Projet en cours
    Je développe un utilitaire Delphi qui convertit du code source Pascal/Delphi en images esthétiques et prêtes à être partagées.

    Le programme prend en charge :
    • Tokenisation fine (mots‑clés, commentaires, chaînes, nombres).
    • Thèmes personnalisables (couleurs pour mots‑clés, commentaires, chaînes, etc.).
    • Numéros de ligne optionnels et barre de titre alignable.
    • Rendu DPI‑aware pour des images nettes sur écrans haute résolution.
    • Export PNG simple depuis l’interface.


    Pourquoi : faciliter la création d’illustrations de code pour articles, slides et réseaux sociaux sans copier‑coller maladroit.

    Nom : Capture d'écran 2026-02-14 182341.png
Affichages : 256
Taille : 71,7 Ko

    Nom : Capture d'écran 2026-02-14 182357.png
Affichages : 245
Taille : 67,2 Ko

    Nom : Capture d'écran 2026-02-14 182442.png
Affichages : 244
Taille : 64,8 Ko

    Nom : Capture d'écran 2026-02-14 182945.png
Affichages : 246
Taille : 98,0 Ko

    Oui, il existe déjà de nombreux services en ligne pour transformer du code en image, mais c’est plus fun de le coder soi‑même en Delphi.
    On ne peut pas faire confiance à un code qu'on n'a pas entièrement écrit soi‑même, et encore moins à celui qu'on a écrit entièrement. :aie:

  2. #2
    Membre expérimenté
    Avatar de XeGregory
    Homme Profil pro
    Passionné par la programmation
    Inscrit en
    Janvier 2017
    Messages
    718
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : Passionné par la programmation
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Janvier 2017
    Messages : 718
    Billets dans le blog
    1
    Par défaut
    Style MacOs
    Nom : Capture d'écran 2026-02-15 073254.png
Affichages : 214
Taille : 79,5 Ko

    Style Windows
    Nom : Capture d'écran 2026-02-15 073305.png
Affichages : 210
Taille : 79,1 Ko

    Nom : Video_2026_02_15-1_edit_0.gif
Affichages : 209
Taille : 1,13 Mo
    On ne peut pas faire confiance à un code qu'on n'a pas entièrement écrit soi‑même, et encore moins à celui qu'on a écrit entièrement. :aie:

  3. #3
    Rédacteur/Modérateur

    Avatar de SergioMaster
    Homme Profil pro
    Développeur informatique retraité
    Inscrit en
    Janvier 2007
    Messages
    15 819
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 69
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Janvier 2007
    Messages : 15 819
    Billets dans le blog
    65
    Par défaut
    J'avoue que c'est intéressant, surtout pour inclure dans un tutoriel (hors dvp qui a déjà son analyseur de syntaxe)
    Mais j'ai quelques questionnements :
    1. Taille de l'image, peut-on réduire ? Actuellement, comme j'ai un écran avec DPI 2, je suis obligé de retailler toutes mes captures d'écrans avant de les mettre sur le forum c'est gênant ce travail supplémentaire. Tu indiques "Rendu DPI‑aware pour des images nettes sur écrans haute résolution", moi c'est justement un DPI 1 que je souhaiterais. Une option pour l'export ?
    2. Je ne vois que l'image d'une partie de source, quid quand je veux voir un "source" plus grand ?
    3. Est-ce à partir d'un sélection de texte dans le source que se construit l'image ?


    Programme dispo où, pour tests ?
    MVP Embarcadero
    Delphi installés : D3,D7,D2010,XE4,XE7,D10 (Rio, Sidney), D11 (Alexandria), D12 (Athènes), D13 (Florence)
    SGBD : Firebird 2.5, 3, 5 et SQLite
    générateurs États : FastReport, Rave, QuickReport
    OS : Window Vista, Windows 10, Windows 11, Ubuntu, Androïd

  4. #4
    Membre expérimenté
    Avatar de XeGregory
    Homme Profil pro
    Passionné par la programmation
    Inscrit en
    Janvier 2017
    Messages
    718
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : Passionné par la programmation
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Janvier 2017
    Messages : 718
    Billets dans le blog
    1
    Par défaut
    Bonjour SergioMaster

    Taille de l'image, peut-on réduire ?
    Actuellement, l’export utilise un rendu DPI‑aware afin d’obtenir une image nette sur les écrans haute résolution.
    Sur un écran avec un facteur d’échelle ×2, cela génère effectivement une image deux fois plus grande.

    Je vais regarder pour ajouter une option permettant de choisir le facteur de mise à l’échelle, par exemple :

    • DPI 1 (taille réelle)
    • DPI 1.5
    • DPI 2 (valeur actuelle)


    Je n'avais pas anticipé cette fonctionnalité.
    Cela éviterait d’avoir à redimensionner manuellement les captures avant de les publier sur un forum.

    Ou bien laisser la possibilité à l’utilisateur de redimensionner l’image manuellement avant l’export, tout en conservant bien sûr la proportion de l’image.

    Je ne vois que l'image d'une partie de source, quid quand je veux voir un "source" plus grand ?
    Pour le moment, l’image générée correspond uniquement à la portion de code.
    Ont peut capturer un bloc plus large, J’ai fait un test avec une unité complète d’environ 3500 lignes… inutile de dire que la taille de l’image était monstrueuse

    Nom : Capture d'écran 2026-02-15 084119.png
Affichages : 204
Taille : 73,8 Ko

    Est-ce à partir d'une sélection de texte dans le source que se construit l'image ?
    Pour l’instant, non.

    Mais effectivement, on pourrait charger une unité complète et générer l’image uniquement à partir de la sélection.
    Je vais ajouter cette possibilité

    Programme dispo où, pour tests ?
    Je préfère finaliser quelques points avant de le mettre à disposition pour les tests.
    On ne peut pas faire confiance à un code qu'on n'a pas entièrement écrit soi‑même, et encore moins à celui qu'on a écrit entièrement. :aie:

  5. #5
    Rédacteur/Modérateur

    Avatar de SergioMaster
    Homme Profil pro
    Développeur informatique retraité
    Inscrit en
    Janvier 2007
    Messages
    15 819
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 69
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Janvier 2007
    Messages : 15 819
    Billets dans le blog
    65
    Par défaut
    Merci d'avoir pris en compte mes demandes.
    laisser la possibilité à l’utilisateur de redimensionner l’image manuellement avant l’export,

    on pourrait charger une unité complète et générer l’image uniquement à partir de la sélection.


    A mon avis cela changerait la donne
    MVP Embarcadero
    Delphi installés : D3,D7,D2010,XE4,XE7,D10 (Rio, Sidney), D11 (Alexandria), D12 (Athènes), D13 (Florence)
    SGBD : Firebird 2.5, 3, 5 et SQLite
    générateurs États : FastReport, Rave, QuickReport
    OS : Window Vista, Windows 10, Windows 11, Ubuntu, Androïd

  6. #6
    Membre expérimenté
    Avatar de XeGregory
    Homme Profil pro
    Passionné par la programmation
    Inscrit en
    Janvier 2017
    Messages
    718
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : Passionné par la programmation
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Janvier 2017
    Messages : 718
    Billets dans le blog
    1
    Par défaut
    laisser la possibilité à l’utilisateur de redimensionner l’image manuellement avant l’export,
    J’ai réfléchi à la question de la taille : Plutôt que de laisser l’utilisateur saisir librement les dimensions, je propose des options prédéfinies en pourcentage (par exemple 30 % = conserver 30 % de la taille d’origine).
    La liste des pourcentages est régénérée à chaque changement de dimension de l’image (par exemple quand l’utilisateur modifie la taille de la police), de sorte que les valeurs affichées en pixels restent toujours pertinentes.

    Selon le pourcentage choisi, l’image est automatiquement redimensionnée à l’export, ce qui simplifie la gestion des proportions ; si le résultat est encore trop grand, l’utilisateur peut réduire la taille de la police pour obtenir une dimension finale adaptée.

    Taille de font : 8
    Nom : Capture d'écran 2026-02-15 110239.png
Affichages : 203
Taille : 89,4 Ko

    Taille de font : 11
    Nom : Capture d'écran 2026-02-15 110251.png
Affichages : 205
Taille : 111,4 Ko

    on pourrait charger une unité complète et générer l’image uniquement à partir de la sélection.
    Nom : Capture d'écran 2026-02-15 114923.png
Affichages : 198
Taille : 72,5 Ko

    Nom : Capture d'écran 2026-02-15 114928.png
Affichages : 197
Taille : 53,5 Ko
    On ne peut pas faire confiance à un code qu'on n'a pas entièrement écrit soi‑même, et encore moins à celui qu'on a écrit entièrement. :aie:

  7. #7
    Rédacteur/Modérateur

    Avatar de SergioMaster
    Homme Profil pro
    Développeur informatique retraité
    Inscrit en
    Janvier 2007
    Messages
    15 819
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 69
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Janvier 2007
    Messages : 15 819
    Billets dans le blog
    65
    Par défaut
    Du coup, j'ai hâte d'avoir une copie du programme. Même s'il doit évoluer cela lèverait déjà un souci (comment mettre en page le code) pour mon futur livre.
    J'y vois déjà un sous avantage, le fait que le code soit en image obligera le lecteur à faire ses propres essais plutôt qu'un bête copier-coller et embêtera bien les IA
    MVP Embarcadero
    Delphi installés : D3,D7,D2010,XE4,XE7,D10 (Rio, Sidney), D11 (Alexandria), D12 (Athènes), D13 (Florence)
    SGBD : Firebird 2.5, 3, 5 et SQLite
    générateurs États : FastReport, Rave, QuickReport
    OS : Window Vista, Windows 10, Windows 11, Ubuntu, Androïd

  8. #8
    Invité de passage
    Homme Profil pro
         ​​​  
    Inscrit en
    Décembre 2025
    Messages
    22
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Autre

    Informations professionnelles :
    Activité :      ​​​  

    Informations forums :
    Inscription : Décembre 2025
    Messages : 22
    Par défaut
    L'image PNG gère la résolution le tag (pYHs) on peut la définir pour maintenir la qualité de l'image selon les écran, Excel par exemple modifie utilise la résolution 150

  9. #9
    Membre expérimenté
    Avatar de XeGregory
    Homme Profil pro
    Passionné par la programmation
    Inscrit en
    Janvier 2017
    Messages
    718
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : Passionné par la programmation
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Janvier 2017
    Messages : 718
    Billets dans le blog
    1
    Par défaut TChunkpHYs
    Citation Envoyé par Informt2025 Voir le message
    L'image PNG gère la résolution le tag (pYHs) on peut la définir pour maintenir la qualité de l'image selon les écran, Excel par exemple modifie utilise la résolution 150
    Oui. Le chunk pHYs du PNG stocke la résolution en pixels par unité (généralement pixels par mètre) et permet aux applications de connaître la densité de l’image.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    procedure SetPngDPI(Png: TPngImage; DPI: Integer);
    var
      PhysChunk: TChunkpHYs;
      PixelsPerMeter: Cardinal;
    begin
      PixelsPerMeter := Round(DPI * 39.3701);
     
      PhysChunk := TChunkpHYs(Png.Chunks.Add(TChunkpHYs));
      PhysChunk.PPUnitX := PixelsPerMeter;
      PhysChunk.PPUnitY := PixelsPerMeter;
      PhysChunk.UnitType := utMeter;
    end;
    Nom : Capture d'écran 2026-02-15 165501.png
Affichages : 185
Taille : 125,4 Ko

    C’est particulièrement utile
    - Export vers Excel/Word/PowerPoint (évite les surprises de taille).
    - Génération d’images pour l’impression (brochures, PDF haute qualité).

    Maintenant, le code appelle SetPngDPI(pngimage, GetDpiForHandle(Self.Handle)), ce qui écrit dans le PNG un chunk pHYs contenant la densité (pixels par mètre) calculée à partir du DPI retourné par GetDpiForHandle.

    Les dimensions en pixels ne changent pas : si FBuffer fait 600×600 px, le fichier PNG restera 600×600 px.
    Seule la métadonnée est modifiée : le chunk pHYs indique combien de pixels correspondent à une unité physique (mètre).

    Nom : Capture d'écran 2026-02-15 175614.png
Affichages : 182
Taille : 244,6 Ko
    On ne peut pas faire confiance à un code qu'on n'a pas entièrement écrit soi‑même, et encore moins à celui qu'on a écrit entièrement. :aie:

  10. #10
    Membre chevronné Avatar de der§en
    Homme Profil pro
    Chambord
    Inscrit en
    Septembre 2005
    Messages
    1 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loir et Cher (Centre)

    Informations professionnelles :
    Activité : Chambord
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 1 288
    Par défaut
    Citation Envoyé par SergioMaster Voir le message
    Le code soit en image obligera le lecteur à faire ses propres essais plutôt qu'un bête copier-coller et embêtera bien les IA
    Alors ça, c’est petit, très petit, moi j’utilise les exemples proposés pour les triturer dans tout les sens pour bien en comprendre les avantages ou les inconvénients tout en partant d’un code dont on est sur qu’il est fonctionnel, sans cela l’intérêt d’un bouquin perd beaucoup de son intérêt !

    C’est exactement comme cela qu’en ce moment je me prend la tête sur le dernier bouquin que j’ai acheté (en anglais ) sur le multithreading : https://www.cesarromero.com.br/en/ !

    Et le prochain sera sûrement : https://dalija.prasnikar.info/delphiebap/index.html !

  11. #11
    Membre expérimenté
    Avatar de XeGregory
    Homme Profil pro
    Passionné par la programmation
    Inscrit en
    Janvier 2017
    Messages
    718
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : Passionné par la programmation
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Janvier 2017
    Messages : 718
    Billets dans le blog
    1
    Par défaut Découpe intelligente des images exportées
    J'ai une idée : je vais inclure une option "Export fractionné" découpe intelligente des images exportées : si une image dépasse la hauteur maximale (par défaut 2000 pixels), elle est automatiquement découpée en tranches alignées sur les lignes de texte.
    Chaque tranche conserve l’intégrité des lignes et évite les coupures au milieu d’un texte ou d’une ligne de code.

    Ce qui change, si cette option "Export fractionné" est activée
    - Découpe automatique des images trop hautes en plusieurs fichiers PNG.
    - Découpe alignée sur la grille de lignes : la séparation se fait toujours entre deux lignes, jamais au milieu d’une ligne.
    - Nommage clair des fichiers produits : NomBase_0.png, NomBase_1.png, etc.

    Nom : Capture d'écran 2026-02-16 072346.png
Affichages : 162
Taille : 73,1 Ko

    Il faudrait que je prévoie une tolérance en pourcentage (par exemple, TolerancePct : = 1.0) pour éviter de découper une image qui dépasse légèrement le seuil (2000 Px), par exemple une image de hauteur de ~2005 Px.
    On ne peut pas faire confiance à un code qu'on n'a pas entièrement écrit soi‑même, et encore moins à celui qu'on a écrit entièrement. :aie:

  12. #12
    Rédacteur/Modérateur

    Avatar de SergioMaster
    Homme Profil pro
    Développeur informatique retraité
    Inscrit en
    Janvier 2007
    Messages
    15 819
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 69
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Janvier 2007
    Messages : 15 819
    Billets dans le blog
    65
    Par défaut
    @dersen En fait, tu utilises un bouquin comme je voudrais que soit utiliser un tutoriel : prendre le code et le comprendre. Que le bouquin propose les exemples téléchargeables à part, ça je le conçois.

    le multithreading
    vaste sujet, je n'ose pas mettre le nez dedans et pourtant j'ai déjà les livres de Dajila. En fait j'ai certainement fait quelques approches mais rien de "sensationnel" par manque d'idée pratique. Tu devrais aussi regarder du côté d'Olaf Monien (githuh) et des quelques présentations et participations aux Bootcamps (je pense à cette vidéo pointée ici) qu'il a faite
    MVP Embarcadero
    Delphi installés : D3,D7,D2010,XE4,XE7,D10 (Rio, Sidney), D11 (Alexandria), D12 (Athènes), D13 (Florence)
    SGBD : Firebird 2.5, 3, 5 et SQLite
    générateurs États : FastReport, Rave, QuickReport
    OS : Window Vista, Windows 10, Windows 11, Ubuntu, Androïd

  13. #13
    Rédacteur/Modérateur

    Avatar de SergioMaster
    Homme Profil pro
    Développeur informatique retraité
    Inscrit en
    Janvier 2007
    Messages
    15 819
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 69
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Janvier 2007
    Messages : 15 819
    Billets dans le blog
    65
    Par défaut sur l'export fractionné
    Une idée à peaufiner, couper une fonction ou une procédure étant toujours embêtant.
    Une suggestion : un découpage avec des propositions genre A4 [landscape,portrait] et autres, avec marges ? Tout en gardant, bien sûr un export choisi
    MVP Embarcadero
    Delphi installés : D3,D7,D2010,XE4,XE7,D10 (Rio, Sidney), D11 (Alexandria), D12 (Athènes), D13 (Florence)
    SGBD : Firebird 2.5, 3, 5 et SQLite
    générateurs États : FastReport, Rave, QuickReport
    OS : Window Vista, Windows 10, Windows 11, Ubuntu, Androïd

  14. #14
    Membre chevronné Avatar de der§en
    Homme Profil pro
    Chambord
    Inscrit en
    Septembre 2005
    Messages
    1 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loir et Cher (Centre)

    Informations professionnelles :
    Activité : Chambord
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 1 288
    Par défaut
    @SergioMaster: Merci pour les liens et vidéos sur le multithreading

    Dergen

  15. #15
    Membre expérimenté
    Avatar de XeGregory
    Homme Profil pro
    Passionné par la programmation
    Inscrit en
    Janvier 2017
    Messages
    718
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : Passionné par la programmation
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Janvier 2017
    Messages : 718
    Billets dans le blog
    1
    Par défaut Intégration d’un petit utilitaire de nettoyage de code
    CodeToImage.CleanPas est une unité qui automatise le nettoyage et la normalisation du code Delphi et Pascal.

    Elle vise à améliorer la lisibilité et la cohérence du code sans en altérer la sémantique.

    Fonctionnalités :
    - Préservation des chaînes littérales et des quotes échappées.
    - Normalisation des espaces autour des opérateurs et de la ponctuation selon des règles configurées.
    - Gestion fine de la ponctuation : traitement spécial de :=, .., ,, :, ;, ( et ).
    - Insertion automatique de sauts de ligne après ; lorsque du code suit sur la même ligne.
    - Réduction des lignes vides consécutives à une seule ligne vide.
    - Gère correctement les génériques TDictionary<string,string> sans insérer d’espaces indésirables.
    - Normalise les identifiants en se basant sur la ressource embarquée.

    Nom : Video_2026_02_23-2_edit_0.gif
Affichages : 113
Taille : 816,9 Ko

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    433
    434
    435
    436
    437
    438
    439
    440
    441
    442
    443
    444
    445
    446
    447
    448
    449
    450
    451
    452
    453
    454
    455
    456
    457
    458
    459
    460
    461
    462
    463
    464
    465
    466
    467
    468
    469
    470
    471
    472
    473
    474
    475
    476
    477
    478
    479
    480
    481
    482
    483
    484
    485
    486
    487
    488
    489
    490
    491
    492
    493
    494
    495
    496
    497
    498
    499
    500
    501
    502
    503
    504
    505
    506
    507
    508
    509
    510
    511
    512
    513
    514
    515
    516
    517
    518
    519
    520
    521
    522
    523
    524
    525
    526
    527
    528
    529
    530
    531
    532
    533
    534
    535
    536
    537
    538
    539
    540
    541
    542
    543
    544
    545
    546
    547
    548
    549
    550
    551
    552
    553
    554
    555
    556
    557
    558
    559
    560
    561
    562
    563
    564
    565
    566
    567
    568
    569
    570
    571
    572
    573
    574
    575
    576
    577
    578
    579
    unit CodeToImage.CleanPas;
     
    interface
     
    uses
      {Winapi}
      Winapi.Windows,
      {System}
      System.SysUtils, System.Classes, System.Generics.Collections, System.Types;
     
    procedure CleanPas(const IntSrc: TStrings; OutScr: TStrings);
     
    implementation
     
    type
      // État du parseur pour gérer les chaînes, commentaires et directives
      TState = (stNormal, stString, stCommentBrace, stDirective);
     
    var
      // Dictionnaire de normalisation :
      // clé = nom en minuscules, valeur = forme canonique
      FormattedMap: TDictionary<string, string>;
     
      { Charge la liste canonique depuis la ressource RCDATA. }
    procedure LoadFormattedNamesFromResource(const ResName: string);
    var
      RS: TResourceStream;
      SL: TStringList;
      I: Integer;
      key: string;
    begin
      if Assigned(FormattedMap) then
        Exit;
     
      // Création du dictionnaire
      FormattedMap := TDictionary<string, string>.Create;
      RS := nil;
      SL := TStringList.Create;
      try
        // Lecture de la ressource RCDATA dans un TStringList
        RS := TResourceStream.Create(HInstance, ResName, RT_RCDATA);
        SL.LoadFromStream(RS, TEncoding.UTF8);
     
        // Remplissage du dictionnaire
        for I := 0 to SL.Count - 1 do
        begin
          if SL[I].Trim = '' then
            Continue;
          key := LowerCase(SL[I].Trim);
          if not FormattedMap.ContainsKey(key) then
            FormattedMap.Add(key, SL[I].Trim);
        end;
      finally
        RS.Free;
        SL.Free;
      end;
    end;
     
    { Renvoie la forme canonique d'un identifiant si elle existe dans FormattedMap,
      sinon renvoie l'identifiant tel quel. }
    function NormalizeIdentifier(const Ident: string): string;
    var
      key: string;
    begin
      key := LowerCase(Ident);
      if Assigned(FormattedMap) and FormattedMap.TryGetValue(key, Result) then
        Exit
      else
        Result := Ident;
    end;
     
    { Retourne vrai si le caractère peut faire partie d'un opérateur (utilisé pour la mise en forme des espaces) }
    function IsOpChar(Ch: Char): Boolean; inline;
    begin
      Result := CharInSet(Ch, [':', '+', '-', '*', '/', '=', '<', '>', '.']);
    end;
     
    { Renvoie le caractère à la position sPos dans la chaîne S, ou #0 si hors limites }
    function PeekCharAt(const S: string; sPos: Integer): Char; inline;
    begin
      if (sPos >= 1) and (sPos <= Length(S)) then
        Result := S[sPos]
      else
        Result := #0;
    end;
     
    { Tente de reconnaître un opérateur multi-caractères à partir de sPos.
      Retourne True et place l'opérateur dans sOp si trouvé. }
    function MatchOpInLine(const S: string; sPos: Integer; out sOp: string)
      : Boolean;
    begin
      sOp := '';
      if sPos > Length(S) then
        Exit(False);
      if (sPos + 1 <= Length(S)) and (Copy(S, sPos, 2) = ':=') then
      begin
        sOp := ':=';
        Exit(True);
      end;
      if (sPos + 1 <= Length(S)) and (Copy(S, sPos, 2) = '<=') then
      begin
        sOp := '<=';
        Exit(True);
      end;
      if (sPos + 1 <= Length(S)) and (Copy(S, sPos, 2) = '>=') then
      begin
        sOp := '>=';
        Exit(True);
      end;
      if (sPos + 1 <= Length(S)) and (Copy(S, sPos, 2) = '<>') then
      begin
        sOp := '<>';
        Exit(True);
      end;
      if (sPos + 1 <= Length(S)) and (Copy(S, sPos, 2) = '..') then
      begin
        sOp := '..';
        Exit(True);
      end;
      if CharInSet(S[sPos], ['+', '-', '*', '/', '=', '<', '>']) then
      begin
        sOp := S[sPos];
        Exit(True);
      end;
      Result := False;
    end;
     
    { Procédure principale : nettoie et formate le code.
      - Normalise les identifiants en utilisant FormattedMap (chargée depuis la ressource)
      - Gère les espaces, ponctuation, opérateurs, chaînes, commentaires et directives
      - Traite les génériques (TDictionary<string,string>) }
    procedure CleanPas(const IntSrc: TStrings; OutScr: TStrings);
    var
      I, L, Idx, IndentLen: Integer;
      LineStr, LeadingIndent, FinalLine, Op: string;
      Ch, NextCh: Char;
      State: TState;
      SpacePending: Boolean;
      Sb: TStringBuilder;
      TempLines: TStringList;
      BlankCount: Integer;
      // Profondeur de générique (< ... >) ; 0 = hors générique
      GenericDepth: Integer;
     
      { Ajoute un espace en attente si SpacePending est vrai }
      procedure AppendPendingSpace;
      begin
        if SpacePending then
        begin
          Sb.Append(' ');
          SpacePending := False;
        end;
      end;
     
    { Assure qu'il y a exactement un espace final (utilisé après certains tokens) }
      procedure EnsureSingleTrailingSpace;
      begin
        if (Sb.Length = 0) then
          Sb.Append(' ')
        else if Sb.Chars[Sb.Length - 1] <> ' ' then
          Sb.Append(' ');
      end;
     
    begin
      if OutScr = nil then
        Exit;
     
      LoadFormattedNamesFromResource('NormalizeIdent');
     
      OutScr.BeginUpdate;
      try
        OutScr.Clear;
        if (IntSrc = nil) or (IntSrc.Count = 0) then
          Exit;
     
        Sb := TStringBuilder.Create;
        TempLines := TStringList.Create;
        try
          // Parcours ligne par ligne
          for I := 0 to IntSrc.Count - 1 do
          begin
            LineStr := IntSrc[I];
            IndentLen := 0;
     
            // Conserver l'indentation initiale (tabs et espaces)
            while (IndentLen < Length(LineStr)) and
              CharInSet(LineStr[IndentLen + 1], [#9, ' ']) do
              Inc(IndentLen);
            LeadingIndent := Copy(LineStr, 1, IndentLen);
     
            Sb.Clear;
            Sb.Append(LeadingIndent);
            State := stNormal;
            SpacePending := False;
            GenericDepth := 0;
            L := Length(LineStr);
            Idx := IndentLen + 1;
     
            // Parcours caractère par caractère
            while Idx <= L do
            begin
              Ch := LineStr[Idx];
              NextCh := PeekCharAt(LineStr, Idx + 1);
     
              case State of
                stNormal:
                  begin
                    // Début de chaîne littérale
                    if Ch = '''' then
                    begin
                      AppendPendingSpace;
                      Sb.Append('''');
                      State := stString;
                      Inc(Idx);
                      Continue;
                    end;
     
                    // Commentaire // jusqu'à la fin de la ligne
                    if (Ch = '/') and (NextCh = '/') then
                    begin
                      AppendPendingSpace;
                      Sb.Append(Copy(LineStr, Idx, L - Idx + 1));
                      Break; // fin de la ligne
                    end;
     
                    // Commentaire ou directive entre accolades { ... }
                    if Ch = '{' then
                    begin
                      var
                      j := Idx + 1;
                      while (j <= L) and (LineStr[j] = ' ') do
                        Inc(j);
                      if (j <= L) and (LineStr[j] = '$') then
                      begin
                        // directive du compilateur {$...}
                        AppendPendingSpace;
                        Sb.Append('{');
                        State := stDirective;
                        Inc(Idx);
                        Continue;
                      end
                      else
                      begin
                        // commentaire normal { ... }
                        AppendPendingSpace;
                        Sb.Append('{');
                        State := stCommentBrace;
                        Inc(Idx);
                        Continue;
                      end;
                    end;
     
                    // Espaces : on marque qu'un espace est en attente et on l'ajoute au prochain token significatif
                    if CharInSet(Ch, [' ']) then
                    begin
                      SpacePending := True;
                      Inc(Idx);
                      Continue;
                    end;
     
                    // Point-virgule : fin d'instruction ; on gère la coupure de ligne si nécessaire
                    if Ch = ';' then
                    begin
                      SpacePending := False;
                      if (Sb.Length > 0) and (Sb.Chars[Sb.Length - 1] = ' ') then
                        Sb.Length := Sb.Length - 1;
     
                      Sb.Append(';');
     
                      Inc(Idx);
                      while (Idx <= L) and CharInSet(LineStr[Idx], [' ']) do
                        Inc(Idx);
     
                      if Idx <= L then
                      begin
                        // Si la ligne continue après le ; on ajoute la ligne courante à TempLines et on recommence
                        FinalLine := Sb.ToString;
     
                        while (Length(FinalLine) > 0) and
                          (FinalLine[Length(FinalLine)] = ' ') do
                          SetLength(FinalLine, Length(FinalLine) - 1);
                        TempLines.Add(FinalLine);
     
                        Sb.Clear;
                        Sb.Append(LeadingIndent);
                        SpacePending := False;
                        Continue;
                      end
                      else
                      begin
                        // fin de ligne : assurer un espace final unique
                        EnsureSingleTrailingSpace;
                        Continue;
                      end;
                    end;
     
                    // --- Gestion des génériques : '<', '>' et virgule interne ---
                    if Ch = '<' then
                    begin
                      // Annule tout espace en attente et supprime un espace déjà ajouté avant '<'
                      SpacePending := False;
                      if (Sb.Length > 0) and (Sb.Chars[Sb.Length - 1] = ' ') then
                        Sb.Length := Sb.Length - 1;
     
                      // Entrée en générique : pas d'espace avant '<'
                      Sb.Append('<');
                      Inc(GenericDepth);
                      Inc(Idx);
     
                      // Supprimer les espaces immédiatement après '<' dans la source
                      while (Idx <= L) and CharInSet(LineStr[Idx], [' ']) do
                        Inc(Idx);
     
                      Continue;
                    end;
     
                    if Ch = '>' then
                    begin
                      if GenericDepth > 0 then
                      begin
                        // Supprimer les espaces avant '>' dans le résultat si présents
                        if (Sb.Length > 0) and (Sb.Chars[Sb.Length - 1] = ' ') then
                          Sb.Length := Sb.Length - 1;
     
                        Sb.Append('>');
                        Dec(GenericDepth);
                        Inc(Idx);
     
                        // Supprimer les espaces immédiatement après '>' dans la source
                        while (Idx <= L) and CharInSet(LineStr[Idx], [' ']) do
                          Inc(Idx);
     
                        Continue;
                      end;
                    end;
     
                    // Virgule : si on est dans un générique, ne pas forcer d'espace après la virgule
                    if Ch = ',' then
                    begin
                      SpacePending := False;
                      if (Sb.Length > 0) and (Sb.Chars[Sb.Length - 1] = ' ') then
                        Sb.Length := Sb.Length - 1;
                      Sb.Append(',');
                      Inc(Idx);
     
                      if GenericDepth > 0 then
                      begin
                        // supprimer les espaces après la virgule dans un générique
                        while (Idx <= L) and CharInSet(LineStr[Idx], [' ']) do
                          Inc(Idx);
                      end
                      else
                      begin
                        EnsureSingleTrailingSpace;
                        while (Idx <= L) and CharInSet(LineStr[Idx], [' ']) do
                          Inc(Idx);
                      end;
                      Continue;
                    end;
     
                    // --- Gestion spécifique du point '.' et de l'opérateur '..' ---
                    if Ch = '.' then
                    begin
                      // opérateur intervalle '..'
                      if NextCh = '.' then
                      begin
                        AppendPendingSpace;
                        if (Sb.Length > 0) and (Sb.Chars[Sb.Length - 1] <> ' ') then
                          Sb.Append(' ');
                        Sb.Append('..');
                        Inc(Idx, 2);
                        if (Idx <= L) and not CharInSet(LineStr[Idx],
                          [' ', ';', ',', ')', ']', ':']) then
                          Sb.Append(' ');
                        Continue;
                      end
                      else
                      begin
                        // séparateur '.' : AUCUN espace avant ni après
                        SpacePending := False;
                        if (Sb.Length > 0) and (Sb.Chars[Sb.Length - 1] = ' ') then
                          Sb.Length := Sb.Length - 1;
                        Sb.Append('.');
                        Inc(Idx);
                        while (Idx <= L) and CharInSet(LineStr[Idx], [' ']) do
                          Inc(Idx);
                        Continue;
                      end;
                    end;
     
                    // Deux points : gestion spéciale pour ':=' sinon formatage normal
                    if Ch = ':' then
                    begin
                      if (NextCh = '=') then
                      begin
                        AppendPendingSpace;
                        if (Sb.Length > 0) and (Sb.Chars[Sb.Length - 1] <> ' ') then
                          Sb.Append(' ');
                        Sb.Append(':=');
                        Inc(Idx, 2);
                        if (Idx <= L) and not CharInSet(LineStr[Idx],
                          [' ', ';', ',', ')', ']', ':']) then
                          Sb.Append(' ');
                        Continue;
                      end
                      else
                      begin
                        SpacePending := False;
                        if (Sb.Length > 0) and (Sb.Chars[Sb.Length - 1] = ' ') then
                          Sb.Length := Sb.Length - 1;
                        Sb.Append(':');
                        EnsureSingleTrailingSpace;
                        Inc(Idx);
                        while (Idx <= L) and CharInSet(LineStr[Idx], [' ']) do
                          Inc(Idx);
                        Continue;
                      end;
                    end;
     
                    // Fermeture de parenthèse ou crochet : pas d'espace avant, espace après
                    if CharInSet(Ch, [')', ']']) then
                    begin
                      SpacePending := False;
                      if (Sb.Length > 0) and (Sb.Chars[Sb.Length - 1] = ' ') then
                        Sb.Length := Sb.Length - 1;
                      Sb.Append(Ch);
                      EnsureSingleTrailingSpace;
                      Inc(Idx);
                      while (Idx <= L) and CharInSet(LineStr[Idx], [' ']) do
                        Inc(Idx);
                      Continue;
                    end;
     
                    // Ouverture de parenthèse ou crochet : espace avant si nécessaire, pas d'espace après
                    if CharInSet(Ch, ['(', '[']) then
                    begin
                      AppendPendingSpace;
                      Sb.Append(Ch);
                      Inc(Idx);
                      while (Idx <= L) and CharInSet(LineStr[Idx], [' ']) do
                        Inc(Idx);
                      Continue;
                    end;
     
                    // Opérateurs : si on est dans un générique, on ignore la détection d'opérateurs pour '<' et '>'
                    if IsOpChar(Ch) then
                    begin
                      if GenericDepth = 0 then
                      begin
                        if MatchOpInLine(LineStr, Idx, Op) then
                        begin
                          AppendPendingSpace;
                          if (Sb.Length > 0) and (Sb.Chars[Sb.Length - 1] <> ' ')
                          then
                            Sb.Append(' ');
                          Sb.Append(Op);
                          Inc(Idx, Length(Op));
                          if (Idx <= L) and not CharInSet(LineStr[Idx],
                            [' ', ';', ',', ')', ']', ':']) then
                            Sb.Append(' ');
                          Continue;
                        end;
                      end;
                    end;
     
                    // Détection d'un identifiant complet (lettre ou underscore suivi de lettres/chiffres/_)
                    // On normalise l'identifiant via NormalizeIdentifier avant de l'ajouter
                    if CharInSet(Ch, ['A' .. 'Z', 'a' .. 'z', '_']) then
                    begin
                      var
                      startIdx := Idx;
                      Inc(Idx);
                      while (Idx <= L) and CharInSet(LineStr[Idx],
                        ['A' .. 'Z', 'a' .. 'z', '0' .. '9', '_']) do
                        Inc(Idx);
                      var
                      Ident := Copy(LineStr, startIdx, Idx - startIdx);
                      AppendPendingSpace;
                      Sb.Append(NormalizeIdentifier(Ident));
                      Continue;
                    end;
     
                    // Par défaut : caractère isolé (ponctuation non traitée, etc.)
                    AppendPendingSpace;
                    Sb.Append(Ch);
                    Inc(Idx);
                  end;
     
                stString:
                  begin
                    // Dans une chaîne : on copie tout tel quel, en gérant les quotes doubles ('')
                    Sb.Append(Ch);
                    if Ch = '''' then
                    begin
                      if (Idx < L) and (LineStr[Idx + 1] = '''') then
                      begin
                        // quote échappée : '' -> on ajoute la seconde quote et on avance de 2
                        Sb.Append('''');
                        Inc(Idx, 2);
                        Continue;
                      end
                      else
                      begin
                        // fin de chaîne
                        State := stNormal;
                        Inc(Idx);
                        Continue;
                      end;
                    end
                    else
                      Inc(Idx);
                  end;
     
                stCommentBrace:
                  begin
                    // Dans un commentaire { ... } : on copie jusqu'à '}' et on revient à stNormal
                    Sb.Append(Ch);
                    if Ch = '}' then
                      State := stNormal;
                    Inc(Idx);
                  end;
     
                stDirective:
                  begin
                    // Directive {$ ... } : on copie jusqu'à '}' et on revient à stNormal
                    Sb.Append(Ch);
                    if Ch = '}' then
                      State := stNormal;
                    Inc(Idx);
                  end;
              end;
            end;
     
            // Nettoyage des espaces de fin de ligne
            FinalLine := Sb.ToString;
            L := Length(FinalLine);
            while (L > 0) and (FinalLine[L] = ' ') do
              Dec(L);
            FinalLine := Copy(FinalLine, 1, L);
     
            TempLines.Add(FinalLine);
          end;
     
          // Élimination des lignes vides consécutives : on garde au plus une ligne vide
          BlankCount := 0;
          for I := 0 to TempLines.Count - 1 do
          begin
            if Trim(TempLines[I]) = '' then
            begin
              Inc(BlankCount);
              if BlankCount = 1 then
                OutScr.Add('');
            end
            else
            begin
              BlankCount := 0;
              OutScr.Add(TempLines[I]);
            end;
          end;
     
        finally
          // Libération des ressources temporaires
          Sb.Free;
          TempLines.Free;
        end;
     
        // Libération de la table de correspondance chargée depuis la ressource
        if Assigned(FormattedMap) then
        begin
          FormattedMap.Free;
          FormattedMap := nil;
        end;
     
      finally
        OutScr.EndUpdate;
      end;
    end;
     
    end.
    NormalizeIdent.txt
    On ne peut pas faire confiance à un code qu'on n'a pas entièrement écrit soi‑même, et encore moins à celui qu'on a écrit entièrement. :aie:

Discussions similaires

  1. [PHP-JS] pasteHTML qui transforme le code
    Par jibouze dans le forum Langage
    Réponses: 1
    Dernier message: 28/10/2005, 18h36
  2. [] transformer un code ean en isbn
    Par dlpxlid dans le forum VB 6 et antérieur
    Réponses: 3
    Dernier message: 15/10/2005, 16h54
  3. [Général] Nombre de lignes de code d'un projet
    Par bigquick dans le forum MFC
    Réponses: 7
    Dernier message: 30/03/2005, 10h14
  4. Comptabiliser les lignes de code d'un projet
    Par JPigKeud dans le forum Qualimétrie
    Réponses: 5
    Dernier message: 07/01/2005, 15h09
  5. [image]Transformer un JTree en image
    Par loulou dans le forum Composants
    Réponses: 5
    Dernier message: 30/08/2004, 21h57

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo