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.

HwmfEscape.java 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  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.hwmf.record;
  16. import java.io.IOException;
  17. import java.util.Collections;
  18. import java.util.LinkedHashMap;
  19. import java.util.Map;
  20. import java.util.function.Supplier;
  21. import org.apache.poi.common.usermodel.GenericRecord;
  22. import org.apache.poi.hwmf.draw.HwmfGraphics;
  23. import org.apache.poi.util.GenericRecordJsonWriter;
  24. import org.apache.poi.util.GenericRecordUtil;
  25. import org.apache.poi.util.IOUtils;
  26. import org.apache.poi.util.LittleEndianConsts;
  27. import org.apache.poi.util.LittleEndianInputStream;
  28. /**
  29. * The MetafileEscapes specifies printer driver functionality that
  30. * might not be directly accessible through WMF records
  31. */
  32. @SuppressWarnings("WeakerAccess")
  33. public class HwmfEscape implements HwmfRecord {
  34. private static final int MAX_OBJECT_SIZE = 0xFFFF;
  35. public enum EscapeFunction {
  36. /** Notifies the printer driver that the application has finished writing to a page. */
  37. NEWFRAME(0x0001, WmfEscapeUnknownData::new),
  38. /** Stops processing the current document. */
  39. ABORTDOC(0x0002, WmfEscapeUnknownData::new),
  40. /** Notifies the printer driver that the application has finished writing to a band. */
  41. NEXTBAND(0x0003, WmfEscapeUnknownData::new),
  42. /** Sets color table values. */
  43. SETCOLORTABLE(0x0004, WmfEscapeUnknownData::new),
  44. /** Gets color table values. */
  45. GETCOLORTABLE(0x0005, WmfEscapeUnknownData::new),
  46. /** Causes all pending output to be flushed to the output device. */
  47. FLUSHOUT(0x0006, WmfEscapeUnknownData::new),
  48. /** Indicates that the printer driver SHOULD print text only, and no graphics. */
  49. DRAFTMODE(0x0007, WmfEscapeUnknownData::new),
  50. /** Queries a printer driver to determine whether a specific escape function is supported on the output device it drives. */
  51. QUERYESCSUPPORT(0x0008, WmfEscapeUnknownData::new),
  52. /** Sets the application-defined function that allows a print job to be canceled during printing. */
  53. SETABORTPROC(0x0009, WmfEscapeUnknownData::new),
  54. /** Notifies the printer driver that a new print job is starting. */
  55. STARTDOC(0x000A, WmfEscapeUnknownData::new),
  56. /** Notifies the printer driver that the current print job is ending. */
  57. ENDDOC(0x000B, WmfEscapeUnknownData::new),
  58. /** Retrieves the physical page size currently selected on an output device. */
  59. GETPHYSPAGESIZE(0x000C, WmfEscapeUnknownData::new),
  60. /** Retrieves the offset from the upper-left corner of the physical page where the actual printing or drawing begins. */
  61. GETPRINTINGOFFSET(0x000D, WmfEscapeUnknownData::new),
  62. /** Retrieves the scaling factors for the x-axis and the y-axis of a printer. */
  63. GETSCALINGFACTOR(0x000E, WmfEscapeUnknownData::new),
  64. /** Used to embed an enhanced metafile format (EMF) metafile within a WMF metafile. */
  65. META_ESCAPE_ENHANCED_METAFILE(0x000F, WmfEscapeEMF::new),
  66. /** Sets the width of a pen in pixels. */
  67. SETPENWIDTH(0x0010, WmfEscapeUnknownData::new),
  68. /** Sets the number of copies. */
  69. SETCOPYCOUNT(0x0011, WmfEscapeUnknownData::new),
  70. /** Sets the source, such as a particular paper tray or bin on a printer, for output forms. */
  71. SETPAPERSOURCE(0x0012, WmfEscapeUnknownData::new),
  72. /** This record passes through arbitrary data. */
  73. PASSTHROUGH(0x0013, WmfEscapeUnknownData::new),
  74. /** Gets information concerning graphics technology that is supported on a device. */
  75. GETTECHNOLOGY(0x0014, WmfEscapeUnknownData::new),
  76. /** Specifies the line-drawing mode to use in output to a device. */
  77. SETLINECAP(0x0015, WmfEscapeUnknownData::new),
  78. /** Specifies the line-joining mode to use in output to a device. */
  79. SETLINEJOIN(0x0016, WmfEscapeUnknownData::new),
  80. /** Sets the limit for the length of miter joins to use in output to a device. */
  81. SETMITERLIMIT(0x0017, WmfEscapeUnknownData::new),
  82. /** Retrieves or specifies settings concerning banding on a device, such as the number of bands. */
  83. BANDINFO(0x0018, WmfEscapeUnknownData::new),
  84. /** Draws a rectangle with a defined pattern. */
  85. DRAWPATTERNRECT(0x0019, WmfEscapeUnknownData::new),
  86. /** Retrieves the physical pen size currently defined on a device. */
  87. GETVECTORPENSIZE(0x001A, WmfEscapeUnknownData::new),
  88. /** Retrieves the physical brush size currently defined on a device. */
  89. GETVECTORBRUSHSIZE(0x001B, WmfEscapeUnknownData::new),
  90. /** Enables or disables double-sided (duplex) printing on a device. */
  91. ENABLEDUPLEX(0x001C, WmfEscapeUnknownData::new),
  92. /** Retrieves or specifies the source of output forms on a device. */
  93. GETSETPAPERBINS(0x001D, WmfEscapeUnknownData::new),
  94. /** Retrieves or specifies the paper orientation on a device. */
  95. GETSETPRINTORIENT(0x001E, WmfEscapeUnknownData::new),
  96. /** Retrieves information concerning the sources of different forms on an output device. */
  97. ENUMPAPERBINS(0x001F, WmfEscapeUnknownData::new),
  98. /** Specifies the scaling of device-independent bitmaps (DIBs). */
  99. SETDIBSCALING(0x0020, WmfEscapeUnknownData::new),
  100. /** Indicates the start and end of an encapsulated PostScript (EPS) section. */
  101. EPSPRINTING(0x0021, WmfEscapeUnknownData::new),
  102. /** Queries a printer driver for paper dimensions and other forms data. */
  103. ENUMPAPERMETRICS(0x0022, WmfEscapeUnknownData::new),
  104. /** Retrieves or specifies paper dimensions and other forms data on an output device. */
  105. GETSETPAPERMETRICS(0x0023, WmfEscapeUnknownData::new),
  106. /** Sends arbitrary PostScript data to an output device. */
  107. POSTSCRIPT_DATA(0x0025, WmfEscapeUnknownData::new),
  108. /** Notifies an output device to ignore PostScript data. */
  109. POSTSCRIPT_IGNORE(0x0026, WmfEscapeUnknownData::new),
  110. /** Gets the device units currently configured on an output device. */
  111. GETDEVICEUNITS(0x002A, WmfEscapeUnknownData::new),
  112. /** Gets extended text metrics currently configured on an output device. */
  113. GETEXTENDEDTEXTMETRICS(0x0100, WmfEscapeUnknownData::new),
  114. /** Gets the font kern table currently defined on an output device. */
  115. GETPAIRKERNTABLE(0x0102, WmfEscapeUnknownData::new),
  116. /** Draws text using the currently selected font, background color, and text color. */
  117. EXTTEXTOUT(0x0200, WmfEscapeUnknownData::new),
  118. /** Gets the font face name currently configured on a device. */
  119. GETFACENAME(0x0201, WmfEscapeUnknownData::new),
  120. /** Sets the font face name on a device. */
  121. DOWNLOADFACE(0x0202, WmfEscapeUnknownData::new),
  122. /** Queries a printer driver about the support for metafiles on an output device. */
  123. METAFILE_DRIVER(0x0801, WmfEscapeUnknownData::new),
  124. /** Queries the printer driver about its support for DIBs on an output device. */
  125. QUERYDIBSUPPORT(0x0C01, WmfEscapeUnknownData::new),
  126. /** Opens a path. */
  127. BEGIN_PATH(0x1000, WmfEscapeUnknownData::new),
  128. /** Defines a clip region that is bounded by a path. The input MUST be a 16-bit quantity that defines the action to take. */
  129. CLIP_TO_PATH(0x1001, WmfEscapeUnknownData::new),
  130. /** Ends a path. */
  131. END_PATH(0x1002, WmfEscapeUnknownData::new),
  132. /** The same as STARTDOC specified with a NULL document and output filename, data in raw mode, and a type of zero. */
  133. OPEN_CHANNEL(0x100E, WmfEscapeUnknownData::new),
  134. /** Instructs the printer driver to download sets of PostScript procedures. */
  135. DOWNLOADHEADER(0x100F, WmfEscapeUnknownData::new),
  136. /** The same as ENDDOC. See OPEN_CHANNEL. */
  137. CLOSE_CHANNEL(0x1010, WmfEscapeUnknownData::new),
  138. /** Sends arbitrary data directly to a printer driver, which is expected to process this data only when in PostScript mode. */
  139. POSTSCRIPT_PASSTHROUGH(0x1013, WmfEscapeUnknownData::new),
  140. /** Sends arbitrary data directly to the printer driver. */
  141. ENCAPSULATED_POSTSCRIPT(0x1014, WmfEscapeUnknownData::new),
  142. /** Sets the printer driver to either PostScript or GDI mode. */
  143. POSTSCRIPT_IDENTIFY(0x1015, WmfEscapeUnknownData::new),
  144. /** Inserts a block of raw data into a PostScript stream. The input MUST be
  145. a 32-bit quantity specifying the number of bytes to inject, a 16-bit quantity specifying the
  146. injection point, and a 16-bit quantity specifying the page number, followed by the bytes to
  147. inject. */
  148. POSTSCRIPT_INJECTION(0x1016, WmfEscapeUnknownData::new),
  149. /** Checks whether the printer supports a JPEG image. */
  150. CHECKJPEGFORMAT(0x1017, WmfEscapeUnknownData::new),
  151. /** Checks whether the printer supports a PNG image */
  152. CHECKPNGFORMAT(0x1018, WmfEscapeUnknownData::new),
  153. /** Gets information on a specified feature setting for a PostScript printer driver. */
  154. GET_PS_FEATURESETTING(0x1019, WmfEscapeUnknownData::new),
  155. /** Enables applications to write documents to a file or to a printer in XML Paper Specification (XPS) format. */
  156. MXDC_ESCAPE(0x101A, WmfEscapeUnknownData::new),
  157. /** Enables applications to include private procedures and other arbitrary data in documents. */
  158. SPCLPASSTHROUGH2(0x11D8, WmfEscapeUnknownData::new);
  159. private final int flag;
  160. private final Supplier<? extends HwmfEscape.HwmfEscapeData> constructor;
  161. EscapeFunction(int flag, Supplier<? extends HwmfEscape.HwmfEscapeData> constructor) {
  162. this.flag = flag;
  163. this.constructor = constructor;
  164. }
  165. public int getFlag() {
  166. return flag;
  167. }
  168. public Supplier<? extends HwmfEscapeData> getConstructor() {
  169. return constructor;
  170. }
  171. static EscapeFunction valueOf(int flag) {
  172. for (EscapeFunction hs : values()) {
  173. if (hs.flag == flag) return hs;
  174. }
  175. return null;
  176. }
  177. }
  178. public interface HwmfEscapeData {
  179. int init(LittleEndianInputStream leis, long recordSize, EscapeFunction escapeFunction) throws IOException;
  180. }
  181. /**
  182. * A 16-bit unsigned integer that defines the escape function. The
  183. * value MUST be from the MetafileEscapes enumeration.
  184. */
  185. private EscapeFunction escapeFunction;
  186. private HwmfEscapeData escapeData;
  187. @Override
  188. public HwmfRecordType getWmfRecordType() {
  189. return HwmfRecordType.escape;
  190. }
  191. @Override
  192. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  193. escapeFunction = EscapeFunction.valueOf(leis.readUShort());
  194. // A 16-bit unsigned integer that specifies the size, in bytes, of the EscapeData field.
  195. int byteCount = leis.readUShort();
  196. int size = 2*LittleEndianConsts.SHORT_SIZE;
  197. escapeData = (escapeFunction == null) ? new WmfEscapeUnknownData() : escapeFunction.constructor.get();
  198. size += escapeData.init(leis, byteCount, escapeFunction);
  199. return size;
  200. }
  201. public EscapeFunction getEscapeFunction() {
  202. return escapeFunction;
  203. }
  204. @SuppressWarnings("unchecked")
  205. public <T extends HwmfEscapeData> T getEscapeData() {
  206. return (T)escapeData;
  207. }
  208. @Override
  209. public void draw(HwmfGraphics ctx) {
  210. }
  211. public String toString() {
  212. return GenericRecordJsonWriter.marshal(this);
  213. }
  214. @Override
  215. public Map<String, Supplier<?>> getGenericProperties() {
  216. return GenericRecordUtil.getGenericProperties(
  217. "escapeFunction", this::getEscapeFunction,
  218. "escapeData", this::getEscapeData
  219. );
  220. }
  221. public static class WmfEscapeUnknownData implements HwmfEscapeData, GenericRecord {
  222. EscapeFunction escapeFunction;
  223. private byte[] escapeDataBytes;
  224. public byte[] getEscapeDataBytes() {
  225. return escapeDataBytes;
  226. }
  227. @Override
  228. public int init(LittleEndianInputStream leis, long recordSize, EscapeFunction escapeFunction) throws IOException {
  229. this.escapeFunction = escapeFunction;
  230. escapeDataBytes = IOUtils.toByteArray(leis,(int)recordSize,MAX_OBJECT_SIZE);
  231. return (int)recordSize;
  232. }
  233. @Override
  234. public String toString() {
  235. return GenericRecordJsonWriter.marshal(this);
  236. }
  237. @Override
  238. public Map<String, Supplier<?>> getGenericProperties() {
  239. return GenericRecordUtil.getGenericProperties("escapeDataBytes", this::getEscapeDataBytes);
  240. }
  241. }
  242. public static class WmfEscapeEMF implements HwmfEscapeData, GenericRecord {
  243. // The magic for EMF parts, i.e. the byte sequence for "WMFC"
  244. private static final int EMF_COMMENT_IDENTIFIER = 0x43464D57;
  245. int commentIdentifier;
  246. int commentType;
  247. int version;
  248. int checksum;
  249. int flags;
  250. int commentRecordCount;
  251. int currentRecordSize;
  252. int remainingBytes;
  253. int emfRecordSize;
  254. byte[] emfData;
  255. @Override
  256. public int init(LittleEndianInputStream leis, long recordSize, EscapeFunction escapeFunction) throws IOException {
  257. if (recordSize < LittleEndianConsts.INT_SIZE) {
  258. return 0;
  259. }
  260. // A 32-bit unsigned integer that defines this record as a WMF Comment record.
  261. commentIdentifier = leis.readInt();
  262. if (commentIdentifier != EMF_COMMENT_IDENTIFIER) {
  263. // there are some WMF implementation using this record as a MFCOMMENT or similar
  264. // if the commentIdentifier doesn't match, then return immediately
  265. emfData = IOUtils.toByteArray(leis, (int)(recordSize-LittleEndianConsts.INT_SIZE), MAX_OBJECT_SIZE);
  266. remainingBytes = emfData.length;
  267. return (int)recordSize;
  268. }
  269. // A 32-bit unsigned integer that identifies the type of comment in this record.
  270. // This value MUST be 0x00000001.
  271. commentType = leis.readInt();
  272. assert(commentType == 0x00000001);
  273. // A 32-bit unsigned integer that specifies EMF metafile interoperability. This SHOULD be 0x00010000.
  274. version = leis.readInt();
  275. // A 16-bit unsigned integer used to validate the correctness of the embedded EMF stream.
  276. // This value MUST be the one's-complement of the result of applying an XOR operation to all WORDs in the EMF stream.
  277. checksum = leis.readUShort();
  278. // This 32-bit unsigned integer is unused and MUST be set to zero.
  279. flags = leis.readInt();
  280. assert(flags == 0);
  281. // A 32-bit unsigned integer that specifies the total number of consecutive META_ESCAPE_ENHANCED_METAFILE
  282. // records that contain the embedded EMF metafile.
  283. commentRecordCount = leis.readInt();
  284. // A 32-bit unsigned integer that specifies the size, in bytes, of the EnhancedMetafileData field.
  285. // This value MUST be less than or equal to 8,192.
  286. currentRecordSize = leis.readInt();
  287. assert(0 <= currentRecordSize && currentRecordSize <= 0x2000);
  288. // A 32-bit unsigned integer that specifies the number of bytes in the EMF stream that remain to be
  289. // processed after this record. Those additional EMF bytes MUST follow in the EnhancedMetafileData
  290. // fields of subsequent META_ESCAPE_ENHANDED_METAFILE escape records.
  291. remainingBytes = leis.readInt();
  292. // A 32-bit unsigned integer that specifies the total size of the EMF stream embedded in this
  293. // sequence of META_ESCAPE_ENHANCED_METAFILE records.
  294. emfRecordSize = leis.readInt();
  295. // A segment of an EMF file. The bytes in consecutive META_ESCAPE_ENHANCED_METAFILE records
  296. // MUST be concatenated to represent the entire embedded EMF file.
  297. emfData = IOUtils.toByteArray(leis, currentRecordSize, MAX_OBJECT_SIZE);
  298. return LittleEndianConsts.INT_SIZE*8+ LittleEndianConsts.SHORT_SIZE+emfData.length;
  299. }
  300. public boolean isValid() {
  301. return commentIdentifier == EMF_COMMENT_IDENTIFIER;
  302. }
  303. public int getCommentRecordCount() {
  304. return commentRecordCount;
  305. }
  306. public int getCurrentRecordSize() {
  307. return currentRecordSize;
  308. }
  309. public int getRemainingBytes() {
  310. return remainingBytes;
  311. }
  312. public int getEmfRecordSize() {
  313. return emfRecordSize;
  314. }
  315. public byte[] getEmfData() {
  316. return emfData;
  317. }
  318. @Override
  319. public Map<String, Supplier<?>> getGenericProperties() {
  320. final Map<String,Supplier<?>> m = new LinkedHashMap<>();
  321. m.put("commentIdentifier", () -> commentIdentifier);
  322. m.put("commentType", () -> commentType);
  323. m.put("version", () -> version);
  324. m.put("checksum", () -> checksum);
  325. m.put("flags", () -> flags);
  326. m.put("commentRecordCount", this::getCommentRecordCount);
  327. m.put("currentRecordSize", this::getCurrentRecordSize);
  328. m.put("remainingBytes", this::getRemainingBytes);
  329. m.put("emfRecordSize", this::getEmfRecordSize);
  330. m.put("emfData", this::getEmfData);
  331. return Collections.unmodifiableMap(m);
  332. }
  333. }
  334. }