advPDFLib : "go to next page" and TAdvGraphicsPDFEngine

Hi,

Following this post

Will the fix be applied for the use of TAdvGraphicsPDFEngine?
or include functions looks like "CalculateTextOverflow"

Regards

Hi,

We are currently finishing up TMS FNC Core 4.5 who will receive some improvements and new additions for TMS FNC PDF Lib, which will then at a later point in time be made available for TMS VCL UI Pack as well. The calculation you are refering to will not be included, but with TAdvGraphicsPDFEngine, there are calculation methods. If for some reason you cannot achieve what you want, let me know.

Hi

I used "CalculateTextHeight" function to do this, and it works. I was able to move the text that extends beyond the page boundaries to a new page.
In fact it works perfectly with no HTML text, but wit HTML tags I failed. If you have some idea, I'd appreciate.

Thank you,

Can you give me some example which reproduces the issue with HTML?

I think I found a solution last night.
I'm going to split the text into words and HTML tags, and the tags will be assigned an identifier—the same one for both the opening and closing tags. This will allow me to place the “correct” tags at the end of the split text on one page and at the beginning of the rest of the text on the other page.

Thanks

I finished this function.
If you wish I can give you this code to add into future update (and improve it, fo sure) ?

I just saw the new version of FNC Core: wow.
I think I'm going to “convert” all my modifications to use the FNC PDF components instead of the pure VCL I've been using so far.

Hi,

You can send in functions I'll take a look at them.

Hi and sorry for the delay.

