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.

HemfGraphics.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  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.hemf.draw;
  16. import static org.apache.poi.hwmf.record.HwmfBrushStyle.BS_NULL;
  17. import static org.apache.poi.hwmf.record.HwmfBrushStyle.BS_SOLID;
  18. import java.awt.Color;
  19. import java.awt.Graphics2D;
  20. import java.awt.Paint;
  21. import java.awt.geom.Path2D;
  22. import java.awt.geom.Point2D;
  23. import java.awt.geom.Rectangle2D;
  24. import java.util.function.Consumer;
  25. import org.apache.poi.hemf.record.emf.HemfComment.EmfComment;
  26. import org.apache.poi.hemf.record.emf.HemfRecord;
  27. import org.apache.poi.hemf.record.emfplus.HemfPlusRecord;
  28. import org.apache.poi.hwmf.draw.HwmfDrawProperties;
  29. import org.apache.poi.hwmf.draw.HwmfGraphics;
  30. import org.apache.poi.hwmf.record.HwmfColorRef;
  31. import org.apache.poi.hwmf.record.HwmfObjectTableEntry;
  32. import org.apache.poi.hwmf.record.HwmfPenStyle;
  33. import org.apache.poi.util.Internal;
  34. public class HemfGraphics extends HwmfGraphics {
  35. public enum EmfRenderState {
  36. INITIAL,
  37. EMF_ONLY,
  38. EMFPLUS_ONLY,
  39. EMF_DCONTEXT
  40. }
  41. private static final HwmfColorRef WHITE = new HwmfColorRef(Color.WHITE);
  42. private static final HwmfColorRef LTGRAY = new HwmfColorRef(new Color(0x00C0C0C0));
  43. private static final HwmfColorRef GRAY = new HwmfColorRef(new Color(0x00808080));
  44. private static final HwmfColorRef DKGRAY = new HwmfColorRef(new Color(0x00404040));
  45. private static final HwmfColorRef BLACK = new HwmfColorRef(Color.BLACK);
  46. private EmfRenderState renderState = EmfRenderState.INITIAL;
  47. public HemfGraphics(Graphics2D graphicsCtx, Rectangle2D bbox) {
  48. super(graphicsCtx,bbox);
  49. // add dummy entry for object ind ex 0, as emf is 1-based
  50. objectIndexes.set(0);
  51. }
  52. @Override
  53. public HemfDrawProperties getProperties() {
  54. return (HemfDrawProperties)super.getProperties();
  55. }
  56. @Override
  57. protected HemfDrawProperties newProperties(HwmfDrawProperties oldProps) {
  58. return (oldProps == null)
  59. ? new HemfDrawProperties()
  60. : new HemfDrawProperties((HemfDrawProperties)oldProps);
  61. }
  62. public EmfRenderState getRenderState() {
  63. return renderState;
  64. }
  65. public void setRenderState(EmfRenderState renderState) {
  66. this.renderState = renderState;
  67. }
  68. public void draw(HemfRecord r) {
  69. switch (renderState) {
  70. case EMF_DCONTEXT:
  71. // keep the dcontext state, if the next record is an EMF+ record
  72. // only reset it, when we are processing EMF records again
  73. if (!(r instanceof EmfComment)) {
  74. renderState = EmfRenderState.INITIAL;
  75. }
  76. r.draw(this);
  77. break;
  78. case INITIAL:
  79. r.draw(this);
  80. break;
  81. case EMF_ONLY:
  82. case EMFPLUS_ONLY:
  83. if ((r instanceof EmfComment) == (renderState == EmfRenderState.EMFPLUS_ONLY)) {
  84. r.draw(this);
  85. }
  86. break;
  87. default:
  88. break;
  89. }
  90. }
  91. public void draw(HemfPlusRecord r) {
  92. switch (renderState) {
  93. case EMFPLUS_ONLY:
  94. case EMF_DCONTEXT:
  95. case INITIAL:
  96. r.draw(this);
  97. break;
  98. case EMF_ONLY:
  99. default:
  100. break;
  101. }
  102. }
  103. @Internal
  104. public void draw(Consumer<Path2D> pathConsumer, FillDrawStyle fillDraw) {
  105. final HemfDrawProperties prop = getProperties();
  106. final boolean useBracket = prop.getUsePathBracket();
  107. final Path2D path;
  108. if (useBracket) {
  109. path = prop.getPath();
  110. } else {
  111. path = new Path2D.Double();
  112. path.setWindingRule(prop.getWindingRule());
  113. }
  114. // add dummy move-to at start, to handle invalid emfs not containing that move-to
  115. if (path.getCurrentPoint() == null) {
  116. Point2D pnt = prop.getLocation();
  117. path.moveTo(pnt.getX(), pnt.getY());
  118. }
  119. try {
  120. pathConsumer.accept(path);
  121. } catch (Exception e) {
  122. // workaround if a path has been started and no MoveTo command
  123. // has been specified before the first lineTo/splineTo
  124. final Point2D loc = prop.getLocation();
  125. path.moveTo(loc.getX(), loc.getY());
  126. pathConsumer.accept(path);
  127. }
  128. Point2D curPnt = path.getCurrentPoint();
  129. if (curPnt == null) {
  130. return;
  131. }
  132. prop.setLocation(curPnt);
  133. if (!useBracket) {
  134. switch (fillDraw) {
  135. case FILL:
  136. super.fill(path);
  137. break;
  138. case DRAW:
  139. super.draw(path);
  140. break;
  141. case FILL_DRAW:
  142. super.fill(path);
  143. super.draw(path);
  144. break;
  145. }
  146. }
  147. }
  148. /**
  149. * Adds or sets an record of type {@link HwmfObjectTableEntry} to the object table.
  150. * If the {@code index} is less than 1, the method acts the same as
  151. * {@link HwmfGraphics#addObjectTableEntry(HwmfObjectTableEntry)}, otherwise the
  152. * index is used to access the object table.
  153. * As the table is filled successively, the index must be between 1 and size+1
  154. *
  155. * @param entry the record to be stored
  156. * @param index the index to be overwritten, regardless if its content was unset before
  157. *
  158. * @see HwmfGraphics#addObjectTableEntry(HwmfObjectTableEntry)
  159. */
  160. public void addObjectTableEntry(HwmfObjectTableEntry entry, int index) {
  161. checkTableEntryIndex(index);
  162. objectIndexes.set(index);
  163. objectTable.put(index, entry);
  164. }
  165. /**
  166. * Gets a record which was registered earliser
  167. * @param index the record index
  168. * @return the record or {@code null} if it doesn't exist
  169. */
  170. public HwmfObjectTableEntry getObjectTableEntry(int index) {
  171. checkTableEntryIndex(index);
  172. return objectTable.get(index);
  173. }
  174. private void checkTableEntryIndex(int index) {
  175. if (renderState != EmfRenderState.EMFPLUS_ONLY) {
  176. // in EMF the index must > 0
  177. if (index < 1) {
  178. throw new IndexOutOfBoundsException("Object table entry index in EMF must be > 0 - invalid index: "+index);
  179. }
  180. } else {
  181. // in EMF+ the index must be between 0 and 63
  182. if (index < 0 || index > 63) {
  183. throw new IndexOutOfBoundsException("Object table entry index in EMF+ must be [0..63] - invalid index: "+index);
  184. }
  185. }
  186. }
  187. @Override
  188. public void applyObjectTableEntry(int index) {
  189. if ((index & 0x80000000) != 0) {
  190. selectStockObject(index);
  191. } else {
  192. super.applyObjectTableEntry(index);
  193. }
  194. }
  195. private void selectStockObject(int objectIndex) {
  196. final HemfDrawProperties prop = getProperties();
  197. switch (objectIndex) {
  198. case 0x80000000:
  199. // WHITE_BRUSH - A white, solid-color brush
  200. // BrushStyle: BS_SOLID
  201. // Color: 0x00FFFFFF
  202. prop.setBrushColor(WHITE);
  203. prop.setBrushStyle(BS_SOLID);
  204. break;
  205. case 0x80000001:
  206. // LTGRAY_BRUSH - A light gray, solid-color brush
  207. // BrushStyle: BS_SOLID
  208. // Color: 0x00C0C0C0
  209. prop.setBrushColor(LTGRAY);
  210. prop.setBrushStyle(BS_SOLID);
  211. break;
  212. case 0x80000002:
  213. // GRAY_BRUSH - A gray, solid-color brush
  214. // BrushStyle: BS_SOLID
  215. // Color: 0x00808080
  216. prop.setBrushColor(GRAY);
  217. prop.setBrushStyle(BS_SOLID);
  218. break;
  219. case 0x80000003:
  220. // DKGRAY_BRUSH - A dark gray, solid color brush
  221. // BrushStyle: BS_SOLID
  222. // Color: 0x00404040
  223. prop.setBrushColor(DKGRAY);
  224. prop.setBrushStyle(BS_SOLID);
  225. break;
  226. case 0x80000004:
  227. // BLACK_BRUSH - A black, solid color brush
  228. // BrushStyle: BS_SOLID
  229. // Color: 0x00000000
  230. prop.setBrushColor(BLACK);
  231. prop.setBrushStyle(BS_SOLID);
  232. break;
  233. case 0x80000005:
  234. // NULL_BRUSH - A null brush
  235. // BrushStyle: BS_NULL
  236. prop.setBrushStyle(BS_NULL);
  237. break;
  238. case 0x80000006:
  239. // WHITE_PEN - A white, solid-color pen
  240. // PenStyle: PS_COSMETIC + PS_SOLID
  241. // ColorRef: 0x00FFFFFF
  242. prop.setPenStyle(HwmfPenStyle.valueOf(0));
  243. prop.setPenWidth(1);
  244. prop.setPenColor(WHITE);
  245. break;
  246. case 0x80000007:
  247. // BLACK_PEN - A black, solid-color pen
  248. // PenStyle: PS_COSMETIC + PS_SOLID
  249. // ColorRef: 0x00000000
  250. prop.setPenStyle(HwmfPenStyle.valueOf(0));
  251. prop.setPenWidth(1);
  252. prop.setPenColor(BLACK);
  253. break;
  254. case 0x80000008:
  255. // NULL_PEN - A null pen
  256. // PenStyle: PS_NULL
  257. prop.setPenStyle(HwmfPenStyle.valueOf(HwmfPenStyle.HwmfLineDash.NULL.wmfFlag));
  258. break;
  259. case 0x8000000A:
  260. // OEM_FIXED_FONT - A fixed-width, OEM character set
  261. // Charset: OEM_CHARSET
  262. // PitchAndFamily: FF_DONTCARE + FIXED_PITCH
  263. break;
  264. case 0x8000000B:
  265. // ANSI_FIXED_FONT - A fixed-width font
  266. // Charset: ANSI_CHARSET
  267. // PitchAndFamily: FF_DONTCARE + FIXED_PITCH
  268. break;
  269. case 0x8000000C:
  270. // ANSI_VAR_FONT - A variable-width font
  271. // Charset: ANSI_CHARSET
  272. // PitchAndFamily: FF_DONTCARE + VARIABLE_PITCH
  273. break;
  274. case 0x8000000D:
  275. // SYSTEM_FONT - A font that is guaranteed to be available in the operating system
  276. break;
  277. case 0x8000000E:
  278. // DEVICE_DEFAULT_FONT
  279. // The default font that is provided by the graphics device driver for the current output device
  280. break;
  281. case 0x8000000F:
  282. // DEFAULT_PALETTE
  283. // The default palette that is defined for the current output device.
  284. break;
  285. case 0x80000010:
  286. // SYSTEM_FIXED_FONT
  287. // A fixed-width font that is guaranteed to be available in the operating system.
  288. break;
  289. case 0x80000011:
  290. // DEFAULT_GUI_FONT
  291. // The default font that is used for user interface objects such as menus and dialog boxes.
  292. break;
  293. case 0x80000012:
  294. // DC_BRUSH
  295. // The solid-color brush that is currently selected in the playback device context.
  296. break;
  297. case 0x80000013:
  298. // DC_PEN
  299. // The solid-color pen that is currently selected in the playback device context.
  300. break;
  301. }
  302. }
  303. @Override
  304. protected Paint getHatchedFill() {
  305. // TODO: use EmfPlusHatchBrushData
  306. return super.getHatchedFill();
  307. }
  308. }