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