From a2bc714fa312065b4d4f9c8ecadd5f36f624fede Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Wed, 17 Jan 2007 16:59:45 +0000 Subject: [PATCH] Add support for ExHyperlink and ExHyperlinkAtom git-svn-id: https://svn.apache.org/repos/asf/jakarta/poi/trunk@497079 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/poi/hslf/record/ExHyperlink.java | 148 ++++++++++++++++++ .../poi/hslf/record/ExHyperlinkAtom.java | 117 ++++++++++++++ .../poi/hslf/record/InteractiveInfo.java | 2 +- .../apache/poi/hslf/record/RecordTypes.java | 4 +- .../org/apache/poi/hslf/data/WithLinks.ppt | Bin 20480 -> 20992 bytes .../poi/hslf/record/TestExHyperlink.java | 100 ++++++++++++ .../poi/hslf/record/TestExHyperlinkAtom.java | 106 +++++++++++++ 7 files changed, 474 insertions(+), 3 deletions(-) create mode 100644 src/scratchpad/src/org/apache/poi/hslf/record/ExHyperlink.java create mode 100644 src/scratchpad/src/org/apache/poi/hslf/record/ExHyperlinkAtom.java create mode 100644 src/scratchpad/testcases/org/apache/poi/hslf/record/TestExHyperlink.java create mode 100644 src/scratchpad/testcases/org/apache/poi/hslf/record/TestExHyperlinkAtom.java 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 6c4a8fdd58be661c11762c198e125aaf5f5f39a8..f08a81a09b78855b92cfceb0fe16f173a3c3820d 100644 GIT binary patch delta 1891 zcmbtVZA_b06h8OuTUJ`iTRyrL2DG$%R7yt~AF>fZ5V}HS8ybF?5CU;qFk=~W8kHar zGh7yjGEV%6q7gHaWyT=9KQs#l62&ZLCd)qjVN8rVW@e(POI#LzP@ns@6HG?@;rsU9 zd+vG8J@=mHoZff*12KM1Ooc_sLgg<)6mgDn%;_hh^>{p9oE%H#xX|^KSX8G}{YRl*le7h{MjZZHeg{94`!|CggnOEf&6D(r`{GP+;ih5vSLghm|R40&N?bf@aef zoMuH_*FSa5i?LQfIfg}isEpESFs4ulb6P(vdt;@?7`MiycR^d@&aLq=V}t*4P_(rJ zW4#Q(rBoPJg1i`Mok$*gv-a}kIJ9!F4Q+=PQ)w*ExS{sagN*On%HwRdO?cpaxhrNL zCJgarmU1gzEY42$>S;Hp@PgUr3RyHUW?mtwzGvfJnG3c zG;{HM{ZuWUI4fuAvQ?K`SivL-oFZ&AMVb&K+ zmNKfGZ}|KhMeZx!ROBJQKRH8@^OOD%*8bpk@%#Slr1W)C3MS{iq9mg4E4rYlXIwW- z{7GB3?kG(Y;*6djm{kPn%Vo!f=+-OCPYC5z7q66U=f2wS_|2W8yI)C_74wJ506{^GePlH(l%&}gWQ#@ly^7r& zZpoFG2YItr+#0E|&DO@IaQZK6zFXnu;Yv4`gnLa(S}D)BIe8`=5N7VLcJcSnR%QIm zT`f+2yfVOj)oz}y>{Z(>dxVUfExT^=)fzXeRlRCnR#G`!bxT;V{yEq!yA8YFt8a=_ z!{x6v(lV*;tG%UIEj-$qryqQHR46M6KJV2viY_|{3gL$7G!Y$Di6+fNALI7<1~<=d zh}@1?LN?%Oz){tc>A*GM4&WKUvw&vSpy&W4Xff^&hD*Q@YBQ z<}%+!Y$6k|i19!&0T9Uq2QmR2CVr$O$Yag6V#(=;X^9L7z%o$=rw}KMEAbQwp0B9! zSja+6hyXuw7#aIf^qE$)hio!8@9?$ecf~p%Yk6IK#qpNU(@Iejg0d5T%`YE4&^>{L zbD&`r9@cOiGbktq1#O_<5Ga^Hxy)hvRnV{o8qA=e7!`hSj92dK!e!@ z9d|{t`K8t);vU~_4W^!he;N4<=eN0(Y9ZqcyJ3sH@0ZX&;X=P9h#1| pnlAxy^V#-N9*SPaR}C{w+#O1; z&Ip|wUE3?4a#pU=w>&Q@K82fH9v*l3U3myi#Sj=)3fi)kL50jQ37Z2Pqln5zZfST%P-+2Po#U0j^Tfl%HTSqH{rRJ3m5~hpjT+Ex>z_Z@G};-aO12eU*1T3;7*if%)Di6S`>T zX=g59^W~Yy2MM3@Ja70yR{8E15@j6pmzeWwiAH`hIsZ9v*f{BbSeU8)M?sd12b{^7w`hCTo}7iY7~?|G zCB-u7{)Sf?T*fm&n-G)6OM7P&Rk7AO_(;&nixG_LL#j}puy3Kp`Ql!oPPnm} zs`-U)l@8aQ)?{D(W`TjFQ5e$6ralfpCneqq>nANPd2^V!C4SS&vuz>%vh1zQSx^p2 zH(eACN8J2exKCZwgBfNe*TZ{-#f1G8eWHp-DjK;v?BUglKD9%u6QP@SZP>R%&W#T`#)5^weX<>52n3=Eb^>|dE|gw$+5_j*@tK(1+K=MU@zFV4oCne zWN)U7xChhM%}>slJbPLdc=G;eHa9eD;xtE_r^RjlvH4`mXJB<+X}OQDws?8IWmpR! zLXeZVzV*msW1uf0HU`P-D8`D+LCB3l?f~SDrGZD%7$mPlvK4ZJkQ;^E0mvP5fQRH5 zid%gqJxvi$82gn_nQrmyX%;oN& zLq$o%J<(pZI_skxZmM$%8{Y`$^H0$erMFP49pALsAW=^-zRqEywFm-eLobj9^(Zmu~>^QD)xvI@~9=9l-8h)i~So>Ul>hb#< Di2HWS 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