diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 94691ec8f..c2e4bee92 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,7 @@ + 49919 - support for BorderCode in HWPF 49908 - support for processing of symbols in HWPF 50022 - support for retrieving pictures from HSSF workbooks 50020 - Avoid IllegalStateException when creating Data validation in sheet with macro diff --git a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/BorderCode.java b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/BorderCode.java index 52747c17e..f705cc0a4 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/BorderCode.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/BorderCode.java @@ -17,13 +17,29 @@ package org.apache.poi.hwpf.usermodel; +import org.apache.poi.util.BitField; +import org.apache.poi.util.BitFieldFactory; import org.apache.poi.util.LittleEndian; +/** + * Mapping class for BRC80 structure (Border Code for Word 97) + * + *

Comments are copied out from the binary format specification. + */ public final class BorderCode implements Cloneable { + public static final int SIZE = 4; + private short _info; + private static final BitField _dptLineWidth = BitFieldFactory.getInstance(0x00ff); + private static final BitField _brcType = BitFieldFactory.getInstance(0xff00); + private short _info2; - + private static final BitField _ico = BitFieldFactory.getInstance(0x00ff); + private static final BitField _dptSpace = BitFieldFactory.getInstance(0x1f00); + private static final BitField _fShadow = BitFieldFactory.getInstance(0x2000); + private static final BitField _fFrame = BitFieldFactory.getInstance(0x4000); + public BorderCode() { } @@ -63,4 +79,119 @@ public final class BorderCode implements Cloneable { { return super.clone(); } + + /** + * Width of a single line in 1/8 pt, max of 32 pt. + */ + public int getLineWidth() { + return _dptLineWidth.getShortValue(_info); + } + + public void setLineWidth(int lineWidth) { + _dptLineWidth.setValue(_info, lineWidth); + } + + /** + * Border type code: + *

  • 0 none + *
  • 1 single + *
  • 2 thick + *
  • 3 double + *
  • 5 hairline + *
  • 6 dot + *
  • 7 dash large gap + *
  • 8 dot dash + *
  • 9 dot dot dash + *
  • 10 triple + *
  • 11 thin-thick small gap + *
  • 12 thick-thin small gap + *
  • 13 thin-thick-thin small gap + *
  • 14 thin-thick medium gap + *
  • 15 thick-thin medium gap + *
  • 16 thin-thick-thin medium gap + *
  • 17 thin-thick large gap + *
  • 18 thick-thin large gap + *
  • 19 thin-thick-thin large gap + *
  • 20 wave + *
  • 21 double wave + *
  • 22 dash small gap + *
  • 23 dash dot stroked + *
  • 24 emboss 3D + *
  • 25 engrave 3D + *
  • codes 64 – 230 represent border art types and are used only for page borders + */ + public int getBorderType() { + return _brcType.getShortValue(_info); + } + + public void setBorderType(int borderType) { + _brcType.setValue(_info, borderType); + } + + /** + * Color: + *
  • 0 Auto + *
  • 1 Black + *
  • 2 Blue + *
  • 3 Cyan + *
  • 4 Green + *
  • 5 Magenta + *
  • 6 Red + *
  • 7 Yellow + *
  • 8 White + *
  • 9 DkBlue + *
  • 10 DkCyan + *
  • 11 DkGreen + *
  • 12 DkMagenta + *
  • 13 DkRed + *
  • 14 DkYellow + *
  • 15 DkGray + *
  • 16 LtGray + */ + public short getColor() { + return _ico.getShortValue(_info2); + } + + public void setColor(short color) { + _ico.setValue(_info2, color); + } + + /** + * Width of space to maintain between border and text within border. + * + *

    Must be 0 when BRC is a substructure of TC. + * + *

    Stored in points. + */ + public int getSpace() { + return _dptSpace.getShortValue(_info2); + } + + public void setSpace(int space) { + _dptSpace.setValue(_info2, space); + } + + /** + * When true, border is drawn with shadow + * Must be false when BRC is a substructure of the TC. + */ + public boolean isShadow() { + return _fShadow.getValue(_info2) != 0; + } + + public void setShadow(boolean shadow) { + _fShadow.setValue(_info2, shadow ? 1 : 0); + } + + /** + * Don‘t reverse the border. + */ + public boolean isFrame() { + return _fFrame.getValue(_info2) != 0; + } + + public void setFrame(boolean frame) { + _fFrame.setValue(_info2, frame ? 1 : 0); + } + } diff --git a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/CharacterRun.java b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/CharacterRun.java index 076b0029a..ccd5f81c2 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/CharacterRun.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/CharacterRun.java @@ -603,5 +603,10 @@ public final class CharacterRun } else throw new IllegalStateException("Not a symbol CharacterRun"); } + + public BorderCode getBorder() { + return _props.getBrc(); + } + } diff --git a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/TableRow.java b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/TableRow.java index a2a8d4676..97698ef6f 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/TableRow.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/TableRow.java @@ -17,7 +17,6 @@ package org.apache.poi.hwpf.usermodel; -import org.apache.poi.hwpf.model.PropertyNode; import org.apache.poi.hwpf.sprm.TableSprmUncompressor; public final class TableRow @@ -140,4 +139,33 @@ public final class TableRow { return _cells[index]; } + + public BorderCode getTopBorder() { + return _tprops.getBrcBottom(); + } + + public BorderCode getBottomBorder() { + return _tprops.getBrcBottom(); + } + + public BorderCode getLeftBorder() { + return _tprops.getBrcLeft(); + } + + public BorderCode getRightBorder() { + return _tprops.getBrcRight(); + } + + public BorderCode getHorizontalBorder() { + return _tprops.getBrcHorizontal(); + } + + public BorderCode getVerticalBorder() { + return _tprops.getBrcVertical(); + } + + public BorderCode getBarBorder() { + throw new UnsupportedOperationException("not applicable for TableRow"); + } + } diff --git a/src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestBorderCode.java b/src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestBorderCode.java new file mode 100644 index 000000000..4adf93996 --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestBorderCode.java @@ -0,0 +1,113 @@ +/* ==================================================================== + 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.hwpf.usermodel; + +import junit.framework.TestCase; + +import org.apache.poi.hwpf.HWPFDocument; +import org.apache.poi.hwpf.HWPFTestDataSamples; + +/** + * API for BorderCode, see Bugzill 49919 + */ +public final class TestBorderCode extends TestCase { + + private int pos = 0; + private Range range; + + public void test() { + HWPFDocument doc = HWPFTestDataSamples.openSampleFile("Bug49919.doc"); + range = doc.getRange(); + + Paragraph par = findParagraph("Paragraph normal\r"); + assertEquals(0, par.getLeftBorder().getBorderType()); + assertEquals(0, par.getRightBorder().getBorderType()); + assertEquals(0, par.getTopBorder().getBorderType()); + assertEquals(0, par.getBottomBorder().getBorderType()); + + par = findParagraph("Paragraph with border\r"); + assertEquals(18, par.getLeftBorder().getBorderType()); + assertEquals(17, par.getRightBorder().getBorderType()); + assertEquals(18, par.getTopBorder().getBorderType()); + assertEquals(17, par.getBottomBorder().getBorderType()); + assertEquals(15, par.getLeftBorder().getColor()); + + par = findParagraph("Paragraph with red border\r"); + assertEquals(1, par.getLeftBorder().getBorderType()); + assertEquals(1, par.getRightBorder().getBorderType()); + assertEquals(1, par.getTopBorder().getBorderType()); + assertEquals(1, par.getBottomBorder().getBorderType()); + assertEquals(6, par.getLeftBorder().getColor()); + + par = findParagraph("Paragraph with bordered words.\r"); + assertEquals(0, par.getLeftBorder().getBorderType()); + assertEquals(0, par.getRightBorder().getBorderType()); + assertEquals(0, par.getTopBorder().getBorderType()); + assertEquals(0, par.getBottomBorder().getBorderType()); + + assertEquals(3, par.numCharacterRuns()); + CharacterRun chr = par.getCharacterRun(0); + assertEquals(0, chr.getBorder().getBorderType()); + chr = par.getCharacterRun(1); + assertEquals(1, chr.getBorder().getBorderType()); + assertEquals(0, chr.getBorder().getColor()); + chr = par.getCharacterRun(2); + assertEquals(0, chr.getBorder().getBorderType()); + + while (pos < range.numParagraphs() - 1) { + par = range.getParagraph(pos++); + if (par.isInTable()) + break; + } + + assertEquals(true, par.isInTable()); + Table tab = range.getTable(par); + + // Border could be defined for the entire row, or for each cell, with the same visual appearance. + assertEquals(2, tab.numRows()); + TableRow row = tab.getRow(0); + assertEquals(1, row.getLeftBorder().getBorderType()); + assertEquals(1, row.getRightBorder().getBorderType()); + assertEquals(1, row.getTopBorder().getBorderType()); + assertEquals(1, row.getBottomBorder().getBorderType()); + + assertEquals(2, row.numCells()); + TableCell cell = row.getCell(1); + assertEquals(3, cell.getBrcTop().getBorderType()); + + row = tab.getRow(1); + cell = row.getCell(0); + // 255 clears border inherited from row + assertEquals(255, cell.getBrcBottom().getBorderType()); + } + + private Paragraph findParagraph(String expectedText) { + while (pos < range.numParagraphs() - 1) { + Paragraph par = range.getParagraph(pos); + pos++; + if (par.text().equals(expectedText)) + return par; + } + + fail("Expected paragraph not found"); + + // should never come here + throw null; + } + +} diff --git a/test-data/document/Bug49919.doc b/test-data/document/Bug49919.doc new file mode 100644 index 000000000..656872c94 Binary files /dev/null and b/test-data/document/Bug49919.doc differ