Hi,
Following this post
Will the fix be applied for the use of TAdvGraphicsPDFEngine?
or include functions looks like "CalculateTextOverflow"
Regards
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
).
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