From ef93bdc6aea0e6a3c530eecc7571d2f49c6fa2d5 Mon Sep 17 00:00:00 2001 From: Josh Micich Date: Mon, 23 Mar 2009 19:42:39 +0000 Subject: [PATCH] Fix for bug 46840 - PageSettingsBlock should include HEADERFOOTER record git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@757520 13f79535-47bb-0310-9956-ffa450edef68 --- src/documentation/content/xdocs/changes.xml | 3 +- src/documentation/content/xdocs/status.xml | 3 +- src/java/org/apache/poi/hssf/model/Sheet.java | 6 +- .../apache/poi/hssf/record/UnknownRecord.java | 3 +- .../record/aggregates/PageSettingsBlock.java | 18 +++++ .../aggregates/TestPageSettingBlock.java | 65 +++++++++++++++++++ 6 files changed, 94 insertions(+), 4 deletions(-) diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index fd101fb8f..0cb62592c 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -37,7 +37,8 @@ - update cell type when setting cached formula result in XSSFCell + 46840 - PageSettingsBlock should include HEADERFOOTER record + 46885 - update cell type when setting cached formula result in XSSFCell added modifiers for anchor type to XSSFClientAnchor 46772 - support built-in data formats in XSSFDataFormat 46719 - fixed XSSFSheet.shiftRows to correctly preserve row heights diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 3b3ba8e7a..bef81975f 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,7 +34,8 @@ - update cell type when setting cached formula result in XSSFCell + 46840 - PageSettingsBlock should include HEADERFOOTER record + 46885 - update cell type when setting cached formula result in XSSFCell added modifiers for anchor type to XSSFClientAnchor 46772 - support built-in data formats in XSSFDataFormat 46719 - fixed XSSFSheet.shiftRows to correctly preserve row heights diff --git a/src/java/org/apache/poi/hssf/model/Sheet.java b/src/java/org/apache/poi/hssf/model/Sheet.java index 80adc8457..825655642 100644 --- a/src/java/org/apache/poi/hssf/model/Sheet.java +++ b/src/java/org/apache/poi/hssf/model/Sheet.java @@ -222,8 +222,12 @@ public final class Sheet implements Model { } else if (windowTwo != null) { // probably 'Custom View Settings' sub-stream which is found between // USERSVIEWBEGIN(01AA) and USERSVIEWEND(01AB) + // TODO - create UsersViewAggregate to hold these sub-streams, and simplify this code a bit // This happens three times in test sample file "29982.xls" - if (rs.peekNextSid() != UnknownRecord.USERSVIEWEND_01AB) { + // Also several times in bugzilla samples 46840-23373 and 46840-23374 + if (recSid == UnknownRecord.HEADER_FOOTER_089C) { + _psBlock.addLateHeaderFooter(rs.getNext()); + } else if (rs.peekNextSid() != UnknownRecord.USERSVIEWEND_01AB) { // not quite the expected situation throw new RuntimeException("two Page Settings Blocks found in the same sheet"); } diff --git a/src/java/org/apache/poi/hssf/record/UnknownRecord.java b/src/java/org/apache/poi/hssf/record/UnknownRecord.java index e3cef59d9..0a7209332 100644 --- a/src/java/org/apache/poi/hssf/record/UnknownRecord.java +++ b/src/java/org/apache/poi/hssf/record/UnknownRecord.java @@ -48,6 +48,7 @@ public final class UnknownRecord extends StandardRecord { public static final int SHEETEXT_0862 = 0x0862; // OOO calls this SHEETLAYOUT public static final int SHEETPROTECTION_0867 = 0x0867; public static final int RANGEPROTECTION_0868 = 0x0868; + public static final int HEADER_FOOTER_089C = 0x089C; private int _sid; private byte[] _rawData; @@ -181,7 +182,7 @@ public final class UnknownRecord extends StandardRecord { case 0x0897: return "GUIDTYPELIB"; case 0x089A: return "MTRSETTINGS"; case 0x089B: return "COMPRESSPICTURES"; - case 0x089C: return "HEADERFOOTER"; + case HEADER_FOOTER_089C: return "HEADERFOOTER"; case 0x08A1: return "SHAPEPROPSSTREAM"; case 0x08A3: return "FORCEFULLCALCULATION"; case 0x08A4: return "SHAPEPROPSSTREAM"; diff --git a/src/java/org/apache/poi/hssf/record/aggregates/PageSettingsBlock.java b/src/java/org/apache/poi/hssf/record/aggregates/PageSettingsBlock.java index 828c0e31f..1d818aa03 100644 --- a/src/java/org/apache/poi/hssf/record/aggregates/PageSettingsBlock.java +++ b/src/java/org/apache/poi/hssf/record/aggregates/PageSettingsBlock.java @@ -71,6 +71,7 @@ public final class PageSettingsBlock extends RecordAggregate { private List _plsContinues; private PrintSetupRecord printSetup; private Record _bitmap; + private Record _headerFooter; public PageSettingsBlock(RecordStream rs) { while(true) { @@ -112,6 +113,7 @@ public final class PageSettingsBlock extends RecordAggregate { case UnknownRecord.PLS_004D: case PrintSetupRecord.sid: case UnknownRecord.BITMAP_00E9: + case UnknownRecord.HEADER_FOOTER_089C: // extra header/footer settings supported by Excel 2007 return true; } return false; @@ -164,6 +166,9 @@ public final class PageSettingsBlock extends RecordAggregate { case UnknownRecord.BITMAP_00E9: _bitmap = rs.getNext(); break; + case UnknownRecord.HEADER_FOOTER_089C: + _headerFooter = rs.getNext(); + break; default: // all other record types are not part of the PageSettingsBlock return false; @@ -224,6 +229,7 @@ public final class PageSettingsBlock extends RecordAggregate { } visitIfPresent(printSetup, rv); visitIfPresent(_bitmap, rv); + visitIfPresent(_headerFooter, rv); } private static void visitIfPresent(Record r, RecordVisitor rv) { if (r != null) { @@ -523,4 +529,16 @@ public final class PageSettingsBlock extends RecordAggregate { public HCenterRecord getHCenter() { return _hCenter; } + + /** + * HEADERFOOTER is new in 2007. Some apps seem to have scattered this record long after + * the {@link PageSettingsBlock} where it belongs. + * + */ + public void addLateHeaderFooter(Record rec) { + if (_headerFooter != null) { + throw new IllegalStateException("This page settings block already has a header/footer record"); + } + _headerFooter = rec; + } } diff --git a/src/testcases/org/apache/poi/hssf/record/aggregates/TestPageSettingBlock.java b/src/testcases/org/apache/poi/hssf/record/aggregates/TestPageSettingBlock.java index 518ed5f30..07a49fd1e 100644 --- a/src/testcases/org/apache/poi/hssf/record/aggregates/TestPageSettingBlock.java +++ b/src/testcases/org/apache/poi/hssf/record/aggregates/TestPageSettingBlock.java @@ -17,13 +17,28 @@ package org.apache.poi.hssf.record.aggregates; +import java.util.Arrays; + import junit.framework.AssertionFailedError; import junit.framework.TestCase; import org.apache.poi.hssf.HSSFTestDataSamples; +import org.apache.poi.hssf.model.RecordStream; +import org.apache.poi.hssf.model.Sheet; +import org.apache.poi.hssf.record.BOFRecord; +import org.apache.poi.hssf.record.DimensionsRecord; +import org.apache.poi.hssf.record.EOFRecord; +import org.apache.poi.hssf.record.FooterRecord; +import org.apache.poi.hssf.record.HeaderRecord; +import org.apache.poi.hssf.record.NumberRecord; +import org.apache.poi.hssf.record.Record; +import org.apache.poi.hssf.record.UnknownRecord; +import org.apache.poi.hssf.record.WindowTwoRecord; import org.apache.poi.hssf.usermodel.HSSFPrintSetup; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.hssf.usermodel.RecordInspector.RecordCollector; +import org.apache.poi.util.HexRead; /** * Tess for {@link PageSettingsBlock} @@ -47,4 +62,54 @@ public final class TestPageSettingBlock extends TestCase { throw new AssertionFailedError("Identified bug 46548: PageSettingBlock missing PrintSetupRecord record"); } } + + /** + * Bug 46840 occurred because POI failed to recognise HEADERFOOTER as part of the + * {@link PageSettingsBlock}. + * + */ + public void testHeaderFooter_bug46840() { + + int rowIx = 5; + int colIx = 6; + NumberRecord nr = new NumberRecord(); + nr.setRow(rowIx); + nr.setColumn((short) colIx); + nr.setValue(3.0); + + Record[] recs = { + BOFRecord.createSheetBOF(), + new HeaderRecord("&LSales Figures"), + new FooterRecord("&LJanuary"), + ur(UnknownRecord.HEADER_FOOTER_089C, "9C 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C4 60 00 00 00 00 00 00 00 00"), + new DimensionsRecord(), + new WindowTwoRecord(), + ur(UnknownRecord.USERSVIEWBEGIN_01AA, "ED 77 3B 86 BC 3F 37 4C A9 58 60 23 43 68 54 4B 01 00 00 00 64 00 00 00 40 00 00 00 02 00 00 00 3D 80 04 00 00 00 00 00 00 00 0C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 3F FF FF 01 00"), + new HeaderRecord("&LSales Figures"), + new FooterRecord("&LJanuary"), + ur(UnknownRecord.HEADER_FOOTER_089C, "9C 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C4 60 00 00 00 00 00 00 00 00"), + ur(UnknownRecord.USERSVIEWEND_01AB, "01, 00"), + + EOFRecord.instance, + }; + RecordStream rs = new RecordStream(Arrays.asList(recs), 0); + Sheet sheet; + try { + sheet = Sheet.createSheet(rs); + } catch (RuntimeException e) { + if (e.getMessage().equals("two Page Settings Blocks found in the same sheet")) { + throw new AssertionFailedError("Identified bug 46480"); + } + throw e; + } + + RecordCollector rv = new RecordCollector(); + sheet.visitContainedRecords(rv, rowIx); + Record[] outRecs = rv.getRecords(); + assertEquals(13, outRecs.length); + } + + private static UnknownRecord ur(int sid, String hexData) { + return new UnknownRecord(sid, HexRead.readFromString(hexData)); + } }