Zones de texte de hauteur variable pour l'impression

maestricmaestric Membre
18:58 modifié dans API AppKit #1
Voila, j'ai un tableau (NSArray) de dictionnaires (NSDictionnary) de chaà®nes (NSString). Je voudrais l'imprimer avec un dictionnaire par ligne. Dans le dictionnaire, j'ai un champ (NSString) qui peut-être très long et qui demandera donc plusieurs lignes.

Comment avoir la hauteur du NSRect dans lequel je vais dessiner ce NSString ? La hauteur pour une ligne est 30, pour 2 -> 60, etc Mais comment avoir le nombre de lignes ??  B)

Réponses

  • 18:58 modifié #2
    Je ne connais pas de moyen simple pour faire ce que tu demandes, les méthodes simples permettant d'avoir la taille d'une string ne marchant que pour une seule ligne.

    La solution la plus facile facile à  mettre en ½uvre serait de générer du HTML, que tu places dans une WebView et c'est cette webview que tu imprimes. Sinon il est possible de générer un NSTextSortage, mais tu seras limité si tu veux afficher des tableaux (à  moins de faire du Tiger Only).
  • AliGatorAliGator Membre, Modérateur
    18:58 modifié #3
    dans 1134730972:

    Voila, j'ai un tableau (NSArray) de dictionnaires (NSDictionnary) de chaà®nes (NSString). Je voudrais l'imprimer avec un dictionnaire par ligne. Dans le dictionnaire, j'ai un champ (NSString) qui peut-être très long et qui demandera donc plusieurs lignes.

    Comment avoir la hauteur du NSRect dans lequel je vais dessiner ce NSString ? La hauteur pour une ligne est 30, pour 2 -> 60, etc Mais comment avoir le nombre de lignes ??  B)
    Une solution proposée par Matt Neuburg :
    NSTextView* t =<br />&nbsp; [[NSTextView alloc] initWithFrame: NSMakeRect(0,0,width,0.1)];<br />[[t textStorage] setAttributedString: s];<br />[t sizeToFit];<br />// now fetch [t frame].size.height; don&#39;t forget to release t
    
    Sinon il parait qu'il y a de quoi faire dans NSStringDrawing
    (/System/Library/Frameworks/AppKit.framework/Versions/C/Headers/NSStringDrawing.h
  • 18:58 modifié #4
    Ce qu'il y a dans NSStringDrawing ne marche que pour une seule ligne.
  • maestricmaestric Membre
    décembre 2005 modifié #5
    Pour avoir la hauteur d'un champ de texte d'une largeur donnée pour une NSString  donnée, j'ai essayé deux méthodes :

    Celle de AliGator :

    <br />- (float) heightForStringDrawing:(NSString *)aString withFont:(NSFont *)aFont andWitdh:(float)myWidth	<br />	NSTextView* t = [[[NSTextView alloc] initWithFrame: NSMakeRect(0, 0, myWidth, 0.1)] autorelease];<br />	<br />	NSMutableDictionary *attributes = [[[NSMutableDictionary alloc] init] autorelease];<br />	[attributes setObject:[NSFont fontWithName:@&quot;Georgia&quot; size:10] forKey:NSFontAttributeName];<br />	[attributes setObject:[NSColor blackColor] forKey:NSForegroundColorAttributeName];<br />	<br />	[[t textStorage] setAttributedString:[[NSAttributedString alloc] initWithString:aString attributes:attributes]];<br />	[t sizeToFit];<br /><br />	return [t frame].size.height;<br />}<br />
    


    et celle dans la doc d'Apple à  propos de NSLayout :

    <br />- (float) heightForStringDrawing:(NSString *)aString withFont:(NSFont *)aFont andWitdh:(float)myWidth<br />{<br />	NSTextStorage *textStorage = [[[NSTextStorage alloc] initWithString:aString] autorelease];<br />	NSTextContainer *textContainer = [[[NSTextContainer alloc] initWithContainerSize:NSMakeSize(myWidth,150)] autorelease];<br />	NSLayoutManager *layoutManager = [[[NSLayoutManager alloc] init] autorelease];<br />	[layoutManager addTextContainer:textContainer];<br />	[textStorage addLayoutManager:layoutManager];<br />	<br />	[textStorage addAttribute:NSFontAttributeName value:aFont range:NSMakeRange(0,[textStorage length])];<br />	[textContainer setLineFragmentPadding:0.0];<br />	<br />	(void) [layoutManager glyphRangeForTextContainer:textContainer];<br />	return [layoutManager usedRectForTextContainer:textContainer].size.height;	<br />}<br />
    


    Les deux renvoient strictement la même valeur, mais quand je dessine, la valeur est trop petite ! Il manque toujours une partie de la dernière ligne ! Ci-dessous, j'ai dessiné les rectangles conteneurs.


    NSTextView_MauvaiseHauteur.jpg


    Avez-vous déjà  eu un problème similaire ? :'(
  • maestricmaestric Membre
    décembre 2005 modifié #6
    J'ai fait une petite appli juste pour résoudre ce problème de NSTextView : comment trouver la bonne hauteur ? Elle implémente les deux méthodes précédemment citées (qui donnent bien la même hauteur). Sur une ligne, c'est impeccable. Mais plus on ajoute des lignes plus il y a un décalage !

    Voilà  l'appli : http://www.maestric.com/shared/cocoa/NSTextViewPrinting.zip
    et une capture d'écran. Si vous avez le temps d'y jeter un coup d'½il..  ;)

    NSTextViewPrinting_ScreenShot.jpg
  • maestricmaestric Membre
    janvier 2006 modifié #7
    N'ayant pas obtenu de réponse vraiment satisfaisante, j'ai lancé un topic à  ce propos sur la mailing-list Cocoa d'Apple. Il est ici :
    http://www.cocoabuilder.com/archive/message/cocoa/2006/1/3/153626

    En résumé, voilà  une fonction qui marche parfaitement, qui renvoie pour
    - une chaine
    - une police
    - une largeur
    la hauteur optimale d'un rectangle où est inscrit la chaà®ne écrite dans la police donnée.

    // height for a fixed width attributed string (from Apple Text NSLayout Programming Guide)<br />- (float) heightForStringDrawing:(NSString *)aString withFont:(NSFont *)aFont andWitdh:(float)myWidth<br />{<br />	NSTextStorage *textStorage = [[[NSTextStorage alloc] initWithString:aString] autorelease];<br />	NSTextContainer *textContainer = [[[NSTextContainer alloc] initWithContainerSize:NSMakeSize(myWidth,1e7)] autorelease];<br />	NSLayoutManager *layoutManager = [[[NSLayoutManager alloc] init] autorelease];<br />	[layoutManager addTextContainer:textContainer];<br />	[textStorage addLayoutManager:layoutManager];<br />	<br />	[textStorage addAttribute:NSFontAttributeName value:aFont range:NSMakeRange(0,[textStorage length])];<br />	[textContainer setLineFragmentPadding:0.0];<br />	<br />	[layoutManager setTypesetterBehavior:NSTypesetterBehavior_10_2_WithCompatibility];<br />	<br />	(void) [layoutManager glyphRangeForTextContainer:textContainer];	<br />	return [layoutManager usedRectForTextContainer:textContainer].size.height;	<br />}
    
  • ClicCoolClicCool Membre
    janvier 2008 modifié #8
    Bonjour à  Tous  <3 <br />
    Je déterre ce thread car j'ai aussi des soucis avec le sizeToFit qui fixe une hauteur trop petite.

    J'ai donc une NSView (connectée à  un IBOutlet nommé printView) Off-Screen, bâtie sous I.B., contenant divers "objets décoratifs" des NSTextFields d'information et une NSTextView (connectée à  un IBOutlet nommé printText) centrale qui doit pouvoir s'entendre sur plusieurs pages si besoin est.

    La TextView est bindée (bien sur) sur un NSData et je dois donc en recalculer la hauteur pour qu'elle puisse s'étendre sur le nombre de pages voulues et j'utilise pour ça -(void) sizeToFit de NSTextView.
    et ... je me suis cassé la tête avec la fin du texte manquante, sizeToFit ayant l'air de travailler comme un pied (et devrait donc s'appeler sizeToFeet )
    Puis je me suis rendu compte que seule la première impression était tronquée en bas ... !?!
    (Pourtant je faisais bien chaque fois un display juste après le sizeToFeet)

    J'ai alors bidouillé comme un porc mon code pour essayer de comprendre ce que je loupais, en pure perte car je ne comprend pas et me tourne donc vers vous.

    En fait un seul appel à  sizeToFit suffit mais suivi par deux séries d'appel à  -(void) display de NSView suivi d'un redimensionnement de la printView contenant la NSTextView et j'arrive alors à  l'exacte dimension souhaitée.

    Bizarrement les appels à  display sur la textView semble modifier sa frame ???

    Donc ce code marche mais le soucis c'est que:
    - 1 j'aimerai comprendre ce qui se passe et pourquoi
    - 2 je ne peut pas utiliser un code aussi pourris dans mon projet sans risquer qu'à  l'avenir ça foire sans que je comprenne pas non plus ce qui ne va plus

    Voici mon code:
    Je calcule d'abord la taille à  ajouter à  celle de ma TextView pour la page
    - (void) print: (id) sender {<br />	<br />	NSRect  frame, scrollerFrame, pageFrame;<br />	// ***** Calcul du Chapô de première page + Pied de la dernière page (frame.origin.y)<br />	scrollerFrame = [[[printText superview] superview]frame]; // Fame de la ScrollView contenant la TextView  (printText)<br />	pageFrame = [printView frame];<br />	int hauteurChapo = pageFrame.size.height - scrollerFrame.size.height; // - scrollerFrame.origin.y;
    


    Puis voilà  la "tambouille efficace mais si laide
    frame = [printText frame];<br />	frame.size.height = 38;	// Force la réduction au minimum de la hauteur de la zone texte<br />							// SizeToFit semblant capable d&#39;agrandir la zone si nécessaire mais pas de la réduire ... ?<br />	[printView setFrame:frame];         &nbsp; // La TextView suivra le mouvement par le jeu des printView et du autoresizes subViewq<br />	<br />	[printText sizeToFit]; // SIZETOFIT<br />	<br />	frame = [printText frame];<br />	pageFrame.size.height = frame.size.height + hauteurChapo;<br />	[printView setFrame:pageFrame]; // RESIZE de la PAGE selon le résultat du sizeToFit de la texteView<br />	<br />	frame = [printText frame]; // Frame trop petite malgré le sizeToFit<br />	pageFrame = [printView frame];<br />	NSLog (@&quot;-1-TextView&#39;s Height: %f  | PageVIEW&#39;s Height: %f&quot;, frame.size.height, pageFrame.size.height);<br />		<br />	<br />	[printText display]; //&lt;--	ça semble modifier la &quot;frame&quot; de la textView !! Pourquoi ?<br />	<br />	frame = [printText frame]; // Frame &quot;PRESQUE bonne&quot;<br />	pageFrame = [printView frame];<br />	NSLog (@&quot;-2-TextView&#39;s Height: %f  | PageVIEW&#39;s Height: %f&quot;, frame.size.height, pageFrame.size.height);<br />	<br />	frame = [printText frame];<br />	pageFrame.size.height = frame.size.height + hauteurChapo;<br />	[printView setFrame:pageFrame]; // RESIZE de la PAGE Encore une fois avec la nouvelle hauteur de la Texte View<br /><br />	<br />	frame = [printText frame]; <br />	pageFrame = [printView frame];<br />	NSLog (@&quot;-3-TextView&#39;s Height: %f  | PageVIEW&#39;s Height: %f&quot;, frame.size.height, pageFrame.size.height);<br />	<br />	[printText display]; //&lt;--	ça semble ENCORE modifier la &quot;frame&quot; de la textView !!<br />						//		Et cette fois on dirait que c&#39;est la bonne ?? (espérons)<br />	<br />	frame = [printText frame];<br />	pageFrame = [printView frame];<br />	NSLog (@&quot;-4-TextView&#39;s Height: %f  | PageVIEW&#39;s Height: %f&quot;, frame.size.height, pageFrame.size.height);<br />	<br />	frame = [printText frame];<br />	pageFrame.size.height = frame.size.height + hauteurChapo;<br />	[printView setFrame:pageFrame]; // RESIZE de la PAGE ENCORE une (dernière ?) fois avec la nouvelle hauteur de la Texte View<br />	NSLog (@&quot;-5-TextView&#39;s Height: %f  | PageVIEW&#39;s Height: %f&quot;, frame.size.height, pageFrame.size.height);<br />	<br />	<br />	NSPrintOperation *pop = [NSPrintOperation printOperationWithView:printView];
    


    et voici les Logs avec un texte de 24 pages.
    On y retrouve 3 hauteurs différentes de la textView à  la suite d'un seul appel à  sizeToFit:
    2008-01-24 21:05:50.203 Patientelle[911:10b] -1-TextView&#39;s Height: 14210.000000  | PageVIEW&#39;s Height: 14323.000000<br />2008-01-24 21:05:50.248 ClicConsult[911:10b] -2-TextView&#39;s Height: 16954.000000  | PageVIEW&#39;s Height: 14323.000000<br />2008-01-24 21:05:50.248 ClicConsult[911:10b] -3-TextView&#39;s Height: 16954.000000  | PageVIEW&#39;s Height: 17067.000000<br />2008-01-24 21:05:50.262 ClicConsult[911:10b] -4-TextView&#39;s Height: 17052.000000  | PageVIEW&#39;s Height: 17067.000000<br />2008-01-24 21:05:50.262 ClicConsult[911:10b] -5-TextView&#39;s Height: 17052.000000  | PageVIEW&#39;s Height: 17165.000000
    


    Quelqu'un a-t-il une inspiration là  dessus j'avoue que je n'ai pas codé depuis des mois et ne m'étais jamais encore essayé à  l'impression et que sans doutes quelque chose m'échappe là Â  :-\\


    MERCI et encore BON JOUR à  TOUS  :p content de revenir par ici  :p


    [EDIT] Correction d'un bug dans mon code
  • Philippe49Philippe49 Membre
    18:58 modifié #9
    J'ai eu ce même problème ici

    je n'ai pas vraiment trouvé de réponse entièrement satisfaisante. Voir l'article IKImageBrowserView.

    dans 1201206644:

    et ... je me suis cassé la tête avec la fin du texte manquante, sizeToFit ayant l'air de travailler comme un pied (et devrait donc s'appeler sizeToFeet )


    C'est sans doute un peu d'énervement  >:)   ;D
  • ClicCoolClicCool Membre
    18:58 modifié #10
    Salut Philippe,

    Oui j'avais aussi lu ce thread.

    Mon bidouilli marche toutefois et tend à  prouver que, in finé, sizeToFit marche bien car je n'ai à  l'appeler qu'une seule fois.

    Par contre je ne comprend pas pourquoi il faut 3 appels à  display et 2 adaptations de la taille de la View contenant la TextView pour qu'enfin la hauteur obtenue soit exacte ... ?
  • Philippe49Philippe49 Membre
    18:58 modifié #11
    dans 1201209638:

    Mon bidouilli marche toutefois et tend à  prouver que, in finé, sizeToFit marche bien car je n'ai à  l'appeler qu'une seule fois.
    Par contre je ne comprend pas pourquoi il faut 3 appels à  display et 2 adaptations de la taille de la View contenant la TextView pour qu'enfin la hauteur obtenue soit exacte ... ?


    Ce serait pas un setNeedsDisplay à  bien cibler ? la scroolview, la clipView, la textView, ou la superview ?
  • ClicCoolClicCool Membre
    18:58 modifié #12
    dans 1201253875:


    Ce serait pas un setNeedsDisplay à  bien cibler ? la scroolview, la clipView, la textView, ou la superview ?


    C'est une idée, je n'ai pas essayé d'arroser toute la hierachie des views avec un Display.
    Je m'y mets de suite.
    (j'essaies pas avec un setNeddsDisplay car ma méthode ne rendra pas la main à  l'event-loop avant de lancer l'impression)
  • ClicCoolClicCool Membre
    18:58 modifié #13
    dans 1201253875:
    la scroolview, la clipView, la textView, ou la superview ?


    Elles y sont toutes passées et rien n'y fait !

    Faut toujours qu'après le sizeToFit de la textlView, je lui envoie un Display puis re-dimensionne la pageView selon la supposée hauteur puis renvoie un Display à  la TextView et re-dimensionne la pageView, puis renvoie un dernier Display à  la TextView et re-dimensionne la pageView une dernière fois !!
    Moyennant quoi les impressions sont impeccables y compris pour une textView de plus de 60 pages.

    Et d'un autre côté, si j'imprime directement la textView seule, un sizeToFit seul (sans aucun appel à  Display etc.) marche impeccable.

    L'imbroglio doit donc en effet se trouver dans la hiérarchie des vues plus qu'ailleurs.
    Merci de cette inspiration, Je vais aller voir un peu du côté des Resizing-masks.

    Si je ne parvient pas à  comprendre pourquoi, je crois que je finirais par bâtir tout ça dans une webView en espérant y trouver une solution pérenne...
  • ClicCoolClicCool Membre
    18:58 modifié #14
    ET bien j'ai cherché du côté des Resizing-masks et du "autoresizes subViews" sans succès

    Par contre je corrige mon code ci-dessus.
    C'est pas la textView qu'il faut "rapetisser" au début mais la pageView contenant la textView (le textView suivant le mouvement par le jeu des Resizing-masks justement.
    Sinon au bout de quelques impressions tout se décale :(
Connectez-vous ou Inscrivez-vous pour répondre.