Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

TextPropCollection.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  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.hslf.model.textproperties;
  16. import java.io.*;
  17. import java.util.*;
  18. import org.apache.poi.hslf.exceptions.HSLFException;
  19. import org.apache.poi.hslf.record.StyleTextPropAtom;
  20. import org.apache.poi.util.HexDump;
  21. import org.apache.poi.util.LittleEndian;
  22. /**
  23. * For a given run of characters, holds the properties (which could
  24. * be paragraph properties or character properties).
  25. * Used to hold the number of characters affected, the list of active
  26. * properties, and the indent level if required.
  27. */
  28. public class TextPropCollection {
  29. /** All the different kinds of paragraph properties we might handle */
  30. private static final TextProp[] paragraphTextPropTypes = {
  31. // TextProp order is according to 2.9.20 TextPFException,
  32. // bitmask order can be different
  33. new ParagraphFlagsTextProp(),
  34. new TextProp(2, 0x80, "bullet.char"),
  35. new TextProp(2, 0x10, "bullet.font"),
  36. new TextProp(2, 0x40, "bullet.size"),
  37. new TextProp(4, 0x20, "bullet.color"),
  38. new TextAlignmentProp(),
  39. new TextProp(2, 0x1000, "linespacing"),
  40. new TextProp(2, 0x2000, "spacebefore"),
  41. new TextProp(2, 0x4000, "spaceafter"),
  42. new TextProp(2, 0x100, "text.offset"), // left margin
  43. // 0x200 - Undefined and MUST be ignored
  44. new TextProp(2, 0x400, "bullet.offset"), // indent
  45. new TextProp(2, 0x8000, "defaultTabSize"),
  46. new TabStopPropCollection(), // tabstops size is variable!
  47. new FontAlignmentProp(),
  48. new WrapFlagsTextProp(),
  49. new TextProp(2, 0x200000, "textDirection"),
  50. // 0x400000 MUST be zero and MUST be ignored
  51. new TextProp(0, 0x800000, "bullet.blip"), // TODO: check size
  52. new TextProp(0, 0x1000000, "bullet.scheme"), // TODO: check size
  53. new TextProp(0, 0x2000000, "hasBulletScheme"), // TODO: check size
  54. // 0xFC000000 MUST be zero and MUST be ignored
  55. };
  56. /** All the different kinds of character properties we might handle */
  57. private static final TextProp[] characterTextPropTypes = new TextProp[] {
  58. new TextProp(0, 0x100000, "pp10ext"),
  59. new TextProp(0, 0x1000000, "newAsian.font.index"), // A bit that specifies whether the newEAFontRef field of the TextCFException10 structure that contains this CFMasks exists.
  60. new TextProp(0, 0x2000000, "cs.font.index"), // A bit that specifies whether the csFontRef field of the TextCFException10 structure that contains this CFMasks exists.
  61. new TextProp(0, 0x4000000, "pp11ext"), // A bit that specifies whether the pp11ext field of the TextCFException10 structure that contains this CFMasks exists.
  62. new CharFlagsTextProp(),
  63. new TextProp(2, 0x10000, "font.index"),
  64. new TextProp(2, 0x200000, "asian.font.index"),
  65. new TextProp(2, 0x400000, "ansi.font.index"),
  66. new TextProp(2, 0x800000, "symbol.font.index"),
  67. new TextProp(2, 0x20000, "font.size"),
  68. new TextProp(4, 0x40000, "font.color"),
  69. new TextProp(2, 0x80000, "superscript")
  70. };
  71. public enum TextPropType {
  72. paragraph, character;
  73. }
  74. private int charactersCovered;
  75. // indentLevel is only valid for paragraph collection
  76. // if it's set to -1, it must be omitted - see 2.9.36 TextMasterStyleLevel
  77. private short indentLevel = 0;
  78. private final Map<String,TextProp> textProps = new HashMap<String,TextProp>();
  79. private int maskSpecial = 0;
  80. private final TextPropType textPropType;
  81. /**
  82. * Create a new collection of text properties (be they paragraph
  83. * or character) which will be groked via a subsequent call to
  84. * buildTextPropList().
  85. */
  86. public TextPropCollection(int charactersCovered, TextPropType textPropType) {
  87. this.charactersCovered = charactersCovered;
  88. this.textPropType = textPropType;
  89. }
  90. public int getSpecialMask() {
  91. return maskSpecial;
  92. }
  93. /** Fetch the number of characters this styling applies to */
  94. public int getCharactersCovered() {
  95. return charactersCovered;
  96. }
  97. /** Fetch the TextProps that define this styling in the record order */
  98. public List<TextProp> getTextPropList() {
  99. List<TextProp> orderedList = new ArrayList<TextProp>();
  100. for (TextProp potProp : getPotentialProperties()) {
  101. TextProp textProp = textProps.get(potProp.getName());
  102. if (textProp != null) {
  103. orderedList.add(textProp);
  104. }
  105. }
  106. return orderedList;
  107. }
  108. /** Fetch the TextProp with this name, or null if it isn't present */
  109. public final TextProp findByName(String textPropName) {
  110. return textProps.get(textPropName);
  111. }
  112. public final TextProp removeByName(String name) {
  113. return textProps.remove(name);
  114. }
  115. public final TextPropType getTextPropType() {
  116. return textPropType;
  117. }
  118. private TextProp[] getPotentialProperties() {
  119. return (textPropType == TextPropType.paragraph) ? paragraphTextPropTypes : characterTextPropTypes;
  120. }
  121. /**
  122. * Checks the paragraph or character properties for the given property name.
  123. * Throws a HSLFException, if the name doesn't belong into this set of properties
  124. *
  125. * @param name the property name
  126. * @return if found, the property template to copy from
  127. */
  128. private TextProp validatePropName(String name) {
  129. for (TextProp tp : getPotentialProperties()) {
  130. if (tp.getName().equals(name)) {
  131. return tp;
  132. }
  133. }
  134. String errStr =
  135. "No TextProp with name " + name + " is defined to add from. " +
  136. "Character and paragraphs have their own properties/names.";
  137. throw new HSLFException(errStr);
  138. }
  139. /** Add the TextProp with this name to the list */
  140. public final TextProp addWithName(String name) {
  141. // Find the base TextProp to base on
  142. TextProp existing = findByName(name);
  143. if (existing != null) return existing;
  144. // Add a copy of this property
  145. TextProp textProp = validatePropName(name).clone();
  146. textProps.put(name,textProp);
  147. return textProp;
  148. }
  149. /**
  150. * Add the property at the correct position. Replaces an existing property with the same name.
  151. *
  152. * @param textProp the property to be added
  153. */
  154. public final void addProp(TextProp textProp) {
  155. if (textProp == null) {
  156. throw new HSLFException("TextProp must not be null");
  157. }
  158. String propName = textProp.getName();
  159. validatePropName(propName);
  160. textProps.put(propName, textProp);
  161. }
  162. /**
  163. * For an existing set of text properties, build the list of
  164. * properties coded for in a given run of properties.
  165. * @return the number of bytes that were used encoding the properties list
  166. */
  167. public int buildTextPropList(int containsField, byte[] data, int dataOffset) {
  168. int bytesPassed = 0;
  169. // For each possible entry, see if we match the mask
  170. // If we do, decode that, save it, and shuffle on
  171. for(TextProp tp : getPotentialProperties()) {
  172. // Check there's still data left to read
  173. // Check if this property is found in the mask
  174. if((containsField & tp.getMask()) != 0) {
  175. if(dataOffset+bytesPassed >= data.length) {
  176. // Out of data, can't be any more properties to go
  177. // remember the mask and return
  178. maskSpecial |= tp.getMask();
  179. return bytesPassed;
  180. }
  181. // Bingo, data contains this property
  182. TextProp prop = tp.clone();
  183. int val = 0;
  184. if (prop instanceof TabStopPropCollection) {
  185. ((TabStopPropCollection)prop).parseProperty(data, dataOffset+bytesPassed);
  186. } else if (prop.getSize() == 2) {
  187. val = LittleEndian.getShort(data,dataOffset+bytesPassed);
  188. } else if(prop.getSize() == 4) {
  189. val = LittleEndian.getInt(data,dataOffset+bytesPassed);
  190. } else if (prop.getSize() == 0) {
  191. //remember "special" bits.
  192. maskSpecial |= tp.getMask();
  193. continue;
  194. }
  195. if (prop instanceof BitMaskTextProp) {
  196. ((BitMaskTextProp)prop).setValueWithMask(val, containsField);
  197. } else {
  198. prop.setValue(val);
  199. }
  200. bytesPassed += prop.getSize();
  201. addProp(prop);
  202. }
  203. }
  204. // Return how many bytes were used
  205. return bytesPassed;
  206. }
  207. /**
  208. * Clones the given text properties
  209. */
  210. public void copy(TextPropCollection other) {
  211. if (this == other) return;
  212. this.charactersCovered = other.charactersCovered;
  213. this.indentLevel = other.indentLevel;
  214. this.maskSpecial = other.maskSpecial;
  215. this.textProps.clear();
  216. for (TextProp tp : other.textProps.values()) {
  217. TextProp tpCopy = (tp instanceof BitMaskTextProp)
  218. ? ((BitMaskTextProp)tp).cloneAll()
  219. : tp.clone();
  220. addProp(tpCopy);
  221. }
  222. }
  223. /**
  224. * Update the size of the text that this set of properties
  225. * applies to
  226. */
  227. public void updateTextSize(int textSize) {
  228. charactersCovered = textSize;
  229. }
  230. /**
  231. * Writes out to disk the header, and then all the properties
  232. */
  233. public void writeOut(OutputStream o) throws IOException {
  234. // First goes the number of characters we affect
  235. StyleTextPropAtom.writeLittleEndian(charactersCovered,o);
  236. // Then we have the indentLevel field if it's a paragraph collection
  237. if (textPropType == TextPropType.paragraph && indentLevel > -1) {
  238. StyleTextPropAtom.writeLittleEndian(indentLevel, o);
  239. }
  240. // Then the mask field
  241. int mask = maskSpecial;
  242. for (TextProp textProp : textProps.values()) {
  243. mask |= textProp.getWriteMask();
  244. }
  245. StyleTextPropAtom.writeLittleEndian(mask,o);
  246. // Then the contents of all the properties
  247. for (TextProp textProp : getTextPropList()) {
  248. int val = textProp.getValue();
  249. if (textProp instanceof BitMaskTextProp && textProp.getWriteMask() == 0) {
  250. // don't add empty properties, as they can't be recognized while reading
  251. continue;
  252. } else if (textProp.getSize() == 2) {
  253. StyleTextPropAtom.writeLittleEndian((short)val,o);
  254. } else if (textProp.getSize() == 4) {
  255. StyleTextPropAtom.writeLittleEndian(val,o);
  256. }
  257. }
  258. }
  259. public short getIndentLevel(){
  260. return indentLevel;
  261. }
  262. public void setIndentLevel(short indentLevel) {
  263. if (textPropType == TextPropType.character) {
  264. throw new RuntimeException("trying to set an indent on a character collection.");
  265. }
  266. this.indentLevel = indentLevel;
  267. }
  268. public int hashCode() {
  269. final int prime = 31;
  270. int result = 1;
  271. result = prime * result + charactersCovered;
  272. result = prime * result + maskSpecial;
  273. result = prime * result + indentLevel;
  274. result = prime * result + ((textProps == null) ? 0 : textProps.hashCode());
  275. return result;
  276. }
  277. /**
  278. * compares most properties apart of the covered characters length
  279. */
  280. public boolean equals(Object other) {
  281. if (this == other) return true;
  282. if (other == null) return false;
  283. if (getClass() != other.getClass()) return false;
  284. TextPropCollection o = (TextPropCollection)other;
  285. if (o.maskSpecial != this.maskSpecial || o.indentLevel != this.indentLevel) {
  286. return false;
  287. }
  288. return textProps.equals(o.textProps);
  289. }
  290. public String toString() {
  291. StringBuilder out = new StringBuilder();
  292. out.append(" chars covered: " + getCharactersCovered());
  293. out.append(" special mask flags: 0x" + HexDump.toHex(getSpecialMask()) + "\n");
  294. if (textPropType == TextPropType.paragraph) {
  295. out.append(" indent level: "+getIndentLevel()+"\n");
  296. }
  297. for(TextProp p : getTextPropList()) {
  298. out.append(" " + p.getName() + " = " + p.getValue() );
  299. out.append(" (0x" + HexDump.toHex(p.getValue()) + ")\n");
  300. if (p instanceof BitMaskTextProp) {
  301. BitMaskTextProp bm = (BitMaskTextProp)p;
  302. int i = 0;
  303. for (String s : bm.getSubPropNames()) {
  304. if (bm.getSubPropMatches()[i]) {
  305. out.append(" " + s + " = " + bm.getSubValue(i) + "\n");
  306. }
  307. i++;
  308. }
  309. }
  310. }
  311. out.append(" bytes that would be written: \n");
  312. try {
  313. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  314. writeOut(baos);
  315. byte[] b = baos.toByteArray();
  316. out.append(HexDump.dump(b, 0, 0));
  317. } catch (Exception e ) {
  318. e.printStackTrace();
  319. }
  320. return out.toString();
  321. }
  322. }