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.

TextSpecInfoAtom.java 5.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  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.record;
  16. import java.io.IOException;
  17. import java.io.OutputStream;
  18. import java.util.ArrayList;
  19. import java.util.Arrays;
  20. import java.util.List;
  21. import java.util.Map;
  22. import java.util.function.Supplier;
  23. import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
  24. import org.apache.poi.hslf.exceptions.HSLFException;
  25. import org.apache.poi.util.GenericRecordUtil;
  26. import org.apache.poi.util.IOUtils;
  27. import org.apache.poi.util.LittleEndian;
  28. import org.apache.poi.util.LittleEndianByteArrayInputStream;
  29. /**
  30. * The special info runs contained in this text.
  31. * Special info runs consist of character properties which don?t follow styles.
  32. */
  33. public final class TextSpecInfoAtom extends RecordAtom {
  34. //arbitrarily selected; may need to increase
  35. private static final int MAX_RECORD_LENGTH = 100_000;
  36. private static final long _type = RecordTypes.TextSpecInfoAtom.typeID;
  37. /**
  38. * Record header.
  39. */
  40. private final byte[] _header;
  41. /**
  42. * Record data.
  43. */
  44. private byte[] _data;
  45. /**
  46. * Constructs an empty atom, with a default run of size 1
  47. */
  48. public TextSpecInfoAtom() {
  49. _header = new byte[8];
  50. LittleEndian.putUInt(_header, 4, _type);
  51. reset(1);
  52. }
  53. /**
  54. * Constructs the link related atom record from its
  55. * source data.
  56. *
  57. * @param source the source data as a byte array.
  58. * @param start the start offset into the byte array.
  59. * @param len the length of the slice in the byte array.
  60. */
  61. public TextSpecInfoAtom(byte[] source, int start, int len) {
  62. // Get the header.
  63. _header = Arrays.copyOfRange(source, start, start+8);
  64. // Get the record data.
  65. _data = IOUtils.safelyClone(source, start+8, len-8, MAX_RECORD_LENGTH);
  66. }
  67. /**
  68. * Gets the record type.
  69. * @return the record type.
  70. */
  71. @Override
  72. public long getRecordType() { return _type; }
  73. /**
  74. * Write the contents of the record back, so it can be written
  75. * to disk
  76. *
  77. * @param out the output stream to write to.
  78. * @throws java.io.IOException if an error occurs.
  79. */
  80. @Override
  81. public void writeOut(OutputStream out) throws IOException {
  82. out.write(_header);
  83. out.write(_data);
  84. }
  85. /**
  86. * Update the text length
  87. *
  88. * @param size the text length
  89. */
  90. public void setTextSize(int size){
  91. LittleEndian.putInt(_data, 0, size);
  92. }
  93. /**
  94. * Reset the content to one info run with the default values
  95. * @param size the site of parent text
  96. */
  97. public void reset(int size){
  98. TextSpecInfoRun sir = new TextSpecInfoRun(size);
  99. UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream();
  100. try {
  101. sir.writeOut(bos);
  102. } catch (IOException e) {
  103. throw new HSLFException(e);
  104. }
  105. _data = bos.toByteArray();
  106. // Update the size (header bytes 5-8)
  107. LittleEndian.putInt(_header, 4, _data.length);
  108. }
  109. /**
  110. * Adapts the size by enlarging the last {@link TextSpecInfoRun}
  111. * or chopping the runs to the given length
  112. */
  113. public void setParentSize(int size) {
  114. assert(size > 0);
  115. try (UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream()) {
  116. TextSpecInfoRun[] runs = getTextSpecInfoRuns();
  117. int remaining = size;
  118. int idx = 0;
  119. for (TextSpecInfoRun run : runs) {
  120. int len = run.getLength();
  121. if (len > remaining || idx == runs.length - 1) {
  122. run.setLength(len = remaining);
  123. }
  124. remaining -= len;
  125. run.writeOut(bos);
  126. idx++;
  127. }
  128. _data = bos.toByteArray();
  129. // Update the size (header bytes 5-8)
  130. LittleEndian.putInt(_header, 4, _data.length);
  131. } catch (IOException e) {
  132. throw new HSLFException(e);
  133. }
  134. }
  135. /**
  136. * Get the number of characters covered by this records
  137. *
  138. * @return the number of characters covered by this records
  139. */
  140. public int getCharactersCovered(){
  141. int covered = 0;
  142. for (TextSpecInfoRun r : getTextSpecInfoRuns()) {
  143. covered += r.getLength();
  144. }
  145. return covered;
  146. }
  147. public TextSpecInfoRun[] getTextSpecInfoRuns(){
  148. LittleEndianByteArrayInputStream bis = new LittleEndianByteArrayInputStream(_data); // NOSONAR
  149. List<TextSpecInfoRun> lst = new ArrayList<>();
  150. while (bis.getReadIndex() < _data.length) {
  151. lst.add(new TextSpecInfoRun(bis));
  152. }
  153. return lst.toArray(new TextSpecInfoRun[0]);
  154. }
  155. @Override
  156. public Map<String, Supplier<?>> getGenericProperties() {
  157. return GenericRecordUtil.getGenericProperties(
  158. "charactersCovered", this::getCharactersCovered,
  159. "textSpecInfoRuns", this::getTextSpecInfoRuns
  160. );
  161. }
  162. }