Line 21... |
Line 21... |
21 |
import org.openconcerto.utils.CollectionUtils;
|
21 |
import org.openconcerto.utils.CollectionUtils;
|
22 |
import org.openconcerto.utils.cc.IPredicate;
|
22 |
import org.openconcerto.utils.cc.IPredicate;
|
23 |
import org.openconcerto.xml.JDOMUtils;
|
23 |
import org.openconcerto.xml.JDOMUtils;
|
24 |
import org.openconcerto.xml.Validator;
|
24 |
import org.openconcerto.xml.Validator;
|
25 |
|
25 |
|
26 |
import java.math.BigDecimal;
|
- |
|
27 |
import java.util.ArrayList;
|
26 |
import java.util.ArrayList;
|
28 |
import java.util.Collections;
|
27 |
import java.util.Collections;
|
29 |
import java.util.Deque;
|
28 |
import java.util.Deque;
|
30 |
import java.util.HashMap;
|
29 |
import java.util.HashMap;
|
31 |
import java.util.Iterator;
|
30 |
import java.util.Iterator;
|
Line 154... |
Line 153... |
154 |
public static synchronized OOXML getDefault() {
|
153 |
public static synchronized OOXML getDefault() {
|
155 |
return defaultInstance;
|
154 |
return defaultInstance;
|
156 |
}
|
155 |
}
|
157 |
|
156 |
|
158 |
// from OpenDocument-v1.2-schema.rng : a coordinate is a length
|
157 |
// from OpenDocument-v1.2-schema.rng : a coordinate is a length
|
159 |
static private final BigDecimal parseCoordinate(final Element elem, final String attrName, final Namespace ns, LengthUnit unit) {
|
158 |
static private final Length parseCoordinate(final Element elem, final String attrName, final Namespace ns) {
|
160 |
return parseLength(elem, attrName, ns, unit);
|
159 |
return parseLength(elem, attrName, ns);
|
161 |
}
|
160 |
}
|
162 |
|
161 |
|
163 |
static private final BigDecimal parseLength(final Element elem, final String attrName, final Namespace ns, LengthUnit unit) {
|
162 |
static private final Length parseLength(final Element elem, final String attrName, final Namespace ns) {
|
164 |
final String attr = elem.getAttributeValue(attrName, ns);
|
163 |
final String attr = elem.getAttributeValue(attrName, ns);
|
165 |
if (attr == null)
|
164 |
final Length res = LengthUnit.parseLength(attr);
|
166 |
return null;
|
165 |
assert res != null;
|
167 |
return LengthUnit.parseLength(attr, unit);
|
166 |
return res.isNone() ? null : res;
|
168 |
}
|
167 |
}
|
169 |
|
168 |
|
170 |
// *** instances
|
169 |
// *** instances
|
171 |
|
170 |
|
172 |
private final XMLFormatVersion version;
|
171 |
private final XMLFormatVersion version;
|
Line 402... |
Line 401... |
402 |
|
401 |
|
403 |
/**
|
402 |
/**
|
404 |
* Return the coordinates of the top-left and bottom-right of the passed shape.
|
403 |
* Return the coordinates of the top-left and bottom-right of the passed shape.
|
405 |
*
|
404 |
*
|
406 |
* @param elem an XML element.
|
405 |
* @param elem an XML element.
|
407 |
* @param unit the unit of the returned numbers.
|
- |
|
408 |
* @return an array of 4 numbers, <code>null</code> if <code>elem</code> is not a shape, numbers
|
406 |
* @return an array of 4 lengths, <code>null</code> if <code>elem</code> is not a shape, items
|
409 |
* themselves are never <code>null</code>.
|
407 |
* themselves are never <code>null</code>.
|
410 |
*/
|
408 |
*/
|
411 |
public final BigDecimal[] getCoordinates(Element elem, LengthUnit unit) {
|
409 |
public final Length[] getCoordinates(Element elem) {
|
412 |
return this.getCoordinates(elem, unit, true, true);
|
410 |
return this.getCoordinates(elem, true, true);
|
413 |
}
|
411 |
}
|
414 |
|
412 |
|
415 |
/**
|
413 |
/**
|
416 |
* Return the coordinates of the top-left and bottom-right of the passed shape.
|
414 |
* Return the coordinates of the top-left and bottom-right of the passed shape.
|
417 |
*
|
415 |
*
|
418 |
* @param elem an XML element.
|
416 |
* @param elem an XML element.
|
419 |
* @param unit the unit of the returned numbers.
|
- |
|
420 |
* @param horizontal <code>true</code> if the x coordinates should be computed,
|
417 |
* @param horizontal <code>true</code> if the x coordinates should be computed,
|
421 |
* <code>false</code> meaning items 0 and 2 of the result are <code>null</code>.
|
418 |
* <code>false</code> meaning items 0 and 2 of the result are <code>null</code>.
|
422 |
* @param vertical <code>true</code> if the y coordinates should be computed, <code>false</code>
|
419 |
* @param vertical <code>true</code> if the y coordinates should be computed, <code>false</code>
|
423 |
* meaning items 1 and 3 of the result are <code>null</code>.
|
420 |
* meaning items 1 and 3 of the result are <code>null</code>.
|
424 |
* @return an array of 4 numbers, <code>null</code> if <code>elem</code> is not a shape, numbers
|
421 |
* @return an array of 4 lengths, <code>null</code> if <code>elem</code> is not a shape, items
|
425 |
* themselves are only <code>null</code> if requested with <code>horizontal</code> or
|
422 |
* themselves are only <code>null</code> if requested with <code>horizontal</code> or
|
426 |
* <code>vertical</code>.
|
423 |
* <code>vertical</code>.
|
427 |
*/
|
424 |
*/
|
428 |
public final BigDecimal[] getCoordinates(Element elem, LengthUnit unit, final boolean horizontal, final boolean vertical) {
|
425 |
public final Length[] getCoordinates(Element elem, final boolean horizontal, final boolean vertical) {
|
429 |
return getCoordinates(elem, getVersion().getNS("svg"), unit, horizontal, vertical);
|
426 |
return getCoordinates(elem, getVersion().getNS("svg"), horizontal, vertical);
|
430 |
}
|
427 |
}
|
431 |
|
428 |
|
432 |
static private final BigDecimal[] getCoordinates(Element elem, final Namespace svgNS, LengthUnit unit, final boolean horizontal, final boolean vertical) {
|
429 |
static private final Length[] getCoordinates(Element elem, final Namespace svgNS, final boolean horizontal, final boolean vertical) {
|
433 |
if (elem.getName().equals("g") && elem.getNamespacePrefix().equals("draw")) {
|
430 |
if (elem.getName().equals("g") && elem.getNamespacePrefix().equals("draw")) {
|
434 |
// put below if to allow null to be returned by getLocalCoordinates() if elem isn't a
|
431 |
// put below if to allow null to be returned by getLocalCoordinates() if elem isn't a
|
435 |
// shape
|
432 |
// shape
|
436 |
if (!horizontal && !vertical)
|
433 |
if (!horizontal && !vertical)
|
437 |
return new BigDecimal[] { null, null, null, null };
|
434 |
return new Length[] { null, null, null, null };
|
438 |
|
435 |
|
439 |
// an OpenDocument group (of shapes) doesn't have any coordinates nor any width and
|
436 |
// an OpenDocument group (of shapes) doesn't have any coordinates nor any width and
|
440 |
// height so iterate through its components to find its coordinates
|
437 |
// height so iterate through its components to find its coordinates
|
441 |
BigDecimal minX = null, minY = null;
|
438 |
Length minX = null, minY = null;
|
442 |
BigDecimal maxX = null, maxY = null;
|
439 |
Length maxX = null, maxY = null;
|
443 |
for (final Object c : elem.getChildren()) {
|
440 |
for (final Object c : elem.getChildren()) {
|
444 |
final Element child = (Element) c;
|
441 |
final Element child = (Element) c;
|
445 |
final BigDecimal[] childCoord = getCoordinates(child, svgNS, unit, horizontal, vertical);
|
442 |
final Length[] childCoord = getCoordinates(child, svgNS, horizontal, vertical);
|
446 |
// e.g. <office:event-listeners>, <svg:desc>, <svg:title>
|
443 |
// e.g. <office:event-listeners>, <svg:desc>, <svg:title>
|
447 |
if (childCoord != null) {
|
444 |
if (childCoord != null) {
|
448 |
{
|
445 |
{
|
449 |
final BigDecimal x = childCoord[0];
|
446 |
final Length x = childCoord[0];
|
450 |
final BigDecimal x2 = childCoord[2];
|
447 |
final Length x2 = childCoord[2];
|
451 |
if (x != null) {
|
448 |
if (x != null) {
|
452 |
assert x2 != null;
|
449 |
assert x2 != null;
|
453 |
if (minX == null || x.compareTo(minX) < 0)
|
450 |
if (minX == null || x.compareTo(minX) < 0)
|
454 |
minX = x;
|
451 |
minX = x;
|
455 |
if (maxX == null || x2.compareTo(maxX) > 0)
|
452 |
if (maxX == null || x2.compareTo(maxX) > 0)
|
456 |
maxX = x2;
|
453 |
maxX = x2;
|
457 |
}
|
454 |
}
|
458 |
}
|
455 |
}
|
459 |
{
|
456 |
{
|
460 |
final BigDecimal y = childCoord[1];
|
457 |
final Length y = childCoord[1];
|
461 |
final BigDecimal y2 = childCoord[3];
|
458 |
final Length y2 = childCoord[3];
|
462 |
if (y != null) {
|
459 |
if (y != null) {
|
463 |
assert y2 != null;
|
460 |
assert y2 != null;
|
464 |
if (minY == null || y.compareTo(minY) < 0)
|
461 |
if (minY == null || y.compareTo(minY) < 0)
|
465 |
minY = y;
|
462 |
minY = y;
|
466 |
if (maxY == null || y2.compareTo(maxY) > 0)
|
463 |
if (maxY == null || y2.compareTo(maxY) > 0)
|
Line 470... |
Line 467... |
470 |
}
|
467 |
}
|
471 |
}
|
468 |
}
|
472 |
// works because we check above if both horizontal and vertical are false
|
469 |
// works because we check above if both horizontal and vertical are false
|
473 |
if (minX == null && minY == null)
|
470 |
if (minX == null && minY == null)
|
474 |
throw new IllegalArgumentException("Empty group : " + JDOMUtils.output(elem));
|
471 |
throw new IllegalArgumentException("Empty group : " + JDOMUtils.output(elem));
|
475 |
return new BigDecimal[] { minX, minY, maxX, maxY };
|
472 |
return new Length[] { minX, minY, maxX, maxY };
|
476 |
} else {
|
473 |
} else {
|
477 |
return getLocalCoordinates(elem, svgNS, unit, horizontal, vertical);
|
474 |
return getLocalCoordinates(elem, svgNS, horizontal, vertical);
|
478 |
}
|
475 |
}
|
479 |
}
|
476 |
}
|
480 |
|
477 |
|
481 |
// return null if elem isn't a shape (no x/y or no width/height)
|
478 |
// return null if elem isn't a shape (no x/y or no width/height)
|
482 |
// BigDecimal null if and only if horizontal/vertical is false
|
479 |
// BigDecimal null if and only if horizontal/vertical is false
|
483 |
static private final BigDecimal[] getLocalCoordinates(Element elem, final Namespace svgNS, LengthUnit unit, final boolean horizontal, final boolean vertical) {
|
480 |
static private final Length[] getLocalCoordinates(Element elem, final Namespace svgNS, final boolean horizontal, final boolean vertical) {
|
484 |
final BigDecimal x = parseCoordinate(elem, "x", svgNS, unit);
|
481 |
final Length x = parseCoordinate(elem, "x", svgNS);
|
485 |
final BigDecimal x1 = parseCoordinate(elem, "x1", svgNS, unit);
|
482 |
final Length x1 = parseCoordinate(elem, "x1", svgNS);
|
486 |
if (x == null && x1 == null)
|
483 |
if (x == null && x1 == null)
|
487 |
return null;
|
484 |
return null;
|
488 |
|
485 |
|
489 |
final BigDecimal y = parseCoordinate(elem, "y", svgNS, unit);
|
486 |
final Length y = parseCoordinate(elem, "y", svgNS);
|
490 |
final BigDecimal y1 = parseCoordinate(elem, "y1", svgNS, unit);
|
487 |
final Length y1 = parseCoordinate(elem, "y1", svgNS);
|
491 |
if (y == null && y1 == null)
|
488 |
if (y == null && y1 == null)
|
492 |
throw new IllegalArgumentException("Have x but missing y in " + JDOMUtils.output(elem));
|
489 |
throw new IllegalArgumentException("Have x but missing y in " + JDOMUtils.output(elem));
|
493 |
|
490 |
|
494 |
final BigDecimal startX;
|
491 |
final Length startX;
|
495 |
final BigDecimal endX;
|
492 |
final Length endX;
|
496 |
if (horizontal) {
|
493 |
if (horizontal) {
|
497 |
if (x == null) {
|
494 |
if (x == null) {
|
498 |
startX = x1;
|
495 |
startX = x1;
|
499 |
endX = parseCoordinate(elem, "x2", svgNS, unit);
|
496 |
endX = parseCoordinate(elem, "x2", svgNS);
|
500 |
} else {
|
497 |
} else {
|
501 |
startX = x;
|
498 |
startX = x;
|
502 |
final BigDecimal width = parseLength(elem, "width", svgNS, unit);
|
499 |
final Length width = parseLength(elem, "width", svgNS);
|
503 |
endX = width == null ? null : startX.add(width);
|
500 |
endX = width == null ? null : startX.add(width);
|
504 |
}
|
501 |
}
|
505 |
// return null if there's no second coordinate (it's a point)
|
502 |
// return null if there's no second coordinate (it's a point)
|
506 |
if (endX == null)
|
503 |
if (endX == null)
|
507 |
return null;
|
504 |
return null;
|
508 |
} else {
|
505 |
} else {
|
509 |
startX = null;
|
506 |
startX = null;
|
510 |
endX = null;
|
507 |
endX = null;
|
511 |
}
|
508 |
}
|
512 |
|
509 |
|
513 |
final BigDecimal startY;
|
510 |
final Length startY;
|
514 |
final BigDecimal endY;
|
511 |
final Length endY;
|
515 |
if (vertical) {
|
512 |
if (vertical) {
|
516 |
if (y == null) {
|
513 |
if (y == null) {
|
517 |
startY = y1;
|
514 |
startY = y1;
|
518 |
endY = parseCoordinate(elem, "y2", svgNS, unit);
|
515 |
endY = parseCoordinate(elem, "y2", svgNS);
|
519 |
} else {
|
516 |
} else {
|
520 |
startY = y;
|
517 |
startY = y;
|
521 |
final BigDecimal height = parseLength(elem, "height", svgNS, unit);
|
518 |
final Length height = parseLength(elem, "height", svgNS);
|
522 |
endY = height == null ? null : startY.add(height);
|
519 |
endY = height == null ? null : startY.add(height);
|
523 |
}
|
520 |
}
|
524 |
// return null if there's no second coordinate (it's a point)
|
521 |
// return null if there's no second coordinate (it's a point)
|
525 |
if (endY == null)
|
522 |
if (endY == null)
|
526 |
return null;
|
523 |
return null;
|
527 |
} else {
|
524 |
} else {
|
528 |
startY = null;
|
525 |
startY = null;
|
529 |
endY = null;
|
526 |
endY = null;
|
530 |
}
|
527 |
}
|
531 |
|
528 |
|
532 |
return new BigDecimal[] { startX, startY, endX, endY };
|
529 |
return new Length[] { startX, startY, endX, endY };
|
533 |
}
|
530 |
}
|
534 |
|
531 |
|
535 |
@Immutable
|
532 |
@Immutable
|
536 |
private static final class XML_OO extends OOXML {
|
533 |
private static final class XML_OO extends OOXML {
|
537 |
private static final Set<Namespace> NS = XMLVersion.OOo.getNamespaceSet();
|
534 |
private static final Set<Namespace> NS = XMLVersion.OOo.getNamespaceSet();
|