This is my function to do this. I added the option to include a title with the text, and that title can either remain as an “orphan” at the bottom of the page or not, depending on whether the entire text carries over to the next page (comment in French :grin: ).


    FUNCTION Un_Chapitre( lGraph: TAdvGraphicsPDFEngine;
        CONST Titre,      /// Titre du paragraphe
        Texte: STRING;    /// Texte du paragraphe
        lHaut: Single;    /// position haut du paragraphe
        la_Ligne: tType_Ligne;   /// Type de ligne pour emphaser le titre   ( tylRien, tylFine, tylEpaisse, tylDouble);
        Decalage: Integer = 0;   /// decalage du texte par rapport au titre
        Ob_Titre: Boolean = False; /// Si True, l'absence de titre, annule l'impression du Texte
        Ya_Orpheline: Boolean = True;  /// Vrai : accepte le titre orphelin en base de page; faux : le bascule sur une nouvelle page
        Ya_Calcul : Boolean = False ): Single;
    VAR
        Min, Max, Position: Integer;
        Reste, la_Chaine, A_Tester: STRING;
        Hauteur, Haut_Calcul, Reste_Hauteur: Single;
        lCadre: TRectF;
        Meme_Page: Boolean;
        Lst_Mot : TStringList;  // Liste des mots et tags d'un Texte
        Lst_Tag : TStringList;  // Liste des tags et leur possition dans le texte
        lst_Tag_Actif : TStringList;  // liste des tags à compléter
        Commence : Boolean;   // Indique le debut de page, et tous les sauts de ligne ne doivent pas ĂŞtre pris en compte


        FUNCTION DecomposeHTMLText( CONST AText: STRING; VAR llstTAG : TStringList ): TStringList;
        VAR
            I: Integer;
            Mot_a_Suivre: STRING;
            UnTag: Boolean;
            le_Texte: STRING;
            TagStack: TStack<TPair<STRING, Integer>>;
            Tag_Suivant: Integer;
            TagName: STRING;
            TagInfo: TPair<STRING, Integer>;

                FUNCTION ExtractTagName( CONST ATag: STRING ): STRING;
                VAR
                    StartPos, EndPos: Integer;
                BEGIN
                    // <tagname ...> ou </tagname>
                    StartPos := 2;
                    IF Copy( ATag, 2, 1 ) = '/' THEN
                        Inc( StartPos );

                    // Trouver la fin du nom (espace ou >)
                    EndPos := StartPos;
                    WHILE ( EndPos <= Length( ATag ) ) AND ( ATag[ EndPos ] <> ' ' ) AND ( ATag[ EndPos ] <> '>' ) DO
                        Inc( EndPos );

                    Result := LowerCase( Copy( ATag, StartPos, EndPos - StartPos ) );
                END;

                FUNCTION IsTagEntrant( CONST ATag: STRING ): Boolean;
                BEGIN
                    Result := ( Copy( ATag, 2, 1 ) <> '/' );
                END;

                FUNCTION IsTagSortant( CONST ATag: STRING ): Boolean;
                BEGIN
                    Result := Copy( ATag, 2, 1 ) = '/';
                END;

        BEGIN
            Result := TStringList.Create;
            /// † remplace le séparateur de chaine "Name=Value" de stringList. Séparateur différent de = pour ne pas rentrer en conflit des éléments des tags
            Result.NameValueSeparator := '†';
            TagStack := TStack<TPair<STRING, Integer>>.Create;
            llstTAG.Clear;
            Mot_a_Suivre := '';
            UnTag := False;
            Tag_Suivant := 1;

              le_Texte := StringReplace( AText, sLineBreak, '<BR>', [ rfIgnoreCase, rfReplaceAll ] );;
              le_Texte := StringReplace( AText, '<br>', '<BR>', [ rfIgnoreCase, rfReplaceAll ] );;
              le_Texte := StringReplace( AText, '<Br>', '<BR>', [ rfIgnoreCase, rfReplaceAll ] );;
 
            FOR I := 1 TO Length( le_Texte ) DO
            BEGIN
                /// † remplace le séparateur de chaine "Name=Value" de stringList
                CASE le_Texte[ I ] OF
                    '<':
                        BEGIN
                            IF Mot_a_Suivre <> '' THEN
                            BEGIN
                                Result.Add( Mot_a_Suivre + '†0' );
                                Mot_a_Suivre := '';
                            END;
                            UnTag := True;
                            Mot_a_Suivre := '<';
                        END;

                    '>':
                        BEGIN
                            Mot_a_Suivre := Mot_a_Suivre + '>';
                            TagName := ExtractTagName( Mot_a_Suivre );

                            IF IsTagEntrant( Mot_a_Suivre ) THEN
                            BEGIN
                                TagStack.Push( TPair<STRING, Integer>.Create( TagName, Tag_Suivant ) );
                                IF TagName = 'BR' THEN
                                BEGIN
                                    Result.Add( Mot_a_Suivre + '†0' );
                                END ELSE
                                BEGIN
                                   Result.Add( Mot_a_Suivre + '†' + IntToStr( Tag_Suivant ) );
                                   llstTAG.Add( IntToStr( Tag_Suivant ) + '=' + IntToStr( Result.Count - 1 ) );
                                   Inc( Tag_Suivant );
                                END;     //  Fin de IF
                            END
                            ELSE
                            BEGIN
                                IF IsTagSortant( Mot_a_Suivre ) THEN
                                BEGIN
                                    IF ( TagStack.Count > 0 ) AND ( TagStack.Peek.Key = TagName ) THEN
                                    BEGIN
                                       TagInfo := TagStack.Pop;
                                        Result.Add( Mot_a_Suivre + '†' + IntToStr( TagInfo.Value ) );
                                        llstTAG.Add( '-' + IntToStr( TagInfo.Value ) + '=' + IntToStr( Result.Count - 1 ) );
                                    END
                                    ELSE
                                        Result.Add( Mot_a_Suivre + '†0' );
                                END
                                ELSE
                                    Result.Add( Mot_a_Suivre + '†0' );
                            END;   // Fin de IF
                            Mot_a_Suivre := '';
                            UnTag := False;
                        END;

                    ' ', #9:
                        BEGIN
                            IF NOT UnTag THEN
                            BEGIN
                                IF Mot_a_Suivre <> '' THEN
                                BEGIN
                                    Result.Add( Mot_a_Suivre + le_Texte[ I ] + '†0' );
                                    Mot_a_Suivre := '';
                                END;
                            END
                            ELSE
                                Mot_a_Suivre := Mot_a_Suivre + le_Texte[ I ];
                        END;

                    #10, #13:
                        BEGIN
                            IF NOT UnTag THEN
                            BEGIN
                                IF Mot_a_Suivre <> '' THEN
                                BEGIN
                                    Result.Add( Mot_a_Suivre + '†0' );
                                    Mot_a_Suivre := '';
                                END;
                            END
                            ELSE
                                Mot_a_Suivre := Mot_a_Suivre + sLineBreak;
                        END;

                ELSE
                    Mot_a_Suivre := Mot_a_Suivre + le_Texte[ I ];
                END;
            END;

            IF Mot_a_Suivre <> '' THEN
                Result.Add( Mot_a_Suivre + '†0' );

            TagStack.Free;
            Exit( Result );
        END;

        FUNCTION Reconstruit_Texte(Fin : Boolean; lMin, lMax : Integer): String;
        VAR
           Chaine : String;
           Mot, Index : String;
           Idx : Integer;
           I : Integer;
           Position : Integer;

        BEGIN
            Result := '';
            Chaine := '';
            lst_Tag_Actif.Clear;

            FOR I := lMin TO lMax DO
            BEGIN
                Mot := Lst_Mot.Names[ I ];
                Index := Lst_Mot.ValueFromIndex[ I ];
                Idx := StrToInt( Index );
                IF (NOT  Mot.Trim.IsEmpty ) THEN
                BEGIN
                    IF Idx = 0 THEN
                    BEGIN  // Mot
                       IF Commence AND ( Mot = '<BR>' ) THEN
                       BEGIN
                          Commence := Commence;
                          Continue;
                       END;    // Fin de IF

                       IF Chaine.IsEmpty THEN
                          Chaine := Mot
                       ELSE
                          Chaine := Chaine + Mot;
                       IF Commence THEN
                          Commence := False;
                    END ELSE
                    BEGIN  // Tag
                            Chaine := Chaine + Mot;
                            // Traite les tags actifs
                            Position := lst_Tag_Actif.IndexOf( ( IF Min = 0 THEN '-' ELSE '' ) + Index );   /// le IF ternaire est pour distinguer le test de la chaine ou la reconstruction de la nouvelle chaine après une bascule sur une nouvelle page
                            IF Position = -1 THEN
                            BEGIN // tag d'entrée <..>
                                lst_Tag_Actif.Add( ( IF Min = 0 THEN '-' ELSE '' ) + Index );
                            END
                            ELSE
                            BEGIN // tag de sortie </..>
                                lst_Tag_Actif.Delete( Position );
                            END; //  Fin de IF
                    END;     //  Fin de IF
                END;    // Fin de IF
            END;    //  Fin de FOR


           FOR I := 0 TO lst_Tag_Actif.Count - 1 DO
           BEGIN
                Mot := Lst_Mot.Names[ StrToInt( Lst_Tag.values[ lst_Tag_Actif.Strings[ I ] ] ) ];
                IF NOT Mot.IsEmpty THEN
                   IF Min = 0 THEN     // reaffecte les tags de sortie pour le test
                      Chaine := Chaine + Mot
                   ELSE                // reaffecte tous les tags d'entrée pour le nouveau texte
                      Chaine := Mot + Chaine;
           END;    //  Fin de FOR
            Result := Chaine;
        END;    // Fin de FUNCTION

    BEGIN
        Commence := False;
        Result := lHaut;
        Hauteur := lHaut;
        lCadre := Page_Active;
        lCadre.Top := lHaut;

        IF Ob_Titre AND Titre.IsEmpty THEN
        BEGIN
            Exit;
        END
        ELSE
        BEGIN
            Lst_Mot := TStringList.Create;  // Liste des mots et tags d'un Texte
            Lst_Mot.Duplicates := dupAccept;
            Lst_Mot.Sorted := False;

            Lst_Tag := TStringList.Create;  // Liste des tags et leur possition dans le texte
            Lst_Tag.Duplicates := dupError;
            Lst_Tag.Sorted := True;

            lst_Tag_Actif := TStringList.Create;  // liste des tags à compléter
            lst_Tag_Actif.Duplicates := dupError;
            lst_Tag_Actif.Sorted := True;

            TRY
                // Le titre
                IF NOT Titre.IsEmpty THEN
                BEGIN
                    Haut_Calcul := Hauteur;
                    Haut_Calcul := Haut_Calcul + lGraph.CalculateTextHeight( Titre,
                        lCadre,
                        True,
                        True );
                    IF NOT Haut_de_Page( Haut_Calcul ) THEN
                    BEGIN  // Tout le Titre tient sur la meme page, pas besoin de changer de page
                        IF ( NOT Haut_de_Page( Haut_Calcul ) ) AND ( NOT Ya_Orpheline ) THEN
                        BEGIN // On continue sur la meme page mais le titre ne peut etre orphelin, donc on teste une lige de texte
                            lCadre.Left := lCadre.Left + MillimeterToPixel( Decalage );
                            Haut_Calcul := Haut_Calcul + lGraph.CalculateTextHeight( 'A' + sLineBreak,
                                lCadre,
                                True,
                                True );
                            IF NOT Haut_de_Page( Haut_Calcul ) THEN // le titre ne sera pas orphelin
                            BEGIN
                                Hauteur := lHaut; // position du titre
                            END ELSE
                            BEGIN
                                Pdf.NewPage;
                                Filigrane( ya_Calcul );
                                Hauteur := Haut_Calcul
                            END; // Fin de IF
                        END;
                    END
                    ELSE
                    BEGIN // le Titre doit passer sur une nouvelle page
                       Pdf.NewPage;
                       Filigrane( ya_Calcul );
                       Hauteur := Haut_Calcul
                    END;

                    // Ecriture du titre
                    lCadre.Top := Hauteur;
                    lCadre.Left := Page_Active.Left;
                    Hauteur := Hauteur + lGraph.CalculateTextHeight( Titre,
                        lCadre,
                        True,
                        True );
                    lGraph.DrawText(
                        lCadre,
                        Titre,
                        True,
                        gtaLeading,
                        gtaLeading,
                        gttWord, 0, -1, -1, True );
                    CASE la_Ligne OF
                       tylFine :
                            Begin
                               lGraph.DrawLine( PointF( Marge, Hauteur - 1 ), PointF( Marge_Droite, Hauteur - 1 ) );
                            End;
                       tylEpaisse :
                            Begin
                               lGraph.DrawLine( PointF( Marge, Hauteur - 1 ), PointF( Marge_Droite, Hauteur - 1 ) );
                               lGraph.DrawLine( PointF( Marge, Hauteur ), PointF( Marge_Droite, Hauteur ) );
                               Hauteur := Hauteur + 1;
                            End;
                       tylDouble :
                            Begin
                               lGraph.DrawLine( PointF( Marge, Hauteur - 1 ), PointF( Marge_Droite, Hauteur - 1 ) );
                               Hauteur := Hauteur + 2;
                               lGraph.DrawLine( PointF( Marge, Hauteur - 1 ), PointF( Marge_Droite, Hauteur - 1 ) );
                            End;
                    END;   //  Fin de CASE
                END; // Fin de IF

                // Le Texte
                lCadre.Left := lCadre.Left + MillimeterToPixel( Decalage );
                lCadre.Top := Hauteur;
                Haut_Calcul := Hauteur;
                la_Chaine := Texte;
                Lst_Mot.Clear;
                Lst_Tag.Clear;

                WHILE la_Chaine <> '' DO
                BEGIN
                    Haut_Calcul := Haut_Calcul + lGraph.CalculateTextHeight( la_Chaine,
                        lCadre,
                        True,
                        True );
                    IF NOT Haut_de_Page( Haut_Calcul ) THEN
                    BEGIN // Tout le texte tient sur la meme page, pas besoin de changer de page
                        lGraph.DrawText(
                            lCadre,
                            la_Chaine,
                            True,
                            gtaLeading,
                            gtaLeading,
                            gttWord, 0, -1, -1, True );
                        Hauteur := Haut_Calcul;
                        lCadre.Top := Hauteur;
                        la_Chaine := '';
                    END
                    ELSE
                    BEGIN // le texte doit passer sur une nouvelle page
                        // Décompose le texte en mots
                        lst_mot := DecomposeHTMLText( Texte, lst_Tag );

                        // Décomposition par mot
                        Min := 0;
                        Max := lst_mot.Count - 1;
                        Reste := '';
                        Haut_Calcul := Hauteur;

                        Meme_Page := False;
                        Reste_Hauteur := Page_Fin - Hauteur;
                        REPEAT // s'arrete quand la chaine de test ne fait plus que 2 caracteres
                            IF NOT Meme_Page THEN
                            BEGIN
                                Position := Round( ( Max - Min ) / 2 );
                                // test si le mot est en fait un tag
                                IF Lst_Mot.ValueFromIndex[ Position ] <> '0' THEN
                                BEGIN
                                   VAR Sortir : Boolean;
                                   Sortir := False;
                                   REPEAT
                                      Chaine := Lst_Mot.Names[ Position ];
                                      IF Posex( '</', Chaine ) > 0 THEN
                                      BEGIN
                                        Inc( Position );
                                        Sortir := Lst_Mot.ValueFromIndex[ Position ] = '0';
                                        IF Sortir THEN
                                           Dec( Position );
                                      END ELSE
                                      BEGIN
                                        Dec( Position );
                                        Sortir := Lst_Mot.ValueFromIndex[ Position ] = '0';
                                      END;     //  Fin de IF
                                   UNTIL Sortir;   //  Fin de REPEAT
                                END;    // Fin de IF
                            END; //  Fin de IF
                            A_Tester := Reconstruit_Texte(False, Min, Position );
                            Haut_Calcul := lGraph.CalculateTextHeight( A_Tester,
                                lCadre,
                                True,
                                True );

                            IF ( Haut_Calcul >= Reste_Hauteur ) THEN
                            BEGIN
                                IF Meme_Page THEN
                                BEGIN
                                    A_Tester := Reconstruit_Texte(False, Min, Position - 1 );
                                    //A_Tester := Copy( la_Chaine, 0, Position - 1 );
                                    Min := Max; // Pour sortir
                                END
                                ELSE
                                    Max := Position; // On reduit
                            END
                            ELSE
                            BEGIN
                                Inc( Position );
                                Meme_Page := True;
                            END; //  Fin de IF
                        UNTIL ( ( Max - Min ) <= 2 );   //  Fin de REPEAT

                        // On imprime "a_tester"
                        lGraph.DrawText(
                            lCadre,
                            A_Tester,
                            True,
                            gtaLeading,
                            gtaLeading,
                            gttWord, 0, -1, -1, True );

                        // On récupère le "Reste" pour la nouvelle page
                        Commence := True;
                        Min := Position;
                        Reste := Reconstruit_Texte(True, Min, Lst_Mot.Count - 1 );
                        //Reste := Copy( la_Chaine, Position, Length( la_Chaine ) );
                        la_Chaine := Reste;

                        Pdf.NewPage;
                        Filigrane( ya_Calcul );
                        lCadre := Page_Active;
                        Hauteur := lCadre.Top;

                        Min := 0;
                        Max := Length( la_Chaine );
                        Meme_Page := False;
                        Reste_Hauteur := Page_Fin - Hauteur;

                    END; //  Fin de IF
                END; //  Fin de WHILE
                Result := Hauteur;
            FINALLY
                Lst_Mot.Free;
                Lst_Tag.Free;
                lst_Tag_Actif.Free;
            END; //  Fin de FINALLY

        END; //  Fin de IF
    END; // Fin de FUNCTION