001: /*
002: * Copyright (c) 2007, intarsys consulting GmbH
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * - Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * - Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * - Neither the name of intarsys nor the names of its contributors may be used
015: * to endorse or promote products derived from this software without specific
016: * prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
022: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
028: * POSSIBILITY OF SUCH DAMAGE.
029: */
030: package de.intarsys.pdf.cds;
031:
032: import java.awt.geom.Rectangle2D;
033: import de.intarsys.pdf.cos.COSArray;
034: import de.intarsys.pdf.cos.COSFixed;
035: import de.intarsys.pdf.cos.COSNumber;
036:
037: /**
038: * The implementation of the pdf rectangle data type.
039: *
040: * <p>
041: * The specification of the data type is found in [PDF} chapter 3.8.3.
042: * </p>
043: *
044: * <p>
045: *
046: * <pre>
047: * A rectangle is defined by
048: * [llx, lly, urx, ury ]
049: * where
050: * llx = lower left x coordinate
051: * lly = lower left y coordinate
052: * urx = upper right x coordinate
053: * ury = upper right y coordinate
054: *
055: * urx
056: * |
057: * v
058: * +-----+ <-ury
059: * | |
060: * lly -> +-----+
061: * ˆ
062: * |
063: * llx
064: *
065: *
066: * </pre>
067: *
068: * An application should be prepared to get any two diagonally opposite corners
069: * in the rectangle specification. Use "normalize()" to ensure a rectangle that
070: * conforms to the above picture.
071: * </p>
072: */
073: public class CDSRectangle extends CDSBase {
074: /** Some common constants in rectangle */
075: static private final float INCH_2_CM = 2.54f;
076:
077: static private final float A4_WIDTH = 21.0f;
078:
079: static private final float A4_HEIGHT = 29.7f;
080:
081: static private final float DPI = 72.0f;
082:
083: /*
084: * paper sizes in mm A0 841 × 1189 B0 1000 × 1414 C0 17 × 1297 A1 594 × 841
085: * B1 707 × 1000 C1 648 × 917 A2 420 × 594 B2 500 × 707 C2 458 × 648 A3 297 ×
086: * 420 B3 353 × 500 C3 324 × 458 A4 210 × 297 B4 250 × 353 C4 229 × 324 A5
087: * 148 × 210 B5 176 × 250 C5 162 × 229 A6 105 × 148 B6 125 × 176 C6 114 ×
088: * 162 A7 74 × 105 B7 88 × 125 C7 81 × 114 A8 52 × 74 B8 62 × 88 C8 57 × 81
089: * A9 37 × 52 B9 44 × 62 C9 40 × 57 A10 26 × 37 B10 31 × 44 C10 28 × 40
090: */
091:
092: // todo 4 add some common paper sizes
093: static public final float[] SIZE_A4 = { 0, 0,
094: (A4_WIDTH / INCH_2_CM * DPI), (A4_HEIGHT / INCH_2_CM * DPI) };
095:
096: /**
097: * Create a {@link CDSRectangle} from an <code>array</code> holding the
098: * rectangle coordinates.
099: *
100: * @param array
101: * The base {@link COSArray}
102: *
103: * @return Create a {@link CDSRectangle} from <code>array</code>
104: */
105: static public CDSRectangle createFromCOS(COSArray array) {
106: CDSRectangle rect = (CDSRectangle) array
107: .getAttribute(CDSRectangle.class);
108: if (rect == null) {
109: rect = new CDSRectangle(array);
110: array.setAttribute(CDSRectangle.class, rect);
111: }
112: return rect;
113: }
114:
115: private Rectangle2D cachedRectangle;
116:
117: private Rectangle2D cachedNormalizedRectangle;
118:
119: /**
120: * CDTRectangle constructor comment.
121: */
122: public CDSRectangle() {
123: super (COSArray.createWith(0, 0, 0, 0));
124: }
125:
126: /**
127: * CDTRectangle constructor. Create a new rectangle with given array.
128: *
129: * @param newR
130: * A four dimensional COSArray defining llx,lly, urx, ury.
131: */
132: protected CDSRectangle(COSArray newR) {
133: super (newR);
134: }
135:
136: /**
137: * CDTRectangle constructor. Create a new rectangle with given size.
138: *
139: * @param llx
140: * the lower left x
141: * @param lly
142: * the lower left y
143: * @param urx
144: * the upper right x
145: * @param ury
146: * the upper right y
147: */
148: public CDSRectangle(float llx, float lly, float urx, float ury) {
149: super (COSArray.createWith(llx, lly, urx, ury));
150: }
151:
152: /**
153: * CDTRectangle constructor. Create a new rectangle with given array. The
154: * array must have 4 elements of type float.
155: *
156: * @param rectArray
157: * A four dimensional array defining llx,lly, urx, ury.
158: *
159: * @see CDSRectangle#CDSRectangle(float llx, float lly, float urx, float
160: * ury)
161: */
162: public CDSRectangle(float[] rectArray) {
163: this (rectArray[0], rectArray[1], rectArray[2], rectArray[3]);
164: }
165:
166: public CDSRectangle(Rectangle2D rect) {
167: super (COSArray.createWith((float) rect.getMinX(), (float) rect
168: .getMinY(), (float) rect.getMaxX(), (float) rect
169: .getMaxY()));
170: }
171:
172: /**
173: * <code>true</code> if x/y lies within this.
174: *
175: * @param x
176: * x coordinate to be checked.
177: * @param y
178: * y coordinate to be checked.
179: * @return <code>true</code> if x/y lies within this.
180: */
181: public boolean contains(double x, double y) {
182: COSArray array = cosGetArray();
183: float x0 = ((COSNumber) array.get(0)).floatValue();
184: float x1 = ((COSNumber) array.get(2)).floatValue();
185: if (x0 < x1) {
186: if ((x < x0) || (x > x1)) {
187: return false;
188: }
189: } else {
190: if ((x > x0) || (x < x1)) {
191: return false;
192: }
193: }
194: float y0 = ((COSNumber) array.get(1)).floatValue();
195: float y1 = ((COSNumber) array.get(3)).floatValue();
196: if (y0 < y1) {
197: return ((y >= y0) && (y <= y1));
198: } else {
199: return ((y >= y1) && (y <= y0));
200: }
201: }
202:
203: /**
204: * <code>true</code> if x/y lies within this, with a "uncertainty" of
205: * epsilon.
206: *
207: * @param x
208: * x coordinate to be checked.
209: * @param y
210: * y coordinate to be checked.
211: * @param epsilon
212: * The allowed range of uncertainty
213: *
214: * @return <code>true</code> if x/y lies within this.
215: */
216: public boolean contains(double x, double y, double epsilon) {
217: COSArray array = cosGetArray();
218: float x0 = ((COSNumber) array.get(0)).floatValue();
219: float x1 = ((COSNumber) array.get(2)).floatValue();
220: if (x0 < x1) {
221: if ((x < (x0 - epsilon)) || (x > (x1 + epsilon))) {
222: return false;
223: }
224: } else {
225: if ((x > (x0 + epsilon)) || (x < (x1 - epsilon))) {
226: return false;
227: }
228: }
229: float y0 = ((COSNumber) array.get(1)).floatValue();
230: float y1 = ((COSNumber) array.get(3)).floatValue();
231: if (y0 < y1) {
232: return ((y >= (y0 - epsilon)) && (y <= (y1 + epsilon)));
233: } else {
234: return ((y >= (y1 - epsilon)) && (y <= (y0 + epsilon)));
235: }
236: }
237:
238: /**
239: * Create a copy of the receiver
240: *
241: * @return a new copy of the receiver
242: */
243: public CDSRectangle copy() {
244: return new CDSRectangle((COSArray) cosGetArray().copyShallow());
245: }
246:
247: /**
248: * Return the height (an absolute value) of the rectangle.
249: *
250: * @return Return the height (an absolute value) of the rectangle.
251: */
252: public float getHeight() {
253: return Math.abs(getUpperRightY() - getLowerLeftY());
254: }
255:
256: /**
257: * The lower left x coordinate.
258: *
259: * @return The lower left x coordinate.
260: */
261: public float getLowerLeftX() {
262: return ((COSNumber) cosGetArray().get(0)).floatValue();
263: }
264:
265: /**
266: * The lower left y coordinate.
267: *
268: * @return The lower left y coordinate.
269: */
270: public float getLowerLeftY() {
271: return ((COSNumber) cosGetArray().get(1)).floatValue();
272: }
273:
274: /**
275: * The upper right x coordinate.
276: *
277: * @return The upper right x coordinate.
278: */
279: public float getUpperRightX() {
280: return ((COSNumber) cosGetArray().get(2)).floatValue();
281: }
282:
283: /**
284: * The upper rigth y coordinate.
285: *
286: * @return The upper right y coordinate.
287: */
288: public float getUpperRightY() {
289: return ((COSNumber) cosGetArray().get(3)).floatValue();
290: }
291:
292: /**
293: * Get the width (an absolute value) of the rectangle.
294: *
295: * @return Get the width (an absolute value) of the rectangle.
296: */
297: public float getWidth() {
298: return Math.abs(getUpperRightX() - getLowerLeftX());
299: }
300:
301: /*
302: * (non-Javadoc)
303: *
304: * @see de.intarsys.pdf.cos.COSBasedObject#invalidateCaches()
305: */
306: public void invalidateCaches() {
307: super .invalidateCaches();
308: cachedNormalizedRectangle = null;
309: cachedRectangle = null;
310: }
311:
312: /**
313: * Move the rectangle by a relative offset. The relationship of the opposite
314: * corners is preserved by this method.
315: *
316: * @param dx
317: * The offset by wich we move in x direction.
318: * @param dy
319: * The offset by wich we move in y direction.
320: */
321: public void move(float dx, float dy) {
322: setLowerLeftX(getLowerLeftX() + dx);
323: setLowerLeftY(getLowerLeftY() + dy);
324: setUpperRightX(getUpperRightX() + dx);
325: setUpperRightY(getUpperRightY() + dy);
326: }
327:
328: /**
329: * Move the rectangle to a new absolute position. The relationship of the
330: * opposite corners is preserved by this method. The receiver is modified.
331: *
332: * @param x
333: * The new x position of the lower left corner.
334: * @param y
335: * The new y position of the lower left corner.
336: *
337: * @return <code>this</code>
338: */
339: public CDSRectangle moveTo(float x, float y) {
340: float width = getUpperRightX() - getLowerLeftX();
341: float height = getUpperRightY() - getLowerLeftY();
342: setLowerLeftX(x);
343: setLowerLeftY(y);
344: setUpperRightX(width + x);
345: setUpperRightY(height + y);
346: return this ;
347: }
348:
349: /**
350: * Adjust the corner coordinates so that lower left is really in the lower
351: * left (this means returns the smallest coordinate values).
352: *
353: * <p>
354: * This method changes <code>this</code> in place!
355: * </p>
356: *
357: * @return <code>this</code>
358: */
359: public CDSRectangle normalize() {
360: float t1;
361: float t2;
362: if ((t1 = getLowerLeftX()) > (t2 = getUpperRightX())) {
363: setLowerLeftX(t2);
364: setUpperRightX(t1);
365: }
366: if ((t1 = getLowerLeftY()) > (t2 = getUpperRightY())) {
367: setLowerLeftY(t2);
368: setUpperRightY(t1);
369: }
370: return this ;
371: }
372:
373: /**
374: * Resize the rectangle by moving the upper right corner.
375: *
376: * @param dx
377: * The distance we move the upper right x coordinate.
378: * @param dy
379: * The distance we move the upper right y coordinate.
380: */
381: public void resize(float dx, float dy) {
382: setUpperRightX(getUpperRightX() + dx);
383: setUpperRightY(getUpperRightY() + dy);
384: }
385:
386: /**
387: * Resize the rectangle to a new width and heigth. The new width and heigth
388: * are defined relative to the lower left corner as signed values.
389: *
390: * @param width
391: * The new width of the rectangle.
392: * @param height
393: * The new height of the rectangle.
394: */
395: public void resizeTo(float width, float height) {
396: setUpperRightX(getLowerLeftX() + width);
397: setUpperRightY(getLowerLeftY() + height);
398: }
399:
400: /**
401: * Set the corners of this.
402: *
403: * @param llx
404: * The lower left x coordinate
405: * @param lly
406: * The lower left y coordinate
407: * @param urx
408: * The upper right x coordinate
409: * @param ury
410: * The upper right y coordinate
411: */
412: public void setCorners(float llx, float lly, float urx, float ury) {
413: cosGetArray().set(0, COSFixed.create(llx));
414: cosGetArray().set(1, COSFixed.create(lly));
415: cosGetArray().set(2, COSFixed.create(urx));
416: cosGetArray().set(3, COSFixed.create(ury));
417: }
418:
419: /**
420: * Set the height of this.
421: *
422: * @param height
423: * THe new height
424: */
425: public void setHeight(float height) {
426: setUpperRightY(getLowerLeftY() + height);
427: }
428:
429: /**
430: * Set the lower left x coordinate.
431: *
432: * @param num
433: * The lower left x coordinate.
434: */
435: public void setLowerLeftX(float num) {
436: cosGetArray().set(0, COSFixed.create(num));
437: }
438:
439: /**
440: * Set the lower left y coordinate.
441: *
442: * @param num
443: * The lower left y coordinate.
444: */
445: public void setLowerLeftY(float num) {
446: cosGetArray().set(1, COSFixed.create(num));
447: }
448:
449: /**
450: * Set the upper right x coordinate.
451: *
452: * @param num
453: * The upper right x coordinate.
454: */
455: public void setUpperRightX(float num) {
456: cosGetArray().set(2, COSFixed.create(num));
457: }
458:
459: /**
460: * Set the upper right y coordinate.
461: *
462: * @param num
463: * The upper right y coordinate.
464: */
465: public void setUpperRightY(float num) {
466: cosGetArray().set(3, COSFixed.create(num));
467: }
468:
469: /**
470: * Set the width of this.
471: *
472: * @param width
473: * The new width.
474: */
475: public void setWidth(float width) {
476: setUpperRightX(getLowerLeftX() + width);
477: }
478:
479: /**
480: * Construct a {@link Rectangle2D} object from the receiver. The rectangle
481: * will be normalized before construction.
482: *
483: * @return The Rectangle2D created from the receiver.
484: */
485: public Rectangle2D toNormalizedRectangle() {
486: if (cachedNormalizedRectangle == null) {
487: float llx = getLowerLeftX();
488: float lly = getLowerLeftY();
489: float urx = getUpperRightX();
490: float ury = getUpperRightY();
491: float temp;
492: if (llx > urx) {
493: temp = llx;
494: llx = urx;
495: urx = temp;
496: }
497: if (lly > ury) {
498: temp = lly;
499: lly = ury;
500: ury = temp;
501: }
502: cachedNormalizedRectangle = new Rectangle2D.Float(llx, lly,
503: urx - llx, ury - lly);
504: }
505: return (Rectangle2D) cachedNormalizedRectangle.clone();
506: }
507:
508: /**
509: * Construct a {@link Rectangle2D} object from the receiver. The resulting
510: * rectangle is not normalized, that means it may return a negative width or
511: * height.
512: *
513: * @return The Rectangle2D created from the receiver.
514: */
515: public Rectangle2D toRectangle() {
516: if (cachedRectangle == null) {
517: float llx = getLowerLeftX();
518: float lly = getLowerLeftY();
519: float urx = getUpperRightX();
520: float ury = getUpperRightY();
521: cachedRectangle = new Rectangle2D.Float(llx, lly,
522: urx - llx, ury - lly);
523: }
524: return (Rectangle2D) cachedRectangle.clone();
525: }
526: }
|