You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

EscherAggregate.java 41KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063
  1. /* ====================================================================
  2. Licensed to the Apache Software Foundation (ASF) under one or more
  3. contributor license agreements. See the NOTICE file distributed with
  4. this work for additional information regarding copyright ownership.
  5. The ASF licenses this file to You under the Apache License, Version 2.0
  6. (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ==================================================================== */
  15. package org.apache.poi.hssf.record;
  16. import java.util.ArrayList;
  17. import java.util.HashMap;
  18. import java.util.Iterator;
  19. import java.util.List;
  20. import java.util.Map;
  21. import org.apache.poi.ddf.DefaultEscherRecordFactory;
  22. import org.apache.poi.ddf.EscherBoolProperty;
  23. import org.apache.poi.ddf.EscherChildAnchorRecord;
  24. import org.apache.poi.ddf.EscherClientAnchorRecord;
  25. import org.apache.poi.ddf.EscherClientDataRecord;
  26. import org.apache.poi.ddf.EscherContainerRecord;
  27. import org.apache.poi.ddf.EscherDgRecord;
  28. import org.apache.poi.ddf.EscherDggRecord;
  29. import org.apache.poi.ddf.EscherOptRecord;
  30. import org.apache.poi.ddf.EscherProperties;
  31. import org.apache.poi.ddf.EscherRecord;
  32. import org.apache.poi.ddf.EscherRecordFactory;
  33. import org.apache.poi.ddf.EscherSerializationListener;
  34. import org.apache.poi.ddf.EscherSimpleProperty;
  35. import org.apache.poi.ddf.EscherSpRecord;
  36. import org.apache.poi.ddf.EscherSpgrRecord;
  37. import org.apache.poi.ddf.EscherTextboxRecord;
  38. import org.apache.poi.hssf.model.AbstractShape;
  39. import org.apache.poi.hssf.model.CommentShape;
  40. import org.apache.poi.hssf.model.ConvertAnchor;
  41. import org.apache.poi.hssf.model.DrawingManager2;
  42. import org.apache.poi.hssf.model.TextboxShape;
  43. import org.apache.poi.hssf.usermodel.HSSFAnchor;
  44. import org.apache.poi.hssf.usermodel.HSSFChildAnchor;
  45. import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
  46. import org.apache.poi.hssf.usermodel.HSSFPatriarch;
  47. import org.apache.poi.hssf.usermodel.HSSFPicture;
  48. import org.apache.poi.hssf.usermodel.HSSFShape;
  49. import org.apache.poi.hssf.usermodel.HSSFShapeContainer;
  50. import org.apache.poi.hssf.usermodel.HSSFShapeGroup;
  51. import org.apache.poi.hssf.usermodel.HSSFSimpleShape;
  52. import org.apache.poi.hssf.usermodel.HSSFTextbox;
  53. import org.apache.poi.util.POILogFactory;
  54. import org.apache.poi.util.POILogger;
  55. /**
  56. * This class is used to aggregate the MSODRAWING and OBJ record
  57. * combinations. This is necessary due to the bizare way in which
  58. * these records are serialized. What happens is that you get a
  59. * combination of MSODRAWING -> OBJ -> MSODRAWING -> OBJ records
  60. * but the escher records are serialized _across_ the MSODRAWING
  61. * records.
  62. * <p>
  63. * It gets even worse when you start looking at TXO records.
  64. * <p>
  65. * So what we do with this class is aggregate lazily. That is
  66. * we don't aggregate the MSODRAWING -> OBJ records unless we
  67. * need to modify them.
  68. *
  69. *
  70. * @author Glen Stampoultzis (glens at apache.org)
  71. */
  72. public final class EscherAggregate extends AbstractEscherHolderRecord {
  73. public static final short sid = 9876; // not a real sid - dummy value
  74. private static POILogger log = POILogFactory.getLogger(EscherAggregate.class);
  75. public static final short ST_MIN = (short) 0;
  76. public static final short ST_NOT_PRIMATIVE = ST_MIN;
  77. public static final short ST_RECTANGLE = (short) 1;
  78. public static final short ST_ROUNDRECTANGLE = (short) 2;
  79. public static final short ST_ELLIPSE = (short) 3;
  80. public static final short ST_DIAMOND = (short) 4;
  81. public static final short ST_ISOCELESTRIANGLE = (short) 5;
  82. public static final short ST_RIGHTTRIANGLE = (short) 6;
  83. public static final short ST_PARALLELOGRAM = (short) 7;
  84. public static final short ST_TRAPEZOID = (short) 8;
  85. public static final short ST_HEXAGON = (short) 9;
  86. public static final short ST_OCTAGON = (short) 10;
  87. public static final short ST_PLUS = (short) 11;
  88. public static final short ST_STAR = (short) 12;
  89. public static final short ST_ARROW = (short) 13;
  90. public static final short ST_THICKARROW = (short) 14;
  91. public static final short ST_HOMEPLATE = (short) 15;
  92. public static final short ST_CUBE = (short) 16;
  93. public static final short ST_BALLOON = (short) 17;
  94. public static final short ST_SEAL = (short) 18;
  95. public static final short ST_ARC = (short) 19;
  96. public static final short ST_LINE = (short) 20;
  97. public static final short ST_PLAQUE = (short) 21;
  98. public static final short ST_CAN = (short) 22;
  99. public static final short ST_DONUT = (short) 23;
  100. public static final short ST_TEXTSIMPLE = (short) 24;
  101. public static final short ST_TEXTOCTAGON = (short) 25;
  102. public static final short ST_TEXTHEXAGON = (short) 26;
  103. public static final short ST_TEXTCURVE = (short) 27;
  104. public static final short ST_TEXTWAVE = (short) 28;
  105. public static final short ST_TEXTRING = (short) 29;
  106. public static final short ST_TEXTONCURVE = (short) 30;
  107. public static final short ST_TEXTONRING = (short) 31;
  108. public static final short ST_STRAIGHTCONNECTOR1 = (short) 32;
  109. public static final short ST_BENTCONNECTOR2 = (short) 33;
  110. public static final short ST_BENTCONNECTOR3 = (short) 34;
  111. public static final short ST_BENTCONNECTOR4 = (short) 35;
  112. public static final short ST_BENTCONNECTOR5 = (short) 36;
  113. public static final short ST_CURVEDCONNECTOR2 = (short) 37;
  114. public static final short ST_CURVEDCONNECTOR3 = (short) 38;
  115. public static final short ST_CURVEDCONNECTOR4 = (short) 39;
  116. public static final short ST_CURVEDCONNECTOR5 = (short) 40;
  117. public static final short ST_CALLOUT1 = (short) 41;
  118. public static final short ST_CALLOUT2 = (short) 42;
  119. public static final short ST_CALLOUT3 = (short) 43;
  120. public static final short ST_ACCENTCALLOUT1 = (short) 44;
  121. public static final short ST_ACCENTCALLOUT2 = (short) 45;
  122. public static final short ST_ACCENTCALLOUT3 = (short) 46;
  123. public static final short ST_BORDERCALLOUT1 = (short) 47;
  124. public static final short ST_BORDERCALLOUT2 = (short) 48;
  125. public static final short ST_BORDERCALLOUT3 = (short) 49;
  126. public static final short ST_ACCENTBORDERCALLOUT1 = (short) 50;
  127. public static final short ST_ACCENTBORDERCALLOUT2 = (short) 51;
  128. public static final short ST_ACCENTBORDERCALLOUT3 = (short) 52;
  129. public static final short ST_RIBBON = (short) 53;
  130. public static final short ST_RIBBON2 = (short) 54;
  131. public static final short ST_CHEVRON = (short) 55;
  132. public static final short ST_PENTAGON = (short) 56;
  133. public static final short ST_NOSMOKING = (short) 57;
  134. public static final short ST_SEAL8 = (short) 58;
  135. public static final short ST_SEAL16 = (short) 59;
  136. public static final short ST_SEAL32 = (short) 60;
  137. public static final short ST_WEDGERECTCALLOUT = (short) 61;
  138. public static final short ST_WEDGERRECTCALLOUT = (short) 62;
  139. public static final short ST_WEDGEELLIPSECALLOUT = (short) 63;
  140. public static final short ST_WAVE = (short) 64;
  141. public static final short ST_FOLDEDCORNER = (short) 65;
  142. public static final short ST_LEFTARROW = (short) 66;
  143. public static final short ST_DOWNARROW = (short) 67;
  144. public static final short ST_UPARROW = (short) 68;
  145. public static final short ST_LEFTRIGHTARROW = (short) 69;
  146. public static final short ST_UPDOWNARROW = (short) 70;
  147. public static final short ST_IRREGULARSEAL1 = (short) 71;
  148. public static final short ST_IRREGULARSEAL2 = (short) 72;
  149. public static final short ST_LIGHTNINGBOLT = (short) 73;
  150. public static final short ST_HEART = (short) 74;
  151. public static final short ST_PICTUREFRAME = (short) 75;
  152. public static final short ST_QUADARROW = (short) 76;
  153. public static final short ST_LEFTARROWCALLOUT = (short) 77;
  154. public static final short ST_RIGHTARROWCALLOUT = (short) 78;
  155. public static final short ST_UPARROWCALLOUT = (short) 79;
  156. public static final short ST_DOWNARROWCALLOUT = (short) 80;
  157. public static final short ST_LEFTRIGHTARROWCALLOUT = (short) 81;
  158. public static final short ST_UPDOWNARROWCALLOUT = (short) 82;
  159. public static final short ST_QUADARROWCALLOUT = (short) 83;
  160. public static final short ST_BEVEL = (short) 84;
  161. public static final short ST_LEFTBRACKET = (short) 85;
  162. public static final short ST_RIGHTBRACKET = (short) 86;
  163. public static final short ST_LEFTBRACE = (short) 87;
  164. public static final short ST_RIGHTBRACE = (short) 88;
  165. public static final short ST_LEFTUPARROW = (short) 89;
  166. public static final short ST_BENTUPARROW = (short) 90;
  167. public static final short ST_BENTARROW = (short) 91;
  168. public static final short ST_SEAL24 = (short) 92;
  169. public static final short ST_STRIPEDRIGHTARROW = (short) 93;
  170. public static final short ST_NOTCHEDRIGHTARROW = (short) 94;
  171. public static final short ST_BLOCKARC = (short) 95;
  172. public static final short ST_SMILEYFACE = (short) 96;
  173. public static final short ST_VERTICALSCROLL = (short) 97;
  174. public static final short ST_HORIZONTALSCROLL = (short) 98;
  175. public static final short ST_CIRCULARARROW = (short) 99;
  176. public static final short ST_NOTCHEDCIRCULARARROW = (short) 100;
  177. public static final short ST_UTURNARROW = (short) 101;
  178. public static final short ST_CURVEDRIGHTARROW = (short) 102;
  179. public static final short ST_CURVEDLEFTARROW = (short) 103;
  180. public static final short ST_CURVEDUPARROW = (short) 104;
  181. public static final short ST_CURVEDDOWNARROW = (short) 105;
  182. public static final short ST_CLOUDCALLOUT = (short) 106;
  183. public static final short ST_ELLIPSERIBBON = (short) 107;
  184. public static final short ST_ELLIPSERIBBON2 = (short) 108;
  185. public static final short ST_FLOWCHARTPROCESS = (short) 109;
  186. public static final short ST_FLOWCHARTDECISION = (short) 110;
  187. public static final short ST_FLOWCHARTINPUTOUTPUT = (short) 111;
  188. public static final short ST_FLOWCHARTPREDEFINEDPROCESS = (short) 112;
  189. public static final short ST_FLOWCHARTINTERNALSTORAGE = (short) 113;
  190. public static final short ST_FLOWCHARTDOCUMENT = (short) 114;
  191. public static final short ST_FLOWCHARTMULTIDOCUMENT = (short) 115;
  192. public static final short ST_FLOWCHARTTERMINATOR = (short) 116;
  193. public static final short ST_FLOWCHARTPREPARATION = (short) 117;
  194. public static final short ST_FLOWCHARTMANUALINPUT = (short) 118;
  195. public static final short ST_FLOWCHARTMANUALOPERATION = (short) 119;
  196. public static final short ST_FLOWCHARTCONNECTOR = (short) 120;
  197. public static final short ST_FLOWCHARTPUNCHEDCARD = (short) 121;
  198. public static final short ST_FLOWCHARTPUNCHEDTAPE = (short) 122;
  199. public static final short ST_FLOWCHARTSUMMINGJUNCTION = (short) 123;
  200. public static final short ST_FLOWCHARTOR = (short) 124;
  201. public static final short ST_FLOWCHARTCOLLATE = (short) 125;
  202. public static final short ST_FLOWCHARTSORT = (short) 126;
  203. public static final short ST_FLOWCHARTEXTRACT = (short) 127;
  204. public static final short ST_FLOWCHARTMERGE = (short) 128;
  205. public static final short ST_FLOWCHARTOFFLINESTORAGE = (short) 129;
  206. public static final short ST_FLOWCHARTONLINESTORAGE = (short) 130;
  207. public static final short ST_FLOWCHARTMAGNETICTAPE = (short) 131;
  208. public static final short ST_FLOWCHARTMAGNETICDISK = (short) 132;
  209. public static final short ST_FLOWCHARTMAGNETICDRUM = (short) 133;
  210. public static final short ST_FLOWCHARTDISPLAY = (short) 134;
  211. public static final short ST_FLOWCHARTDELAY = (short) 135;
  212. public static final short ST_TEXTPLAINTEXT = (short) 136;
  213. public static final short ST_TEXTSTOP = (short) 137;
  214. public static final short ST_TEXTTRIANGLE = (short) 138;
  215. public static final short ST_TEXTTRIANGLEINVERTED = (short) 139;
  216. public static final short ST_TEXTCHEVRON = (short) 140;
  217. public static final short ST_TEXTCHEVRONINVERTED = (short) 141;
  218. public static final short ST_TEXTRINGINSIDE = (short) 142;
  219. public static final short ST_TEXTRINGOUTSIDE = (short) 143;
  220. public static final short ST_TEXTARCHUPCURVE = (short) 144;
  221. public static final short ST_TEXTARCHDOWNCURVE = (short) 145;
  222. public static final short ST_TEXTCIRCLECURVE = (short) 146;
  223. public static final short ST_TEXTBUTTONCURVE = (short) 147;
  224. public static final short ST_TEXTARCHUPPOUR = (short) 148;
  225. public static final short ST_TEXTARCHDOWNPOUR = (short) 149;
  226. public static final short ST_TEXTCIRCLEPOUR = (short) 150;
  227. public static final short ST_TEXTBUTTONPOUR = (short) 151;
  228. public static final short ST_TEXTCURVEUP = (short) 152;
  229. public static final short ST_TEXTCURVEDOWN = (short) 153;
  230. public static final short ST_TEXTCASCADEUP = (short) 154;
  231. public static final short ST_TEXTCASCADEDOWN = (short) 155;
  232. public static final short ST_TEXTWAVE1 = (short) 156;
  233. public static final short ST_TEXTWAVE2 = (short) 157;
  234. public static final short ST_TEXTWAVE3 = (short) 158;
  235. public static final short ST_TEXTWAVE4 = (short) 159;
  236. public static final short ST_TEXTINFLATE = (short) 160;
  237. public static final short ST_TEXTDEFLATE = (short) 161;
  238. public static final short ST_TEXTINFLATEBOTTOM = (short) 162;
  239. public static final short ST_TEXTDEFLATEBOTTOM = (short) 163;
  240. public static final short ST_TEXTINFLATETOP = (short) 164;
  241. public static final short ST_TEXTDEFLATETOP = (short) 165;
  242. public static final short ST_TEXTDEFLATEINFLATE = (short) 166;
  243. public static final short ST_TEXTDEFLATEINFLATEDEFLATE = (short) 167;
  244. public static final short ST_TEXTFADERIGHT = (short) 168;
  245. public static final short ST_TEXTFADELEFT = (short) 169;
  246. public static final short ST_TEXTFADEUP = (short) 170;
  247. public static final short ST_TEXTFADEDOWN = (short) 171;
  248. public static final short ST_TEXTSLANTUP = (short) 172;
  249. public static final short ST_TEXTSLANTDOWN = (short) 173;
  250. public static final short ST_TEXTCANUP = (short) 174;
  251. public static final short ST_TEXTCANDOWN = (short) 175;
  252. public static final short ST_FLOWCHARTALTERNATEPROCESS = (short) 176;
  253. public static final short ST_FLOWCHARTOFFPAGECONNECTOR = (short) 177;
  254. public static final short ST_CALLOUT90 = (short) 178;
  255. public static final short ST_ACCENTCALLOUT90 = (short) 179;
  256. public static final short ST_BORDERCALLOUT90 = (short) 180;
  257. public static final short ST_ACCENTBORDERCALLOUT90 = (short) 181;
  258. public static final short ST_LEFTRIGHTUPARROW = (short) 182;
  259. public static final short ST_SUN = (short) 183;
  260. public static final short ST_MOON = (short) 184;
  261. public static final short ST_BRACKETPAIR = (short) 185;
  262. public static final short ST_BRACEPAIR = (short) 186;
  263. public static final short ST_SEAL4 = (short) 187;
  264. public static final short ST_DOUBLEWAVE = (short) 188;
  265. public static final short ST_ACTIONBUTTONBLANK = (short) 189;
  266. public static final short ST_ACTIONBUTTONHOME = (short) 190;
  267. public static final short ST_ACTIONBUTTONHELP = (short) 191;
  268. public static final short ST_ACTIONBUTTONINFORMATION = (short) 192;
  269. public static final short ST_ACTIONBUTTONFORWARDNEXT = (short) 193;
  270. public static final short ST_ACTIONBUTTONBACKPREVIOUS = (short) 194;
  271. public static final short ST_ACTIONBUTTONEND = (short) 195;
  272. public static final short ST_ACTIONBUTTONBEGINNING = (short) 196;
  273. public static final short ST_ACTIONBUTTONRETURN = (short) 197;
  274. public static final short ST_ACTIONBUTTONDOCUMENT = (short) 198;
  275. public static final short ST_ACTIONBUTTONSOUND = (short) 199;
  276. public static final short ST_ACTIONBUTTONMOVIE = (short) 200;
  277. public static final short ST_HOSTCONTROL = (short) 201;
  278. public static final short ST_TEXTBOX = (short) 202;
  279. public static final short ST_NIL = (short) 0x0FFF;
  280. protected HSSFPatriarch patriarch;
  281. /** Maps shape container objects to their {@link TextObjectRecord} or {@link ObjRecord} */
  282. private Map<EscherRecord, Record> shapeToObj = new HashMap<EscherRecord, Record>();
  283. private DrawingManager2 drawingManager;
  284. private short drawingGroupId;
  285. /**
  286. * list of "tail" records that need to be serialized after all drawing group records
  287. */
  288. private List tailRec = new ArrayList();
  289. public EscherAggregate( DrawingManager2 drawingManager )
  290. {
  291. this.drawingManager = drawingManager;
  292. }
  293. /**
  294. * @return Returns the current sid.
  295. */
  296. public short getSid()
  297. {
  298. return sid;
  299. }
  300. /**
  301. * Calculates the string representation of this record. This is
  302. * simply a dump of all the records.
  303. */
  304. public String toString()
  305. {
  306. String nl = System.getProperty( "line.separtor" );
  307. StringBuffer result = new StringBuffer();
  308. result.append( '[' ).append( getRecordName() ).append( ']' + nl );
  309. for ( Iterator iterator = getEscherRecords().iterator(); iterator.hasNext(); )
  310. {
  311. EscherRecord escherRecord = (EscherRecord) iterator.next();
  312. result.append( escherRecord.toString() );
  313. }
  314. result.append( "[/" ).append( getRecordName() ).append( ']' + nl );
  315. return result.toString();
  316. }
  317. public String toXml(String tab){
  318. StringBuilder builder = new StringBuilder();
  319. builder.append(tab).append("<").append(getRecordName()).append(">\n");
  320. for ( Iterator iterator = getEscherRecords().iterator(); iterator.hasNext(); )
  321. {
  322. EscherRecord escherRecord = (EscherRecord) iterator.next();
  323. builder.append( escherRecord.toXml(tab+"\t") );
  324. }
  325. builder.append(tab).append("</").append(getRecordName()).append(">\n");
  326. return builder.toString();
  327. }
  328. /**
  329. * Collapses the drawing records into an aggregate.
  330. */
  331. public static EscherAggregate createAggregate( List records, int locFirstDrawingRecord, DrawingManager2 drawingManager )
  332. {
  333. // Keep track of any shape records created so we can match them back to the object id's.
  334. // Textbox objects are also treated as shape objects.
  335. final List<EscherRecord> shapeRecords = new ArrayList<EscherRecord>();
  336. EscherRecordFactory recordFactory = new DefaultEscherRecordFactory()
  337. {
  338. public EscherRecord createRecord( byte[] data, int offset )
  339. {
  340. EscherRecord r = super.createRecord( data, offset );
  341. if ( r.getRecordId() == EscherClientDataRecord.RECORD_ID || r.getRecordId() == EscherTextboxRecord.RECORD_ID )
  342. {
  343. shapeRecords.add( r );
  344. }
  345. return r;
  346. }
  347. };
  348. // Calculate the size of the buffer
  349. EscherAggregate agg = new EscherAggregate(drawingManager);
  350. int loc = locFirstDrawingRecord;
  351. int dataSize = 0;
  352. while ( loc + 1 < records.size()
  353. && sid( records, loc ) == DrawingRecord.sid
  354. && isObjectRecord( records, loc + 1 ) )
  355. {
  356. dataSize += ( (DrawingRecord) records.get( loc ) ).getData().length;
  357. loc += 2;
  358. while ( loc + 1 < records.size()
  359. && sid( records, loc ) == ContinueRecord.sid
  360. && isObjectRecord( records, loc + 1 ) )
  361. {
  362. dataSize += ( (ContinueRecord) records.get( loc ) ).getData().length;
  363. loc += 2;
  364. }
  365. }
  366. // Create one big buffer
  367. byte buffer[] = new byte[dataSize];
  368. int offset = 0;
  369. loc = locFirstDrawingRecord;
  370. while ( loc + 1 < records.size()
  371. && sid( records, loc ) == DrawingRecord.sid
  372. && isObjectRecord( records, loc + 1 ) )
  373. {
  374. DrawingRecord drawingRecord = (DrawingRecord) records.get( loc );
  375. System.arraycopy( drawingRecord.getData(), 0, buffer, offset, drawingRecord.getData().length );
  376. offset += drawingRecord.getData().length;
  377. loc += 2;
  378. while ( loc + 1 < records.size()
  379. && sid( records, loc ) == ContinueRecord.sid
  380. && isObjectRecord( records, loc + 1 ) )
  381. {
  382. ContinueRecord continueRecord = (ContinueRecord) records.get( loc );
  383. System.arraycopy( continueRecord.getData(), 0, buffer, offset, continueRecord.getData().length );
  384. offset += continueRecord.getData().length;
  385. loc += 2;
  386. }
  387. }
  388. // Decode the shapes
  389. // agg.escherRecords = new ArrayList();
  390. int pos = 0;
  391. while ( pos < dataSize )
  392. {
  393. EscherRecord r = recordFactory.createRecord( buffer, pos );
  394. int bytesRead = r.fillFields( buffer, pos, recordFactory );
  395. agg.addEscherRecord( r );
  396. pos += bytesRead;
  397. }
  398. // Associate the object records with the shapes
  399. loc = locFirstDrawingRecord;
  400. int shapeIndex = 0;
  401. agg.shapeToObj = new HashMap<EscherRecord, Record>();
  402. while ( loc + 1 < records.size()
  403. && sid( records, loc ) == DrawingRecord.sid
  404. && isObjectRecord( records, loc + 1 ) )
  405. {
  406. Record objRecord = (Record) records.get( loc + 1 );
  407. agg.shapeToObj.put( shapeRecords.get( shapeIndex++ ), objRecord );
  408. loc += 2;
  409. while ( loc + 1 < records.size()
  410. && sid( records, loc ) == ContinueRecord.sid
  411. && isObjectRecord( records, loc + 1 ) )
  412. {
  413. objRecord = (Record) records.get( loc + 1 );
  414. agg.shapeToObj.put( shapeRecords.get( shapeIndex++ ), objRecord );
  415. loc += 2;
  416. }
  417. }
  418. return agg;
  419. }
  420. /**
  421. * Serializes this aggregate to a byte array. Since this is an aggregate
  422. * record it will effectively serialize the aggregated records.
  423. *
  424. * @param offset The offset into the start of the array.
  425. * @param data The byte array to serialize to.
  426. * @return The number of bytes serialized.
  427. */
  428. public int serialize( int offset, byte[] data )
  429. {
  430. convertUserModelToRecords();
  431. // Determine buffer size
  432. List records = getEscherRecords();
  433. int size = getEscherRecordSize( records );
  434. byte[] buffer = new byte[size];
  435. // Serialize escher records into one big data structure and keep note of ending offsets.
  436. final List spEndingOffsets = new ArrayList();
  437. final List shapes = new ArrayList();
  438. int pos = 0;
  439. for ( Iterator iterator = records.iterator(); iterator.hasNext(); )
  440. {
  441. EscherRecord e = (EscherRecord) iterator.next();
  442. pos += e.serialize( pos, buffer, new EscherSerializationListener()
  443. {
  444. public void beforeRecordSerialize( int offset, short recordId, EscherRecord record )
  445. {
  446. }
  447. public void afterRecordSerialize( int offset, short recordId, int size, EscherRecord record )
  448. {
  449. if ( recordId == EscherClientDataRecord.RECORD_ID || recordId == EscherTextboxRecord.RECORD_ID )
  450. {
  451. spEndingOffsets.add( Integer.valueOf( offset ) );
  452. shapes.add( record );
  453. }
  454. }
  455. } );
  456. }
  457. // todo: fix this
  458. shapes.add( 0, null );
  459. spEndingOffsets.add( 0, null );
  460. // Split escher records into separate MSODRAWING and OBJ, TXO records. (We don't break on
  461. // the first one because it's the patriach).
  462. pos = offset;
  463. for ( int i = 1; i < shapes.size(); i++ )
  464. {
  465. int endOffset = ( (Integer) spEndingOffsets.get( i ) ).intValue() - 1;
  466. int startOffset;
  467. if ( i == 1 )
  468. startOffset = 0;
  469. else
  470. startOffset = ( (Integer) spEndingOffsets.get( i - 1 ) ).intValue();
  471. // Create and write a new MSODRAWING record
  472. DrawingRecord drawing = new DrawingRecord();
  473. byte[] drawingData = new byte[endOffset - startOffset + 1];
  474. System.arraycopy( buffer, startOffset, drawingData, 0, drawingData.length );
  475. drawing.setData( drawingData );
  476. int temp = drawing.serialize( pos, data );
  477. pos += temp;
  478. // Write the matching OBJ record
  479. Record obj = shapeToObj.get( shapes.get( i ) );
  480. temp = obj.serialize( pos, data );
  481. pos += temp;
  482. }
  483. // write records that need to be serialized after all drawing group records
  484. for ( int i = 0; i < tailRec.size(); i++ )
  485. {
  486. Record rec = (Record)tailRec.get(i);
  487. pos += rec.serialize( pos, data );
  488. }
  489. int bytesWritten = pos - offset;
  490. if ( bytesWritten != getRecordSize() )
  491. throw new RecordFormatException( bytesWritten + " bytes written but getRecordSize() reports " + getRecordSize() );
  492. return bytesWritten;
  493. }
  494. /**
  495. * How many bytes do the raw escher records contain.
  496. * @param records List of escher records
  497. * @return the number of bytes
  498. */
  499. private int getEscherRecordSize( List records )
  500. {
  501. int size = 0;
  502. for ( Iterator iterator = records.iterator(); iterator.hasNext(); )
  503. size += ( (EscherRecord) iterator.next() ).getRecordSize();
  504. return size;
  505. }
  506. public int getRecordSize() {
  507. // TODO - convert this to RecordAggregate
  508. convertUserModelToRecords();
  509. List records = getEscherRecords();
  510. int rawEscherSize = getEscherRecordSize( records );
  511. int drawingRecordSize = rawEscherSize + ( shapeToObj.size() ) * 4;
  512. int objRecordSize = 0;
  513. for ( Iterator iterator = shapeToObj.values().iterator(); iterator.hasNext(); )
  514. {
  515. Record r = (Record) iterator.next();
  516. objRecordSize += r.getRecordSize();
  517. }
  518. int tailRecordSize = 0;
  519. for ( Iterator iterator = tailRec.iterator(); iterator.hasNext(); )
  520. {
  521. Record r = (Record) iterator.next();
  522. tailRecordSize += r.getRecordSize();
  523. }
  524. return drawingRecordSize + objRecordSize + tailRecordSize;
  525. }
  526. /**
  527. * Associates an escher record to an OBJ record or a TXO record.
  528. */
  529. Object associateShapeToObjRecord( EscherRecord r, ObjRecord objRecord )
  530. {
  531. return shapeToObj.put( r, objRecord );
  532. }
  533. public HSSFPatriarch getPatriarch()
  534. {
  535. return patriarch;
  536. }
  537. public void setPatriarch( HSSFPatriarch patriarch )
  538. {
  539. this.patriarch = patriarch;
  540. }
  541. /**
  542. * Converts the Records into UserModel
  543. * objects on the bound HSSFPatriarch
  544. */
  545. public void convertRecordsToUserModel() {
  546. if(patriarch == null) {
  547. throw new IllegalStateException("Must call setPatriarch() first");
  548. }
  549. // The top level container ought to have
  550. // the DgRecord and the container of one container
  551. // per shape group (patriach overall first)
  552. EscherContainerRecord topContainer = getEscherContainer();
  553. if(topContainer == null) {
  554. return;
  555. }
  556. topContainer = topContainer.getChildContainers().get(0);
  557. List tcc = topContainer.getChildContainers();
  558. if(tcc.size() == 0) {
  559. throw new IllegalStateException("No child escher containers at the point that should hold the patriach data, and one container per top level shape!");
  560. }
  561. // First up, get the patriach position
  562. // This is in the first EscherSpgrRecord, in
  563. // the first container, with a EscherSRecord too
  564. EscherContainerRecord patriachContainer =
  565. (EscherContainerRecord)tcc.get(0);
  566. EscherSpgrRecord spgr = null;
  567. for(Iterator<EscherRecord> it = patriachContainer.getChildIterator(); it.hasNext();) {
  568. EscherRecord r = it.next();
  569. if(r instanceof EscherSpgrRecord) {
  570. spgr = (EscherSpgrRecord)r;
  571. break;
  572. }
  573. }
  574. if(spgr != null) {
  575. patriarch.setCoordinates(
  576. spgr.getRectX1(), spgr.getRectY1(),
  577. spgr.getRectX2(), spgr.getRectY2()
  578. );
  579. }
  580. convertRecordsToUserModelRecursive(tcc, patriarch, null);
  581. // Now, clear any trace of what records make up
  582. // the patriarch
  583. // Otherwise, everything will go horribly wrong
  584. // when we try to write out again....
  585. // clearEscherRecords();
  586. drawingManager.getDgg().setFileIdClusters(new EscherDggRecord.FileIdCluster[0]);
  587. // TODO: Support converting our records
  588. // back into shapes
  589. // log.log(POILogger.WARN, "Not processing objects into Patriarch!");
  590. }
  591. private static void convertRecordsToUserModelRecursive(List tcc, HSSFShapeContainer container, HSSFShape parent) {
  592. // Now process the containers for each group
  593. // and objects
  594. for(int i=1; i<tcc.size(); i++) {
  595. EscherContainerRecord shapeContainer = (EscherContainerRecord)tcc.get(i);
  596. // Could be a group, or a base object
  597. if (shapeContainer.getRecordId() == EscherContainerRecord.SPGR_CONTAINER)
  598. {
  599. // Group
  600. final int shapeChildren = shapeContainer.getChildRecords().size();
  601. if (shapeChildren > 0)
  602. {
  603. HSSFShapeGroup group = new HSSFShapeGroup( parent, new HSSFClientAnchor() );
  604. addToParentOrContainer(group, container, parent);
  605. EscherContainerRecord groupContainer = (EscherContainerRecord) shapeContainer.getChild( 0 );
  606. convertRecordsToUserModel( groupContainer, group );
  607. if (shapeChildren>1){
  608. convertRecordsToUserModelRecursive(shapeContainer.getChildRecords(), container, group);
  609. }
  610. } else
  611. {
  612. log.log( POILogger.WARN,
  613. "Found drawing group without children." );
  614. }
  615. } else if (shapeContainer.getRecordId() == EscherContainerRecord.SP_CONTAINER)
  616. {
  617. EscherSpRecord spRecord = shapeContainer
  618. .getChildById( EscherSpRecord.RECORD_ID );
  619. int type = spRecord.getShapeType();
  620. switch (type)
  621. {
  622. case ST_TEXTBOX:
  623. HSSFTextbox box = new HSSFTextbox( parent,
  624. new HSSFClientAnchor() );
  625. addToParentOrContainer(box, container, parent);
  626. convertRecordsToUserModel( shapeContainer, box );
  627. break;
  628. case ST_PICTUREFRAME:
  629. // Duplicated from
  630. // org.apache.poi.hslf.model.Picture.getPictureIndex()
  631. EscherOptRecord opt = (EscherOptRecord) getEscherChild(
  632. shapeContainer, EscherOptRecord.RECORD_ID );
  633. EscherSimpleProperty prop = (EscherSimpleProperty)opt.lookup(
  634. EscherProperties.BLIP__BLIPTODISPLAY );
  635. if (prop == null)
  636. {
  637. log.log( POILogger.WARN,
  638. "Picture index for picture shape not found." );
  639. } else
  640. {
  641. int pictureIndex = prop.getPropertyValue();
  642. EscherClientAnchorRecord anchorRecord = (EscherClientAnchorRecord) getEscherChild(
  643. shapeContainer,
  644. EscherClientAnchorRecord.RECORD_ID );
  645. EscherChildAnchorRecord childRecord = (EscherChildAnchorRecord) getEscherChild(
  646. shapeContainer,
  647. EscherChildAnchorRecord.RECORD_ID );
  648. if (anchorRecord!=null && childRecord!=null){
  649. log.log( POILogger.WARN, "Picture with both CLIENT and CHILD anchor: "+ type );
  650. }
  651. HSSFAnchor anchor;
  652. if (anchorRecord!=null){
  653. anchor = toClientAnchor(anchorRecord);
  654. }else{
  655. anchor = toChildAnchor(childRecord);
  656. }
  657. HSSFPicture picture = new HSSFPicture( parent, anchor );
  658. picture.setPictureIndex( pictureIndex );
  659. addToParentOrContainer(picture, container, parent);
  660. }
  661. break;
  662. default:
  663. final HSSFSimpleShape shape = new HSSFSimpleShape( parent,
  664. new HSSFClientAnchor() );
  665. addToParentOrContainer(shape, container, parent);
  666. convertRecordsToUserModel( shapeContainer, shape);
  667. log.log( POILogger.WARN, "Unhandled shape type: "
  668. + type );
  669. break;
  670. }
  671. } else
  672. {
  673. log.log( POILogger.WARN, "Unexpected record id of shape group." );
  674. }
  675. }
  676. }
  677. private static void addToParentOrContainer(HSSFShape shape, HSSFShapeContainer container, HSSFShape parent) {
  678. if (parent instanceof HSSFShapeGroup)
  679. ((HSSFShapeGroup) parent).addShape(shape);
  680. else if (container instanceof HSSFPatriarch)
  681. ((HSSFPatriarch) container).addShape(shape);
  682. else
  683. container.getChildren().add(shape);
  684. }
  685. private static HSSFClientAnchor toClientAnchor(EscherClientAnchorRecord anchorRecord){
  686. HSSFClientAnchor anchor = new HSSFClientAnchor();
  687. anchor.setAnchorType(anchorRecord.getFlag());
  688. anchor.setCol1( anchorRecord.getCol1() );
  689. anchor.setCol2( anchorRecord.getCol2() );
  690. anchor.setDx1( anchorRecord.getDx1() );
  691. anchor.setDx2( anchorRecord.getDx2() );
  692. anchor.setDy1( anchorRecord.getDy1() );
  693. anchor.setDy2( anchorRecord.getDy2() );
  694. anchor.setRow1( anchorRecord.getRow1() );
  695. anchor.setRow2( anchorRecord.getRow2() );
  696. return anchor;
  697. }
  698. private static HSSFChildAnchor toChildAnchor(EscherChildAnchorRecord anchorRecord){
  699. HSSFChildAnchor anchor = new HSSFChildAnchor();
  700. // anchor.setAnchorType(anchorRecord.getFlag());
  701. // anchor.setCol1( anchorRecord.getCol1() );
  702. // anchor.setCol2( anchorRecord.getCol2() );
  703. anchor.setDx1( anchorRecord.getDx1() );
  704. anchor.setDx2( anchorRecord.getDx2() );
  705. anchor.setDy1( anchorRecord.getDy1() );
  706. anchor.setDy2( anchorRecord.getDy2() );
  707. // anchor.setRow1( anchorRecord.getRow1() );
  708. // anchor.setRow2( anchorRecord.getRow2() );
  709. return anchor;
  710. }
  711. private static void convertRecordsToUserModel(EscherContainerRecord shapeContainer, Object model) {
  712. for(Iterator<EscherRecord> it = shapeContainer.getChildIterator(); it.hasNext();) {
  713. EscherRecord r = it.next();
  714. if(r instanceof EscherSpgrRecord) {
  715. // This may be overriden by a later EscherClientAnchorRecord
  716. EscherSpgrRecord spgr = (EscherSpgrRecord)r;
  717. if(model instanceof HSSFShapeGroup) {
  718. HSSFShapeGroup g = (HSSFShapeGroup)model;
  719. g.setCoordinates(
  720. spgr.getRectX1(), spgr.getRectY1(),
  721. spgr.getRectX2(), spgr.getRectY2()
  722. );
  723. } else {
  724. throw new IllegalStateException("Got top level anchor but not processing a group");
  725. }
  726. }
  727. else if(r instanceof EscherClientAnchorRecord) {
  728. EscherClientAnchorRecord car = (EscherClientAnchorRecord)r;
  729. if(model instanceof HSSFShape) {
  730. HSSFShape g = (HSSFShape)model;
  731. g.getAnchor().setDx1(car.getDx1());
  732. g.getAnchor().setDx2(car.getDx2());
  733. g.getAnchor().setDy1(car.getDy1());
  734. g.getAnchor().setDy2(car.getDy2());
  735. } else {
  736. throw new IllegalStateException("Got top level anchor but not processing a group or shape");
  737. }
  738. }
  739. else if(r instanceof EscherTextboxRecord) {
  740. EscherTextboxRecord tbr = (EscherTextboxRecord)r;
  741. // Also need to find the TextObjectRecord too
  742. // TODO
  743. }
  744. else if(r instanceof EscherSpRecord) {
  745. // Use flags if needed
  746. final EscherSpRecord spr = (EscherSpRecord) r;
  747. if (model instanceof HSSFShape){
  748. final HSSFShape s = (HSSFShape) model;
  749. }
  750. }
  751. else if(r instanceof EscherOptRecord) {
  752. // Use properties if needed
  753. }
  754. else {
  755. //System.err.println(r);
  756. }
  757. }
  758. }
  759. public void clear()
  760. {
  761. clearEscherRecords();
  762. shapeToObj.clear();
  763. // lastShapeId = 1024;
  764. }
  765. protected String getRecordName()
  766. {
  767. return "ESCHERAGGREGATE";
  768. }
  769. // =============== Private methods ========================
  770. private static boolean isObjectRecord( List records, int loc )
  771. {
  772. return sid( records, loc ) == ObjRecord.sid || sid( records, loc ) == TextObjectRecord.sid;
  773. }
  774. private void convertUserModelToRecords()
  775. {
  776. if ( patriarch != null )
  777. {
  778. shapeToObj.clear();
  779. tailRec.clear();
  780. clearEscherRecords();
  781. if ( patriarch.getChildren().size() != 0 )
  782. {
  783. convertPatriarch( patriarch );
  784. EscherContainerRecord dgContainer = (EscherContainerRecord) getEscherRecord( 0 );
  785. EscherContainerRecord spgrContainer = null;
  786. Iterator<EscherRecord> iter = dgContainer.getChildIterator();
  787. while (iter.hasNext()) {
  788. EscherRecord child = iter.next();
  789. if (child.getRecordId() == EscherContainerRecord.SPGR_CONTAINER) {
  790. spgrContainer = (EscherContainerRecord) child;
  791. }
  792. }
  793. convertShapes( patriarch, spgrContainer, shapeToObj );
  794. patriarch = null;
  795. }
  796. }
  797. }
  798. private void convertShapes( HSSFShapeContainer parent, EscherContainerRecord escherParent, Map shapeToObj )
  799. {
  800. if ( escherParent == null ) throw new IllegalArgumentException( "Parent record required" );
  801. List shapes = parent.getChildren();
  802. for ( Iterator iterator = shapes.iterator(); iterator.hasNext(); )
  803. {
  804. HSSFShape shape = (HSSFShape) iterator.next();
  805. if ( shape instanceof HSSFShapeGroup )
  806. {
  807. convertGroup( (HSSFShapeGroup) shape, escherParent, shapeToObj );
  808. }
  809. else
  810. {
  811. AbstractShape shapeModel = AbstractShape.createShape(
  812. shape,
  813. drawingManager.allocateShapeId(drawingGroupId) );
  814. shapeToObj.put( findClientData( shapeModel.getSpContainer() ), shapeModel.getObjRecord() );
  815. if ( shapeModel instanceof TextboxShape )
  816. {
  817. EscherRecord escherTextbox = ( (TextboxShape) shapeModel ).getEscherTextbox();
  818. shapeToObj.put( escherTextbox, ( (TextboxShape) shapeModel ).getTextObjectRecord() );
  819. // escherParent.addChildRecord(escherTextbox);
  820. if ( shapeModel instanceof CommentShape ){
  821. CommentShape comment = (CommentShape)shapeModel;
  822. tailRec.add(comment.getNoteRecord());
  823. }
  824. }
  825. escherParent.addChildRecord( shapeModel.getSpContainer() );
  826. }
  827. }
  828. // drawingManager.newCluster( (short)1 );
  829. // drawingManager.newCluster( (short)2 );
  830. }
  831. private void convertGroup( HSSFShapeGroup shape, EscherContainerRecord escherParent, Map shapeToObj )
  832. {
  833. EscherContainerRecord spgrContainer = new EscherContainerRecord();
  834. EscherContainerRecord spContainer = new EscherContainerRecord();
  835. EscherSpgrRecord spgr = new EscherSpgrRecord();
  836. EscherSpRecord sp = new EscherSpRecord();
  837. EscherOptRecord opt = new EscherOptRecord();
  838. EscherRecord anchor;
  839. EscherClientDataRecord clientData = new EscherClientDataRecord();
  840. spgrContainer.setRecordId( EscherContainerRecord.SPGR_CONTAINER );
  841. spgrContainer.setOptions( (short) 0x000F );
  842. spContainer.setRecordId( EscherContainerRecord.SP_CONTAINER );
  843. spContainer.setOptions( (short) 0x000F );
  844. spgr.setRecordId( EscherSpgrRecord.RECORD_ID );
  845. spgr.setOptions( (short) 0x0001 );
  846. spgr.setRectX1( shape.getX1() );
  847. spgr.setRectY1( shape.getY1() );
  848. spgr.setRectX2( shape.getX2() );
  849. spgr.setRectY2( shape.getY2() );
  850. sp.setRecordId( EscherSpRecord.RECORD_ID );
  851. sp.setOptions( (short) 0x0002 );
  852. int shapeId = drawingManager.allocateShapeId(drawingGroupId);
  853. sp.setShapeId( shapeId );
  854. if (shape.getAnchor() instanceof HSSFClientAnchor)
  855. sp.setFlags( EscherSpRecord.FLAG_GROUP | EscherSpRecord.FLAG_HAVEANCHOR );
  856. else
  857. sp.setFlags( EscherSpRecord.FLAG_GROUP | EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_CHILD );
  858. opt.setRecordId( EscherOptRecord.RECORD_ID );
  859. opt.setOptions( (short) 0x0023 );
  860. opt.addEscherProperty( new EscherBoolProperty( EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, 0x00040004 ) );
  861. opt.addEscherProperty( new EscherBoolProperty( EscherProperties.GROUPSHAPE__PRINT, 0x00080000 ) );
  862. anchor = ConvertAnchor.createAnchor( shape.getAnchor() );
  863. // clientAnchor.setCol1( ( (HSSFClientAnchor) shape.getAnchor() ).getCol1() );
  864. // clientAnchor.setRow1( (short) ( (HSSFClientAnchor) shape.getAnchor() ).getRow1() );
  865. // clientAnchor.setDx1( (short) shape.getAnchor().getDx1() );
  866. // clientAnchor.setDy1( (short) shape.getAnchor().getDy1() );
  867. // clientAnchor.setCol2( ( (HSSFClientAnchor) shape.getAnchor() ).getCol2() );
  868. // clientAnchor.setRow2( (short) ( (HSSFClientAnchor) shape.getAnchor() ).getRow2() );
  869. // clientAnchor.setDx2( (short) shape.getAnchor().getDx2() );
  870. // clientAnchor.setDy2( (short) shape.getAnchor().getDy2() );
  871. clientData.setRecordId( EscherClientDataRecord.RECORD_ID );
  872. clientData.setOptions( (short) 0x0000 );
  873. spgrContainer.addChildRecord( spContainer );
  874. spContainer.addChildRecord( spgr );
  875. spContainer.addChildRecord( sp );
  876. spContainer.addChildRecord( opt );
  877. spContainer.addChildRecord( anchor );
  878. spContainer.addChildRecord( clientData );
  879. ObjRecord obj = new ObjRecord();
  880. CommonObjectDataSubRecord cmo = new CommonObjectDataSubRecord();
  881. cmo.setObjectType( CommonObjectDataSubRecord.OBJECT_TYPE_GROUP );
  882. cmo.setObjectId( shapeId );
  883. cmo.setLocked( true );
  884. cmo.setPrintable( true );
  885. cmo.setAutofill( true );
  886. cmo.setAutoline( true );
  887. GroupMarkerSubRecord gmo = new GroupMarkerSubRecord();
  888. EndSubRecord end = new EndSubRecord();
  889. obj.addSubRecord( cmo );
  890. obj.addSubRecord( gmo );
  891. obj.addSubRecord( end );
  892. shapeToObj.put( clientData, obj );
  893. escherParent.addChildRecord( spgrContainer );
  894. convertShapes( shape, spgrContainer, shapeToObj );
  895. }
  896. private EscherRecord findClientData( EscherContainerRecord spContainer )
  897. {
  898. for (Iterator<EscherRecord> iterator = spContainer.getChildIterator(); iterator.hasNext();) {
  899. EscherRecord r = iterator.next();
  900. if (r.getRecordId() == EscherClientDataRecord.RECORD_ID) {
  901. return r;
  902. }
  903. }
  904. throw new IllegalArgumentException( "Can not find client data record" );
  905. }
  906. private void convertPatriarch( HSSFPatriarch patriarch )
  907. {
  908. EscherContainerRecord dgContainer = new EscherContainerRecord();
  909. EscherDgRecord dg;
  910. EscherContainerRecord spgrContainer = new EscherContainerRecord();
  911. EscherContainerRecord spContainer1 = new EscherContainerRecord();
  912. EscherSpgrRecord spgr = new EscherSpgrRecord();
  913. EscherSpRecord sp1 = new EscherSpRecord();
  914. dgContainer.setRecordId( EscherContainerRecord.DG_CONTAINER );
  915. dgContainer.setOptions( (short) 0x000F );
  916. dg = drawingManager.createDgRecord();
  917. drawingGroupId = dg.getDrawingGroupId();
  918. // dg.setOptions( (short) ( drawingId << 4 ) );
  919. // dg.setNumShapes( getNumberOfShapes( patriarch ) );
  920. // dg.setLastMSOSPID( 0 ); // populated after all shape id's are assigned.
  921. spgrContainer.setRecordId( EscherContainerRecord.SPGR_CONTAINER );
  922. spgrContainer.setOptions( (short) 0x000F );
  923. spContainer1.setRecordId( EscherContainerRecord.SP_CONTAINER );
  924. spContainer1.setOptions( (short) 0x000F );
  925. spgr.setRecordId( EscherSpgrRecord.RECORD_ID );
  926. spgr.setOptions( (short) 0x0001 ); // version
  927. spgr.setRectX1( patriarch.getX1() );
  928. spgr.setRectY1( patriarch.getY1() );
  929. spgr.setRectX2( patriarch.getX2() );
  930. spgr.setRectY2( patriarch.getY2() );
  931. sp1.setRecordId( EscherSpRecord.RECORD_ID );
  932. sp1.setOptions( (short) 0x0002 );
  933. sp1.setShapeId( drawingManager.allocateShapeId(dg.getDrawingGroupId()) );
  934. sp1.setFlags( EscherSpRecord.FLAG_GROUP | EscherSpRecord.FLAG_PATRIARCH );
  935. dgContainer.addChildRecord( dg );
  936. dgContainer.addChildRecord( spgrContainer );
  937. spgrContainer.addChildRecord( spContainer1 );
  938. spContainer1.addChildRecord( spgr );
  939. spContainer1.addChildRecord( sp1 );
  940. addEscherRecord( dgContainer );
  941. }
  942. private static short sid( List records, int loc )
  943. {
  944. return ( (Record) records.get( loc ) ).getSid();
  945. }
  946. // Duplicated from org.apache.poi.hslf.model.Shape
  947. /**
  948. * Helper method to return escher child by record ID
  949. *
  950. * @return escher record or <code>null</code> if not found.
  951. */
  952. private static EscherRecord getEscherChild(EscherContainerRecord owner,
  953. int recordId)
  954. {
  955. for (Iterator iterator = owner.getChildRecords().iterator(); iterator
  956. .hasNext();)
  957. {
  958. EscherRecord escherRecord = (EscherRecord) iterator.next();
  959. if (escherRecord.getRecordId() == recordId)
  960. return escherRecord;
  961. }
  962. return null;
  963. }
  964. }