create changelog entry
[debian/openrocket] / core / src / net / sf / openrocket / gui / print / visitor / TransitionStrategy.java
1 package net.sf.openrocket.gui.print.visitor;
2
3 import com.itextpdf.text.Document;
4 import com.itextpdf.text.DocumentException;
5 import com.itextpdf.text.Rectangle;
6 import com.itextpdf.text.pdf.PdfWriter;
7 import net.sf.openrocket.gui.print.AbstractPrintable;
8 import net.sf.openrocket.gui.print.ITextHelper;
9 import net.sf.openrocket.gui.print.PrintUnit;
10 import net.sf.openrocket.gui.print.PrintableNoseCone;
11 import net.sf.openrocket.gui.print.PrintableTransition;
12 import net.sf.openrocket.logging.LogHelper;
13 import net.sf.openrocket.rocketcomponent.NoseCone;
14 import net.sf.openrocket.rocketcomponent.RocketComponent;
15 import net.sf.openrocket.rocketcomponent.Transition;
16 import net.sf.openrocket.startup.Application;
17
18 import java.awt.image.BufferedImage;
19 import java.util.List;
20 import java.util.Set;
21
22 /**
23  * A strategy for drawing transition/shroud/nose cone templates.
24  */
25 public class TransitionStrategy {
26
27     /**
28      * The logger.
29      */
30     private static final LogHelper log = Application.getLogger();
31
32     /**
33      * The iText document.
34      */
35     protected Document document;
36
37     /**
38      * The direct iText writer.
39      */
40     protected PdfWriter writer;
41
42     /**
43      * The stages selected.
44      */
45     protected Set<Integer> stages;
46
47     /**
48      * Strategy for fitting multiple components onto a page.
49      */
50     protected PageFitPrintStrategy pageFitPrint;
51
52     /**
53      * Constructor.
54      *
55      * @param doc              The iText document
56      * @param theWriter        The direct iText writer
57      * @param theStagesToVisit The stages to be visited by this strategy
58      */
59     public TransitionStrategy(Document doc, PdfWriter theWriter, Set<Integer> theStagesToVisit, PageFitPrintStrategy pageFit) {
60         document = doc;
61         writer = theWriter;
62         stages = theStagesToVisit;
63         pageFitPrint = pageFit;
64     }
65
66     /**
67      * Recurse through the given rocket component.
68      *
69      * @param root      the root component; all children will be visited recursively
70      * @param noseCones nose cones are a special form of a transition; if true, then print nose cones
71      *
72      * @return true if a transition/nosecone was rendered
73      */
74     public boolean writeToDocument(final RocketComponent root, boolean noseCones) {
75         List<RocketComponent> rc = root.getChildren();
76         return goDeep(rc, noseCones);
77     }
78
79
80     /**
81      * Recurse through the given rocket component.
82      *
83      * @param theRc     an array of rocket components; all children will be visited recursively
84      * @param noseCones nose cones are a special form of a transition; if true, then print nose cones
85      *
86      * @return true if a transition/nosecone was rendered
87      */
88     protected boolean goDeep(final List<RocketComponent> theRc, boolean noseCones) {
89         for (RocketComponent rocketComponent : theRc) {
90             if (rocketComponent instanceof NoseCone) {
91                 if (noseCones) {
92                     return render((Transition) rocketComponent);
93                 }
94             }
95             else if (rocketComponent instanceof Transition && !noseCones) {
96                 return render((Transition) rocketComponent);
97             }
98             else if (rocketComponent.getChildCount() > 0) {
99                 return goDeep(rocketComponent.getChildren(), noseCones);
100             }
101         }
102         return false;
103     }
104
105     /**
106      * The core behavior of this visitor.
107      *
108      * @param component the object to extract info about; a graphical image of the transition shape is drawn to the
109      *                  document
110      *
111      * @return true, always
112      */
113     private boolean render(final Transition component) {
114         try {
115             AbstractPrintable pfs;
116             if (component instanceof NoseCone) {
117                 pfs = new PrintableNoseCone((NoseCone) component);
118             }
119             else {
120                 pfs = new PrintableTransition(component);
121             }
122
123             java.awt.Dimension size = pfs.getSize();
124             final Dimension pageSize = getPageSize();
125             if (fitsOnOnePage(pageSize, size.getWidth(), size.getHeight())) {
126                 pageFitPrint.addComponent(pfs);
127             }
128             else {
129                 int off = (int) (PrintUnit.POINTS_PER_INCH * 0.3f);
130                 pfs.setPrintOffset(off, off);
131                 BufferedImage image = (BufferedImage) pfs.createImage();
132                 ITextHelper.renderImageAcrossPages(new Rectangle(pageSize.getWidth(), pageSize.getHeight()),
133                         document, writer, image);
134                 document.newPage();
135             }
136         }
137         catch (DocumentException e) {
138             log.error("Could not render the transition.", e);
139         }
140         return true;
141     }
142
143     /**
144      * Determine if the image will fit on the given page.
145      *
146      * @param pageSize the page size
147      * @param wImage   the width of the thing to be printed
148      * @param hImage   the height of the thing to be printed
149      *
150      * @return true if the thing to be printed will fit on a single page
151      */
152     private boolean fitsOnOnePage(Dimension pageSize, double wImage, double hImage) {
153         double wPage = pageSize.getWidth();
154         double hPage = pageSize.getHeight();
155
156         int wRatio = (int) Math.ceil(wImage / wPage);
157         int hRatio = (int) Math.ceil(hImage / hPage);
158
159         return wRatio <= 1.0d && hRatio <= 1.0d;
160     }
161
162     /**
163      * Get the dimensions of the paper page.
164      *
165      * @return an internal Dimension
166      */
167     protected Dimension getPageSize() {
168         return new Dimension(document.getPageSize().getWidth(),
169                 document.getPageSize().getHeight());
170     }
171
172     /**
173      * Convenience class to model a dimension.
174      */
175     class Dimension {
176         /**
177          * Width, in points.
178          */
179         public float width;
180         /**
181          * Height, in points.
182          */
183         public float height;
184
185         /**
186          * Constructor.
187          *
188          * @param w width
189          * @param h height
190          */
191         public Dimension(float w, float h) {
192             width = w;
193             height = h;
194         }
195
196         /**
197          * Get the width.
198          *
199          * @return the width
200          */
201         public float getWidth() {
202             return width;
203         }
204
205         /**
206          * Get the height.
207          *
208          * @return the height
209          */
210         public float getHeight() {
211             return height;
212         }
213     }
214 }