[Résolu] Impression "à la publipostage"
Mick
Membre
Bonjour à tous,
Voilà mon prolème et je ne sais pas par quel bout le prendre :
J'ai une tableView dont le contenu dépend de la sélection d'un arrayController (lui-même bindé à une autre tableView, structure classique). L'utilisateur, en changeant la sélection, se voit donc afficher un certain contenu dans ma fameuse tableView.
Maintenant, je cherche à imprimer un "état" qui contiendrait autant de copie de la tableView qu'il y a de possibilité de sélection, autrement dit créer une vue imprimable qui contiendrai un certain nombre de fois la table mais avec les contenus différents correspondant à toutes les sélections possibles.
Ma question : comment commencer pour que ce ne soit pas trop "douloureux" en code ?
Voilà mon prolème et je ne sais pas par quel bout le prendre :
J'ai une tableView dont le contenu dépend de la sélection d'un arrayController (lui-même bindé à une autre tableView, structure classique). L'utilisateur, en changeant la sélection, se voit donc afficher un certain contenu dans ma fameuse tableView.
Maintenant, je cherche à imprimer un "état" qui contiendrait autant de copie de la tableView qu'il y a de possibilité de sélection, autrement dit créer une vue imprimable qui contiendrai un certain nombre de fois la table mais avec les contenus différents correspondant à toutes les sélections possibles.
Ma question : comment commencer pour que ce ne soit pas trop "douloureux" en code ?
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Pour avoir essayé (et réussit , au début c'est quand même un peu douloureux "en code".
il faut user du NSLayOutManager, des NSTextTable et autre NSTextTableBlock, et il n'y a pas beaucoup de doc (la démo Expenses envoie dans le presse papier sans dessiner !) mais si tu as déjà fait des tableaux en html c'est assez comparable.
D'ailleurs dans mes recherches, au début, c'était la solution la plus souvent recommandée, et pourquoi pas après tout.
Ensuite tu le passes à une WebView et le tour est joué ..
Mais bon, j'ai préféré apprendre à utiliser NSLayoutManager et imprimer ma vue tout seul car le tableau n'était qu'une partie d'un tout et j'apprenais à imprimer au pixel près sur ma feuille via "drawAtPoint:" ...
Je venais d'avoir fini quand je suis tombé sur un excellent travail http://themikeswan.wordpress.com/2010/07/31/printing-tabular-data/
Qui fait une classe avec tout ce qu'il faut pour envoyer les données et récupérer sa table prête à dessiner dans une NSAttributedString, y' a plus qu'à .
Je te laisse découvrir, je ne l'ai pas implémenté moi même puisque j'ai ma solution, mais non, avec un peu de persévérance il n'y a rien de si lourd une fois qu'on a compris comment ça marche... Plus le confort de maà®triser bien les choses
Si tu veux quelque chose de vraiment rapide tu essayes de configurer une VueTableau dans IB pour qu'elle ne soit pas trop vilaine sur papier et tu l'ouvres dans Le pdf reader par défaut en laissant la charge de l'impression à l'utilisateur.
Tu peux reprendre la VueTableau ou la ScrollView qui est autour (après l'avoir customisée aussi) ou même une boite dans laquelle tu auras d'autres champs. Tu déclares un IBOutlet NSView *printBox et tu le lies dans IB à la XXXView que tu veux voir imprimer..
A toi de voir ce qui t'intéresse, mais à ma connaissance on en est là ...
Merci pour ton lien, mais j'avoue ne pas comprendre comment fonctionne une NSTextTable.
De même, comment créer une vue perso qui contiendra n fois ma table avec des données différentes ?
Pour l'instant j'ai démarré en créant une boucle qui passe en revue ce qui m'intéresse pour créer mes tables, et dans laquelle je compte créer mes NSTableView, mais là problème : je les remplis comment ?? Normalement, une table est peuplée via une dataSource, mais là je ne vais pas m'amuser à créer une datasource pour chaque version de ma table ... si ?
[EDIT]
Après décorticage du code de la classe, je crois à peu près comprendre qu'une textTable peut se peupler manuellement en fait. Je me plonge là -dedans
[/EDIT]
Si tu as déja bouclé sur tes tables tu n'as qu'à en faire des tableaux de strings et les envoyer à MSPrintTable pour récupérer une NSAttributedString que tu imprimeras ensuite via une textView créée à pour l'occasion dans une NSPrintOperation.
Tu étais prévenu que ce n'était pas si simple que peupler une NSTableView via les bindings
Par contre une fois qu'on a saisi c'est pas non plus si compliqué, enfin, il m'a semblé.. Et pas non plus des tonnes de code [EDIT]<= Celui là je me suis longtemps demandé où on pouvait le caser
Au cas ou: tu peux juger du résultat de tes impressions en demandant Aperçu dans le dialogue d'impression, ça épargnera quelques bouts d'arbre et un peu d'encre pour des impressions pas forcément destinées à la postérité
Bon, je vais un peu galérer pour le MSPrintTable (je suis pas en obj C 2.0 Tiger powerPC...) il faut que j'écrive les accesseurs (@synthetize, bah ça n'existe pas chez moi...)
Je vais creuser les textTable, je ne vois pas d'autres solutions.
Merci à toi Laudema
Hop, un petit coup de main de Xcode: tu vas dans le menu AppleScript (à côté du menu Aide) et tu descends jusque code après avoir au préalable sélectionné tes variables dans l'interface. Tu le connaissais pas celui là ?
Sinon tu n'es pas obligé de tout prendre. Tu dois juste instancier une NSTextTable avec ses attributs et autant de NSTextTableBlock qu'il y a de cellules à remplir mais tu fais ça dans une boucle à la html, rien de folichon mais rien d'exceptionnel non plus.
Si MSTablePrint est trop copieux pour toi tu peux regarder à ce qu'il y a dans la démo iSpend (fais une recherche dans la doc Xcode) le doc est dans la classe MyDocument_Pasteboard (excuses, hier soir j'avais cité Expenses de mémoire) et regarde la méthode - (NSAttributedString *)attributedStringFromTransactions:(NSArray *)transactions et la méthode qu'elle appelle: addCell.
Tu pourras revenir à Mike Swan quand tu voudras regarder comment faire pour imprimer une NSAttributedString ..
En fait je viens enfin de comprendre qu'au bout du compte le but est de créer une NSAttributeString immense, découpée en morceaux, chaque moreceau ayant un attribut NSParagrapheStyle. C'est dans ces paragraphStyle que l'on dit dans quelle(s) cellule(s) le texte doit être en faisant un setTextBlock, et enfin c'est dans le textBlock qu'on associe à la NSTextTable et à la bonne colonne/bonne ligne.
Du coup, j'ai enfin réussit à construire mes moults tableaux. Le petit soucis, c'est : comment récupérer la taille exacte de la NSTextTable afin de dimensionner correctement une NSTextView ? A l'arrache (en faisant la somme de la taille des colonnes) j'ai réussi à afficher une table correctement prête à imprimer, mais je voudrais calculer ça bien...
NSAttributedString a une méthode size (dans la catégorie stringAdditions) mais je suis pas certain qu'elle donne de bons résultats pour une table, tu peux toujours l'essayer et les autres méthodes de calculs de taille http://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSString_AppKitAdditions/Reference/Reference.html#//apple_ref/doc/uid/20000155
Mais tu trouveras probablement mieux sur Google.
[Edit]
Bingo ?
http://stackoverflow.com/questions/2282629/how-to-get-height-for-nsattributedstring-at-a-fixed-width
donne deux liens très intéressants
http://www.sheepsystems.com/sourceCode/sourceStringGeometrics.html pour une démo d'une catégorie qui permet de faire des calculs de ce type
http://teilweise.tumblr.com/post/293948288/boundingrectwithsize-options-attributes qui explique que pour faire ça la méthode à utiliser est donc bien celle de NSAttributedString de NSStringAdditions boundingRectWithSize:options:attributes: http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSString_AppKitAdditions/Reference/Reference.html
en utilisant l'option NSStringDrawingUsesLineFragmentOrigin http://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSString_AppKitAdditions/Reference/Reference.html#//apple_ref/doc/c_ref/NSStringDrawingUsesLineFragmentOrigin
J'ai vu la méthode boundingrectwithsize, mais je ne voyais pas l'intérêt, vu que la méthode demande... un size !!
Apparemment, il suffit de mettre NSZeroSize !
Je vais tester ça.
Merci pour le lien (tu avoueras quand même qu'il faut les trouver les méthodes utiles ! c'est quand même pas super immédiat)
PS : tu vois que tu as pris goût à [Edit] !
Dans mon code à moi j'ai ça pour calculer la NSSize
Mais je ne l'avais pas retrouvé tout de suite, et comme avoir une idée des méthodes disponibles peut être utile je n'ai pas eu de scrupules à te renvoyer à la référence ;/
Oui, j'ai vraiment apprécié J J Rousseau le jour où j'ai lu sa description de l'esprit d'escalier, je m'y suis tant reconnu ..
J'ai finalement réussi à créer une NSAttributedString géante. J'ai créé une NSTextView et lui ai refilé la string.
Un printOperation me donne le résultat escompté... sauf que le découpage n'est pas correct pour les pages..
Quel est l'usage pour gérer le problème des "sauts de pages" ? Je ne sais pas où chercher.
Est-il correct d'utiliser une textView comme vue pour l'impression ? faut-il créer une vue perso ?
pas facile les problèmes d'impression....
Tu dois gérer toi même le découpage. Si ta vue est trop grande tu devras chercher le nombre de lignes maximales imprimables dans une page et dessiner autant de fois que nécessaire ta vue. Tout ça est assez bien expliqué dans le "Programmation Cocoa sous Mac OS X" de Hillegass (Chapitre 27, page 338 Gérer la pagination). Sinon tu as aussi la documentation Apple http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Printing/Concepts/pagination.html#//apple_ref/doc/uid/20001051-BBCHHAHI si tu veux "rapetisser" ta vue ou la "clipper" pour la laisser sur une page. http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Printing/Tasks/PaginatingViews.html#//apple_ref/doc/uid/20000912-BBCHHAHI si tu veux imprimer plusieurs pages.
Finalement j'ai fait une subclass de NSTextView. Je lui passe l'immense attributedString.
J'override les méthodes suivantes :
Et ..... Le découpage des pages ne marche pas. En plus, il me met une erreur du type
M'y prends-je comme un pied ? Au débug, ma hauteur de tableau semble correcte, le nombre de tableaux par page est correct, le nombre de page est correct.... les rectangles envoyés semblent aussi correct (la hauteur est bien nbreTableauParPage*hauteurDUnTableau). Et pourtant, l'aperçu me montre quelque chose qui n'a rien à voir ! On dirait qu'une autre méthode rectForPage est appelée après la mienne !
ça semble bizarre que ta textView n'imprime pas correctement ton tableau. Si je crée un tableau dans TextEdit et que le fais s'imprimer les sauts de page sont gérés correctement, je ne vois pas pourquoi il en irait autrement.
Essaye plutôt de définir pageRect en variable d'instance et de lui attribuer sa valeur dans le knowsPageRange. Je ne sais pas si ça changera quelque chose mais je l'ai toujours vu faire (et fait) comme ça. Pourquoi voudrais tu le faire varier, normalement il est toujours le même d'une page à l'autre ?
ta dernière phrase me laisse penser que je n'ai pas compris le rectForPage
J'ai compris que le rect retourné par rectForPage était le rectangle de ma view qui est sensé s'imprimer sur la page.
Si tu me dit que ce rectangle est sensé ne pas changer pour chaque page, c'est que je n'ai pas compris ce qu'est le rect à retourner...
Mais ce n'est pas le plus important car dans ton cas il ne variera que pour la dernière page.
Par contre, si tu essayes de mettre un point d'arrêt dessus, via le menu Run ->Show Breakpoints (commande-option , double click tout en bas pour ajouter un point d'arrêt -> rectForPage: avec les deux points, une sheet d'alerte se déplie et demande auquel particulièrement tu veux l'associer: celui qui est défini dans ton projet ou à -[NSTextView(NSPrivate) rectForPage] ou à [NSView(NSPrintig1) rectForPage] ce qui fait beaucoup de rectForPage susceptibles de se déclencher. Tu peux mettre des points d'arrêt avec un log ou un son différent pour chacun et voir lequel est appelé qui pourrait parasiter (probablement celui de la NSTextView).
Il se pourrait fort que tu te battes avec les automatismes de la NSTextView.
Peut être en changeant les paramètres d'impressions (du genre fitToPage) obtiendrais tu un résultat correct.
Ou alors tu te passe d'elle pour imprimer, tu la gardes juste pour son layoutManager - textContainer - textStorage et tu imprimes tes tableaux les uns après les autres en regardant s'il te reste de la place.
Voir ici
Il me semble que tu te bats contre les automatismes de la NSTextView, peut être devrais tu essayer différents paramètres d'impressions (du genre fitToPage) et laisser faire les automatismes.
Sinon il te faut repartir de ta NSAttributedString et utiliser les méthodes du NSLayoutManager pour la dessiner bout par bout, table par table si ce sont plusieurs tables. Mais le faire dans une sous classe de NSView plutôt que NSTextView. lien
Tu as quelques démos dans XCode, tapes LayoutManagerDemo dans le champ de recherche de la documentation et aussi TextViewConfig pour avoir des exemples de codes qui utilisent ces concepts, mais pas dans l'impression mais l'impression n'est qu'une option dans le dessin des vues..
Je pense effectivement que je dois me battre avec NSTextView.
Sinon, pour le principe, je veux imprimer un certain nombre de tableaux, et je veux les arranger pour en mettre le maximum sur une même page. Le problème est de déterminer le nombre de tableaux par page, ce que je fais en calculant floorf(hauteurPage/hauteurDUnTableau).
Après, c'est la conception de ces tableaux que je ne savais pas faire avant tes réponses (je ne connaissais pas les textTable). Donc j'ai opté pour la solution de créer une méga attributedString via une textTable (je sépare 2 tableau par une row vierge, je peux ajouter un titre à mon tableau en ajoutant une row sans bordures..). Là où je merdouille, par manque de connaissance de la gestion des textes, et malgré la lecture de la doc (je suis un peu paumé dans les différents objets), c'est d'afficher cette attributeString dans une vue. J'ai donc utilisé une textView. Le problème est qu'à l'impression il ne faut pas qu'un des tableaux (contenu dans le tableau géant) soit coupé lors d'un saut de page. j'ai donc overridé la méthode rectForPage et je lui fait retourner le rectangle de la textView perso qui contient juste pile poil les bons tableaux à imprimer sur la page.
Je pense qu'il faudrait que je me passe de la textView, mais je ne sais pas trop gérer les layoutManager et tout ce qui s'en suit. Je vais essayer de me plonger à fond dedans.
Une idée ?
C'est un peu effrayant à cause du nombre d'objets qui rentrent en jeu mais une fois qu'on est plongé dedans comme tu dis ça prend forme (c'est le cas de le dire ;-)
De toutes façons, que tu gardes ou pas ta NSTextView tu devrais te plonger dedans et si tu utilises une NSTextView les composants sont là déjà , selon la manière dont tu l'as créée
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/TextLayout/Tasks/DrawingStrings.html%23//apple_ref/doc/uid/20001808-DontLinkElementID_11
NSTextStorage est en fait une (sous classe de) NSAttributedString c'est lui qui retient le(s) layoutManager(s) et le(s) textContainer(s)
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/TextArchitecture/Tasks/AssembleSysByHand.html%23//apple_ref/doc/uid/20000843-CJBBIAAF
Dans ton cas il faudrait voir combien la NSAttributedString possède de containers en le demandant au layoutManager, peut être en met elle autant que de tableaux ? Il se peut aussi qu'elle rajoute un espace entre chaque tableau, au moins un retour à la ligne, faussant par là tes calculs.
Faute de temps je ne suis pas capable de vérifier mais il me semble qu'un bout de code intéressant existe dans la documentation avec la demo de TextEdit pour la méthode setHasMultiplePages
MultiplePageView est une classe qui semble aussi très prometteuse
Je regrette vraiment de n'avoir pas de temps pour y regarder plus. Mais arrivé là je pense que tu ne dois plus être loin du but et que désormais les impressions Cocoa n'auront plus de secrets pour toi
Du coup, cette vue n'affiche que ce qui est sur la page donnée. Ainsi, plus de problème de découpage "pile" d'une vue géante qu'on découperait en petits morceaux (un pour chaque page). Je me suis inspiré de l'exemple simple de notre cher Aaron..