| /* ==================================================================== |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| ==================================================================== */ |
| |
| package org.apache.poi.xssf.usermodel; |
| |
| import org.apache.poi.ooxml.util.POIXMLUnits; |
| import org.apache.poi.ss.usermodel.ClientAnchor; |
| import org.apache.poi.util.Internal; |
| import org.apache.poi.util.Units; |
| import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D; |
| import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D; |
| import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker; |
| |
| /** |
| * A client anchor is attached to an excel worksheet. It anchors against: |
| * <ol> |
| * <li>A fixed position and fixed size |
| * <li>A position relative to a cell (top-left) and a fixed size |
| * <li>A position relative to a cell (top-left) and sized relative to another cell (bottom right) |
| * </ol> |
| * |
| * which method is used is determined by the {@link AnchorType}. |
| */ |
| public class XSSFClientAnchor extends XSSFAnchor implements ClientAnchor { |
| |
| /** |
| * placeholder for zeros when needed for dynamic position calculations |
| */ |
| private static final CTMarker EMPTY_MARKER = CTMarker.Factory.newInstance(); |
| |
| private AnchorType anchorType; |
| |
| /** |
| * Starting anchor point (top-left cell + relative offset) |
| * if left null recalculate as needed from point |
| */ |
| private CTMarker cell1; |
| |
| /** |
| * Ending anchor point (bottom-right cell + relative offset) |
| * if left null, re-calculate as needed from size and cell1 |
| */ |
| private CTMarker cell2; |
| |
| /** |
| * if present, fixed size of the object to use instead of cell2, which is inferred instead |
| */ |
| private CTPositiveSize2D size; |
| |
| /** |
| * if present, fixed top-left position to use instead of cell1, which is inferred instead |
| */ |
| private CTPoint2D position; |
| |
| /** |
| * sheet to base dynamic calculations on, if needed. Required if size and/or position or set. |
| * Not needed if cell1/2 are set explicitly (dynamic sizing and position relative to cells). |
| */ |
| private XSSFSheet sheet; |
| |
| /** |
| * Creates a new client anchor and defaults all the anchor positions to 0. |
| * Sets the type to {@link AnchorType#MOVE_AND_RESIZE} relative to cell range A1:A1. |
| */ |
| public XSSFClientAnchor() { |
| this(0,0,0,0,0,0,0,0); |
| } |
| |
| /** |
| * Creates a new client anchor and sets the top-left and bottom-right |
| * coordinates of the anchor by cell references and offsets. |
| * Sets the type to {@link AnchorType#MOVE_AND_RESIZE}. |
| * |
| * @param dx1 the x coordinate within the first cell. |
| * @param dy1 the y coordinate within the first cell. |
| * @param dx2 the x coordinate within the second cell. |
| * @param dy2 the y coordinate within the second cell. |
| * @param col1 the column (0 based) of the first cell. |
| * @param row1 the row (0 based) of the first cell. |
| * @param col2 the column (0 based) of the second cell. |
| * @param row2 the row (0 based) of the second cell. |
| */ |
| public XSSFClientAnchor(int dx1, int dy1, int dx2, int dy2, int col1, int row1, int col2, int row2) { |
| anchorType = AnchorType.MOVE_AND_RESIZE; |
| cell1 = CTMarker.Factory.newInstance(); |
| cell1.setCol(col1); |
| cell1.setColOff(dx1); |
| cell1.setRow(row1); |
| cell1.setRowOff(dy1); |
| cell2 = CTMarker.Factory.newInstance(); |
| cell2.setCol(col2); |
| cell2.setColOff(dx2); |
| cell2.setRow(row2); |
| cell2.setRowOff(dy2); |
| } |
| |
| /** |
| * Create XSSFClientAnchor from existing xml beans, sized and positioned relative to a pair of cells. |
| * Sets the type to {@link AnchorType#MOVE_AND_RESIZE}. |
| * @param cell1 starting anchor point |
| * @param cell2 ending anchor point |
| */ |
| protected XSSFClientAnchor(CTMarker cell1, CTMarker cell2) { |
| anchorType = AnchorType.MOVE_AND_RESIZE; |
| this.cell1 = cell1; |
| this.cell2 = cell2; |
| } |
| |
| /** |
| * Create XSSFClientAnchor from existing xml beans, sized and positioned relative to a pair of cells. |
| * Sets the type to {@link AnchorType#MOVE_DONT_RESIZE}. |
| * |
| * @param sheet needed to calculate ending point based on column/row sizes |
| * @param cell1 starting anchor point |
| * @param size object size, to calculate ending anchor point |
| */ |
| protected XSSFClientAnchor(XSSFSheet sheet, CTMarker cell1, CTPositiveSize2D size) { |
| anchorType = AnchorType.MOVE_DONT_RESIZE; |
| this.sheet = sheet; |
| this.size = size; |
| this.cell1 = cell1; |
| // this.cell2 = calcCell(sheet, cell1, size.getCx(), size.getCy()); |
| } |
| |
| /** |
| * Create XSSFClientAnchor from existing xml beans, sized and positioned relative to a pair of cells. |
| * Sets the type to {@link AnchorType#DONT_MOVE_AND_RESIZE}. |
| * |
| * @param sheet needed to calculate starting and ending points based on column/row sizes |
| * @param position starting absolute position |
| * @param size object size, to calculate ending position |
| */ |
| protected XSSFClientAnchor(XSSFSheet sheet, CTPoint2D position, CTPositiveSize2D size) { |
| anchorType = AnchorType.DONT_MOVE_AND_RESIZE; |
| this.sheet = sheet; |
| this.position = position; |
| this.size = size; |
| // zeros for row/col/offsets |
| // this.cell1 = calcCell(sheet, EMPTY_MARKER, position.getCx(), position.getCy()); |
| // this.cell2 = calcCell(sheet, cell1, size.getCx(), size.getCy()); |
| } |
| |
| private CTMarker calcCell(CTMarker cell, long w, long h) { |
| CTMarker c2 = CTMarker.Factory.newInstance(); |
| |
| int r = cell.getRow(); |
| int c = cell.getCol(); |
| |
| int cw = Units.columnWidthToEMU(sheet.getColumnWidth(c)); |
| |
| // start with width - offset, then keep adding column widths until the next one puts us over w |
| long wPos = cw - POIXMLUnits.parseLength(cell.xgetColOff()); |
| |
| while (wPos < w) { |
| c++; |
| cw = Units.columnWidthToEMU(sheet.getColumnWidth(c)); |
| wPos += cw; |
| } |
| // now wPos >= w, so end column = c, now figure offset |
| c2.setCol(c); |
| c2.setColOff(cw - (wPos - w)); |
| |
| int rh = Units.toEMU(getRowHeight(sheet, r)); |
| // start with height - offset, then keep adding row heights until the next one puts us over h |
| long hPos = rh - POIXMLUnits.parseLength(cell.xgetRowOff()); |
| |
| while (hPos < h) { |
| r++; |
| rh = Units.toEMU(getRowHeight(sheet, r)); |
| hPos += rh; |
| } |
| // now hPos >= h, so end row = r, now figure offset |
| c2.setRow(r); |
| c2.setRowOff(rh - (hPos - h)); |
| |
| return c2; |
| } |
| |
| /** |
| * @param sheet |
| * @param row |
| * @return height in twips (1/20th of point) for row or default |
| */ |
| private static float getRowHeight(XSSFSheet sheet, int row) { |
| XSSFRow r = sheet.getRow(row); |
| return r == null ? sheet.getDefaultRowHeightInPoints() : r.getHeightInPoints(); |
| } |
| |
| private CTMarker getCell1() { |
| return cell1 != null ? cell1 : calcCell(EMPTY_MARKER, POIXMLUnits.parseLength(position.xgetX()), POIXMLUnits.parseLength(position.xgetY())); |
| } |
| |
| private CTMarker getCell2() { |
| return cell2 != null ? cell2 : calcCell(getCell1(), size.getCx(), size.getCy()); |
| } |
| |
| public short getCol1() { |
| return (short)getCell1().getCol(); |
| } |
| |
| /** |
| * @throws NullPointerException if cell1 is null (fixed position) |
| * @see org.apache.poi.ss.usermodel.ClientAnchor#setCol1(int) |
| */ |
| public void setCol1(int col1) { |
| cell1.setCol(col1); |
| } |
| |
| public short getCol2() { |
| return (short) getCell2().getCol(); |
| } |
| |
| /** |
| * @throws NullPointerException if cell2 is null (fixed size) |
| * @see org.apache.poi.ss.usermodel.ClientAnchor#setCol2(int) |
| */ |
| public void setCol2(int col2) { |
| cell2.setCol(col2); |
| } |
| |
| public int getRow1() { |
| return getCell1().getRow(); |
| } |
| |
| /** |
| * @throws NullPointerException if cell1 is null (fixed position) |
| * @see org.apache.poi.ss.usermodel.ClientAnchor#setRow1(int) |
| */ |
| public void setRow1(int row1) { |
| cell1.setRow(row1); |
| } |
| |
| public int getRow2() { |
| return getCell2().getRow(); |
| } |
| |
| /** |
| * @throws NullPointerException if cell2 is null (fixed size) |
| * @see org.apache.poi.ss.usermodel.ClientAnchor#setRow2(int) |
| */ |
| public void setRow2(int row2) { |
| cell2.setRow(row2); |
| } |
| |
| public int getDx1() { |
| return Math.toIntExact(POIXMLUnits.parseLength(getCell1().xgetColOff())); |
| } |
| |
| /** |
| * @throws NullPointerException if cell1 is null (fixed position) |
| * @see org.apache.poi.ss.usermodel.ChildAnchor#setDx1(int) |
| */ |
| public void setDx1(int dx1) { |
| cell1.setColOff(dx1); |
| } |
| |
| public int getDy1() { |
| return Math.toIntExact(POIXMLUnits.parseLength(getCell1().xgetRowOff())); |
| } |
| |
| /** |
| * @throws NullPointerException if cell1 is null (fixed position) |
| * @see org.apache.poi.ss.usermodel.ChildAnchor#setDy1(int) |
| */ |
| public void setDy1(int dy1) { |
| cell1.setRowOff(dy1); |
| } |
| |
| public int getDy2() { |
| return Math.toIntExact(POIXMLUnits.parseLength(getCell2().xgetRowOff())); |
| } |
| |
| /** |
| * @throws NullPointerException if cell2 is null (fixed size) |
| * @see org.apache.poi.ss.usermodel.ChildAnchor#setDy2(int) |
| */ |
| public void setDy2(int dy2) { |
| cell2.setRowOff(dy2); |
| } |
| |
| public int getDx2() { |
| return Math.toIntExact(POIXMLUnits.parseLength(getCell2().xgetColOff())); |
| } |
| |
| /** |
| * @throws NullPointerException if cell2 is null (fixed size) |
| * @see org.apache.poi.ss.usermodel.ChildAnchor#setDx2(int) |
| */ |
| public void setDx2(int dx2) { |
| cell2.setColOff(dx2); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (o == null || !(o instanceof XSSFClientAnchor)) return false; |
| |
| XSSFClientAnchor anchor = (XSSFClientAnchor) o; |
| return getDx1() == anchor.getDx1() && |
| getDx2() == anchor.getDx2() && |
| getDy1() == anchor.getDy1() && |
| getDy2() == anchor.getDy2() && |
| getCol1() == anchor.getCol1() && |
| getCol2() == anchor.getCol2() && |
| getRow1() == anchor.getRow1() && |
| getRow2() == anchor.getRow2() ; |
| |
| } |
| |
| @Override |
| public int hashCode() { |
| assert false : "hashCode not designed"; |
| return 42; // any arbitrary constant will do |
| } |
| |
| @Override |
| public String toString(){ |
| return "from : " + getCell1() + "; to: " + getCell2(); |
| } |
| |
| /** |
| * Return starting anchor point |
| * |
| * @return starting anchor point |
| */ |
| @Internal |
| public CTMarker getFrom(){ |
| return getCell1(); |
| } |
| |
| protected void setFrom(CTMarker from){ |
| cell1 = from; |
| } |
| |
| /** |
| * Return ending anchor point |
| * |
| * @return ending anchor point |
| */ |
| @Internal |
| public CTMarker getTo(){ |
| return getCell2(); |
| } |
| |
| protected void setTo(CTMarker to){ |
| cell2 = to; |
| } |
| |
| /** |
| * @return absolute top-left position, or null if position is determined from the "from" cell |
| * @since POI 3.17 beta 1 |
| */ |
| public CTPoint2D getPosition() { |
| return position; |
| } |
| |
| /** |
| * Sets the top-left absolute position of the object. To use this, "from" must be set to null. |
| * @param position |
| * @since POI 3.17 beta 1 |
| */ |
| public void setPosition(CTPoint2D position) { |
| this.position = position; |
| } |
| |
| /** |
| * |
| * @return size or null, if size is determined from the to and from cells |
| * @since POI 3.17 beta 1 |
| */ |
| public CTPositiveSize2D getSize() { |
| return size; |
| } |
| |
| /** |
| * Sets the size of the object. To use this, "to" must be set to null. |
| * @param size |
| * @since POI 3.17 beta 1 |
| */ |
| public void setSize(CTPositiveSize2D size) { |
| this.size = size; |
| } |
| |
| /** |
| * Sets the anchor type |
| * @param anchorType the anchor type to set |
| * @since POI 3.14 |
| */ |
| @Override |
| public void setAnchorType( AnchorType anchorType ) |
| { |
| this.anchorType = anchorType; |
| } |
| |
| /** |
| * Gets the anchor type |
| * Changed from returning an int to an enum in POI 3.14 beta 1. |
| * @return the anchor type |
| */ |
| @Override |
| public AnchorType getAnchorType() |
| { |
| return anchorType; |
| } |
| |
| public boolean isSet(){ |
| CTMarker c1 = getCell1(); |
| CTMarker c2 = getCell2(); |
| return !(c1.getCol() == 0 && c2.getCol() == 0 && |
| c1.getRow() == 0 && c2.getRow() == 0); |
| } |
| } |