diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExHyperlink.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExHyperlink.java new file mode 100644 index 000000000..b27a39617 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/record/ExHyperlink.java @@ -0,0 +1,148 @@ +/* +* 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.hslf.record; + +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.poi.util.LittleEndian; + +/** + * This class represents the data of a link in the document. + * @author Nick Burch + */ +public class ExHyperlink extends RecordContainer { + private byte[] _header; + private static long _type = 4055; + + // Links to our more interesting children + private ExHyperlinkAtom linkAtom; + private CString linkDetailsA; + private CString linkDetailsB; + private Comment2000Atom commentAtom; + + /** + * Returns the ExHyperlinkAtom of this link + */ + public ExHyperlinkAtom getExHyperlinkAtom() { return linkAtom; } + + /** + * Returns the URL of the link. + * TODO: Figure out which of detailsA or detailsB is the + * one that always holds it + */ + public String getLinkURL() { + return linkDetailsA.getText(); + } + + /** + * Sets the URL of the link + * TODO: Figure out if we should always set both + */ + public void setLinkURL(String url) { + linkDetailsA.setText(url); + linkDetailsB.setText(url); + } + + /** + * Get the link details (field A) + */ + public String _getDetailsA() { + return linkDetailsA.getText(); + } + /** + * Get the link details (field B) + */ + public String _getDetailsB() { + return linkDetailsB.getText(); + } + + /** + * Set things up, and find our more interesting children + */ + protected ExHyperlink(byte[] source, int start, int len) { + // Grab the header + _header = new byte[8]; + System.arraycopy(source,start,_header,0,8); + + // Find our children + _children = Record.findChildRecords(source,start+8,len-8); + findInterestingChildren(); + } + + /** + * Go through our child records, picking out the ones that are + * interesting, and saving those for use by the easy helper + * methods. + */ + private void findInterestingChildren() { + // First child should be the ExHyperlinkAtom + if(_children[0] instanceof ExHyperlinkAtom) { + linkAtom = (ExHyperlinkAtom)_children[0]; + } else { + throw new IllegalStateException("First child record wasn't a ExHyperlinkAtom, was of type " + _children[0].getRecordType()); + } + + // Second child should be the first link details + if(_children[1] instanceof CString) { + linkDetailsA = (CString)_children[1]; + } else { + throw new IllegalStateException("Second child record wasn't a CString, was of type " + _children[1].getRecordType()); + } + // Third child should be the second link details + if(_children[2] instanceof CString) { + linkDetailsB = (CString)_children[2]; + } else { + throw new IllegalStateException("Third child record wasn't a CString, was of type " + _children[2].getRecordType()); + } + } + + /** + * Create a new ExHyperlink, with blank fields + */ + public ExHyperlink() { + _header = new byte[8]; + _children = new Record[3]; + + // Setup our header block + _header[0] = 0x0f; // We are a container record + LittleEndian.putShort(_header, 2, (short)_type); + + // Setup our child records + CString csa = new CString(); + CString csb = new CString(); + csa.setCount(0x00); + csb.setCount(0x10); + _children[0] = new ExHyperlinkAtom(); + _children[1] = csa; + _children[2] = csb; + findInterestingChildren(); + } + + /** + * We are of type 4055 + */ + public long getRecordType() { return _type; } + + /** + * Write the contents of the record back, so it can be written + * to disk + */ + public void writeOut(OutputStream out) throws IOException { + writeOut(_header[0],_header[1],_type,_children,out); + } +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExHyperlinkAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExHyperlinkAtom.java new file mode 100644 index 000000000..14c37076b --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/record/ExHyperlinkAtom.java @@ -0,0 +1,117 @@ +/* ==================================================================== + 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.hslf.record; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Date; + +import org.apache.poi.hslf.util.SystemTimeUtils; +import org.apache.poi.util.LittleEndian; + +/** + * Tne atom that holds metadata on a specific Link in the document. + * (The actual link is held in a sibling CString record) + * + * @author Nick Burch + */ + +public class ExHyperlinkAtom extends RecordAtom +{ + /** + * Record header. + */ + private byte[] _header; + + /** + * Record data. + */ + private byte[] _data; + + /** + * Constructs a brand new link related atom record. + */ + protected ExHyperlinkAtom() { + _header = new byte[8]; + _data = new byte[4]; + + LittleEndian.putShort(_header, 2, (short)getRecordType()); + LittleEndian.putInt(_header, 4, _data.length); + + // It is fine for the other values to be zero + } + + /** + * Constructs the link related atom record from its + * source data. + * + * @param source the source data as a byte array. + * @param start the start offset into the byte array. + * @param len the length of the slice in the byte array. + */ + protected ExHyperlinkAtom(byte[] source, int start, int len) { + // Get the header. + _header = new byte[8]; + System.arraycopy(source,start,_header,0,8); + + // Get the record data. + _data = new byte[len-8]; + System.arraycopy(source,start+8,_data,0,len-8); + + // Must be at least 4 bytes long + if(_data.length < 4) { + throw new IllegalArgumentException("The length of the data for a ExHyperlinkAtom must be at least 4 bytes, but was only " + _data.length); + } + } + + /** + * Gets the link number. This will match the one in the + * InteractiveInfoAtom which uses the link. + * @return the link number + */ + public int getNumber() { + return LittleEndian.getInt(_data,0); + } + + /** + * Sets the link number + * @param number the link number. + */ + public void setNumber(int number) { + LittleEndian.putInt(_data,0,number); + } + + /** + * Gets the record type. + * @return the record type. + */ + public long getRecordType() { return RecordTypes.ExHyperlinkAtom.typeID; } + + /** + * Write the contents of the record back, so it can be written + * to disk + * + * @param out the output stream to write to. + * @throws IOException if an error occurs. + */ + public void writeOut(OutputStream out) throws IOException { + out.write(_header); + out.write(_data); + } +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/InteractiveInfo.java b/src/scratchpad/src/org/apache/poi/hslf/record/InteractiveInfo.java index 69ea76d30..f51d20ae5 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/InteractiveInfo.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/InteractiveInfo.java @@ -29,7 +29,7 @@ import org.apache.poi.util.LittleEndian; */ public class InteractiveInfo extends RecordContainer { private byte[] _header; - private static long _type = RecordTypes.InteractiveInfo.typeID; + private static long _type = 4082; // Links to our more interesting children private InteractiveInfoAtom infoAtom; diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java b/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java index 499f7bf0d..e2194ab7b 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java @@ -107,8 +107,8 @@ public class RecordTypes { public static final Type BookmarkEntityAtom = new Type(4048,null); public static final Type ExLinkAtom = new Type(4049,null); public static final Type SrKinsokuAtom = new Type(4050,null); - public static final Type ExHyperlinkAtom = new Type(4051,null); - public static final Type ExHyperlink = new Type(4055,null); + public static final Type ExHyperlinkAtom = new Type(4051,ExHyperlinkAtom.class); + public static final Type ExHyperlink = new Type(4055,ExHyperlink.class); public static final Type SlideNumberMCAtom = new Type(4056,null); public static final Type HeadersFooters = new Type(4057,null); public static final Type HeadersFootersAtom = new Type(4058,null); diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/data/WithLinks.ppt b/src/scratchpad/testcases/org/apache/poi/hslf/data/WithLinks.ppt index 6c4a8fdd5..f08a81a09 100644 Binary files a/src/scratchpad/testcases/org/apache/poi/hslf/data/WithLinks.ppt and b/src/scratchpad/testcases/org/apache/poi/hslf/data/WithLinks.ppt differ diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExHyperlink.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExHyperlink.java new file mode 100644 index 000000000..64a5dd30d --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExHyperlink.java @@ -0,0 +1,100 @@ + +/* ==================================================================== + 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.hslf.record; + + +import junit.framework.TestCase; +import java.io.ByteArrayOutputStream; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * Tests that ExHyperlink works properly. + * + * @author Nick Burch (nick at torchbox dot com) + */ +public class TestExHyperlink extends TestCase { + // From a real file + private byte[] data_a = new byte[] { + 0x0F, 00, 0xD7-256, 0x0F, 0xA8-256, 00, 00, 00, + + 00, 00, 0xD3-256, 0x0F, 04, 00, 00, 00, + 03, 00, 00, 00, + + 00, 00, 0xBA-256, 0x0F, 0x46, 00, 00, 00, + 0x68, 00, 0x74, 00, 0x74, 00, 0x70, 00, + 0x3A, 00, 0x2F, 00, 0x2F, 00, 0x6A, 00, + 0x61, 00, 0x6B, 00, 0x61, 00, 0x72, 00, + 0x74, 00, 0x61, 00, 0x2E, 00, 0x61, 00, + 0x70, 00, 0x61, 00, 0x63, 00, 0x68, 00, + 0x65, 00, 0x2E, 00, 0x6F, 00, 0x72, 00, + 0x67, 00, 0x2F, 00, 0x70, 00, 0x6F, 00, + 0x69, 00, 0x2F, 00, 0x68, 00, 0x73, 00, + 0x73, 00, 0x66, 00, 0x2F, 00, + + 0x10, 00, 0xBA-256, 0x0F, 0x46, 00, 00, 00, + 0x68, 00, 0x74, 00, 0x74, 00, 0x70, 00, + 0x3A, 00, 0x2F, 00, 0x2F, 00, 0x6A, 00, + 0x61, 00, 0x6B, 00, 0x61, 00, 0x72, 00, + 0x74, 00, 0x61, 00, 0x2E, 00, 0x61, 00, + 0x70, 00, 0x61, 00, 0x63, 00, 0x68, 00, + 0x65, 00, 0x2E, 00, 0x6F, 00, 0x72, 00, + 0x67, 00, 0x2F, 00, 0x70, 00, 0x6F, 00, + 0x69, 00, 0x2F, 00, 0x68, 00, 0x73, 00, + 0x73, 00, 0x66, 00, 0x2F, 00 + }; + + public void testRecordType() throws Exception { + ExHyperlink eh = new ExHyperlink(data_a, 0, data_a.length); + assertEquals(4055l, eh.getRecordType()); + } + + public void testNumber() throws Exception { + ExHyperlink eh = new ExHyperlink(data_a, 0, data_a.length); + assertEquals(3, eh.getExHyperlinkAtom().getNumber()); + } + + public void testLinkURL() throws Exception { + ExHyperlink eh = new ExHyperlink(data_a, 0, data_a.length); + assertEquals("http://jakarta.apache.org/poi/hssf/", eh.getLinkURL()); + } + public void testDetails() throws Exception { + ExHyperlink eh = new ExHyperlink(data_a, 0, data_a.length); + assertEquals("http://jakarta.apache.org/poi/hssf/", eh._getDetailsA()); + assertEquals("http://jakarta.apache.org/poi/hssf/", eh._getDetailsB()); + } + + public void testWrite() throws Exception { + ExHyperlink eh = new ExHyperlink(data_a, 0, data_a.length); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + eh.writeOut(baos); + byte[] b = baos.toByteArray(); + + assertEquals(data_a.length, b.length); + for(int i=0; i