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.

AFPPaintingState.java 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756
  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. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* $Id$ */
  18. package org.apache.fop.afp;
  19. import java.awt.Point;
  20. import org.apache.commons.logging.Log;
  21. import org.apache.commons.logging.LogFactory;
  22. import org.apache.xmlgraphics.java2d.color.ColorConverter;
  23. import org.apache.xmlgraphics.java2d.color.DefaultColorConverter;
  24. import org.apache.fop.afp.fonts.AFPPageFonts;
  25. import org.apache.fop.util.AbstractPaintingState;
  26. /**
  27. * This keeps information about the current painting state when writing to an
  28. * AFP datastream.
  29. */
  30. public class AFPPaintingState extends org.apache.fop.util.AbstractPaintingState implements
  31. Cloneable {
  32. private static final long serialVersionUID = 8206711712452344473L;
  33. private static Log log = LogFactory.getLog("org.apache.xmlgraphics.afp");
  34. /** the portrait rotation */
  35. private int portraitRotation;
  36. /** the landscape rotation */
  37. private int landscapeRotation = 270;
  38. /** color image support */
  39. private boolean colorImages;
  40. /** dithering quality setting (0.0f..1.0f) */
  41. private float ditheringQuality;
  42. /** image encoding quality setting (0.0f..1.0f) */
  43. private float bitmapEncodingQuality;
  44. /** color image handler */
  45. private ColorConverter colorConverter = GrayScaleColorConverter.getInstance();
  46. /**
  47. * true if certain image formats may be embedded unchanged in their native
  48. * format.
  49. */
  50. private boolean nativeImagesSupported;
  51. private boolean canEmbedJpeg;
  52. /**
  53. * true if CMYK images (requires IOCA FS45 suppport on the target platform)
  54. * may be generated
  55. */
  56. private boolean cmykImagesSupported;
  57. /** default value for image depth */
  58. private int bitsPerPixel = 8;
  59. /** the output resolution */
  60. private int resolution = 240; // 240 dpi
  61. /**
  62. * A configurable value to correct the line width so that the output matches the expected. Different
  63. * devices may need different values.
  64. */
  65. private float lineWidthCorrection = AFPConstants.LINE_WIDTH_CORRECTION;
  66. /** determines whether GOCA is enabled or disabled */
  67. private boolean gocaEnabled = true;
  68. /** determines whether to stroke text in GOCA mode or to use text operators where possible */
  69. private boolean strokeGocaText;
  70. /** use page segment with F11 and F45 images*/
  71. private boolean pSeg;
  72. /** use FS45 images*/
  73. private boolean fs45;
  74. /** the current page */
  75. private transient AFPPagePaintingState pagePaintingState = new AFPPagePaintingState();
  76. // /** reference orientation */
  77. // private int orientation = 0;
  78. /** a unit converter */
  79. private final transient AFPUnitConverter unitConv = new AFPUnitConverter(this);
  80. /**
  81. * Sets the rotation to be used for portrait pages, valid values are 0
  82. * (default), 90, 180, 270.
  83. *
  84. * @param rotation
  85. * The rotation in degrees.
  86. */
  87. public void setPortraitRotation(int rotation) {
  88. if (rotation == 0 || rotation == 90 || rotation == 180 || rotation == 270) {
  89. portraitRotation = rotation;
  90. } else {
  91. throw new IllegalArgumentException("The portrait rotation must be one"
  92. + " of the values 0, 90, 180, 270");
  93. }
  94. }
  95. /**
  96. * Returns the rotation to be used for portrait pages
  97. *
  98. * @return the rotation to be used for portrait pages
  99. */
  100. protected int getPortraitRotation() {
  101. return this.portraitRotation;
  102. }
  103. /**
  104. * Sets the rotation to be used for landscape pages, valid values are 0, 90,
  105. * 180, 270 (default).
  106. *
  107. * @param rotation
  108. * The rotation in degrees.
  109. */
  110. public void setLandscapeRotation(int rotation) {
  111. if (rotation == 0 || rotation == 90 || rotation == 180 || rotation == 270) {
  112. landscapeRotation = rotation;
  113. } else {
  114. throw new IllegalArgumentException("The landscape rotation must be one"
  115. + " of the values 0, 90, 180, 270");
  116. }
  117. }
  118. /**
  119. * Returns the landscape rotation
  120. *
  121. * @return the landscape rotation
  122. */
  123. protected int getLandscapeRotation() {
  124. return this.landscapeRotation;
  125. }
  126. /**
  127. * Sets the number of bits used per pixel
  128. *
  129. * @param bitsPerPixel
  130. * number of bits per pixel
  131. */
  132. public void setBitsPerPixel(int bitsPerPixel) {
  133. switch (bitsPerPixel) {
  134. case 1:
  135. case 4:
  136. case 8:
  137. this.bitsPerPixel = bitsPerPixel;
  138. break;
  139. default:
  140. log.warn("Invalid bits_per_pixel value, must be 1, 4 or 8.");
  141. this.bitsPerPixel = 8;
  142. break;
  143. }
  144. }
  145. /**
  146. * Returns the number of bits per pixel
  147. *
  148. * @return the number of bits per pixel
  149. */
  150. public int getBitsPerPixel() {
  151. return this.bitsPerPixel;
  152. }
  153. /**
  154. * Sets whether images are color or not and instantiates a ColorHandler
  155. *
  156. * @param colorImages
  157. * color image output
  158. */
  159. public void setColorImages(boolean colorImages) {
  160. this.colorImages = colorImages;
  161. if (colorImages) {
  162. this.colorConverter = DefaultColorConverter.getInstance();
  163. }
  164. }
  165. /**
  166. * Returns true if color images are to be used
  167. *
  168. * @return true if color images are to be used
  169. */
  170. public boolean isColorImages() {
  171. return this.colorImages;
  172. }
  173. /**
  174. * Used to convert color in respect of the colorImages flag
  175. *
  176. * @return the color converter
  177. */
  178. public ColorConverter getColorConverter() {
  179. return this.colorConverter;
  180. }
  181. /**
  182. * Sets whether images are natively supported or not in the AFP environment
  183. *
  184. * @param nativeImagesSupported
  185. * true if images are natively supported in this AFP environment
  186. */
  187. public void setNativeImagesSupported(boolean nativeImagesSupported) {
  188. this.nativeImagesSupported = nativeImagesSupported;
  189. }
  190. /**
  191. * Returns true if images are supported natively in this AFP environment
  192. *
  193. * @return true if images are supported natively in this AFP environment
  194. */
  195. public boolean isNativeImagesSupported() {
  196. return this.nativeImagesSupported;
  197. }
  198. /**
  199. * Set whether or not JPEG images can be embedded within an AFP document.
  200. *
  201. * @param canEmbed true if the JPEG image can be embedded
  202. */
  203. public void setCanEmbedJpeg(boolean canEmbed) {
  204. canEmbedJpeg = canEmbed;
  205. }
  206. /**
  207. * Returns true if JPEGs can be embedded in an AFP document.
  208. *
  209. * @return true if JPEG embedding is allowed
  210. */
  211. public boolean canEmbedJpeg() {
  212. return canEmbedJpeg;
  213. }
  214. /**
  215. * Controls whether CMYK images (IOCA FS45) are enabled. By default, support
  216. * is disabled for wider compatibility. When disabled, any CMYK image is
  217. * converted to the selected color format.
  218. *
  219. * @param value
  220. * true to enabled CMYK images
  221. */
  222. public void setCMYKImagesSupported(boolean value) {
  223. this.cmykImagesSupported = value;
  224. }
  225. /**
  226. * Indicates whether CMYK images (IOCA FS45) are enabled.
  227. *
  228. * @return true if IOCA FS45 is enabled
  229. */
  230. public boolean isCMYKImagesSupported() {
  231. return this.cmykImagesSupported;
  232. }
  233. /**
  234. * Gets the dithering quality setting to use when converting images to monochrome images.
  235. * @return the dithering quality (a value between 0.0f and 1.0f)
  236. */
  237. public float getDitheringQuality() {
  238. return this.ditheringQuality;
  239. }
  240. /**
  241. * Sets the dithering quality setting to use when converting images to monochrome images.
  242. * @param quality Defines the desired quality level for the conversion.
  243. * Valid values: a value between 0.0f (fastest) and 1.0f (best)
  244. */
  245. public void setDitheringQuality(float quality) {
  246. quality = Math.max(quality, 0.0f);
  247. quality = Math.min(quality, 1.0f);
  248. this.ditheringQuality = quality;
  249. }
  250. /**
  251. * Gets the image encoding quality setting to use when encoding bitmap images.
  252. * @return the encoding quality (a value between 0.0f and 1.0f, 1.0 meaning loss-less)
  253. */
  254. public float getBitmapEncodingQuality() {
  255. return this.bitmapEncodingQuality;
  256. }
  257. /**
  258. * Sets the image encoding quality setting to use when encoding bitmap images.
  259. * @param quality Defines the desired quality level for the conversion.
  260. * Valid values: a value between 0.0f (lowest) and 1.0f (best, loss-less)
  261. */
  262. public void setBitmapEncodingQuality(float quality) {
  263. quality = Math.max(quality, 0.0f);
  264. quality = Math.min(quality, 1.0f);
  265. this.bitmapEncodingQuality = quality;
  266. }
  267. /**
  268. * Sets the output/device resolution
  269. *
  270. * @param resolution
  271. * the output resolution (dpi)
  272. */
  273. public void setResolution(int resolution) {
  274. if (log.isDebugEnabled()) {
  275. log.debug("renderer-resolution set to: " + resolution + "dpi");
  276. }
  277. this.resolution = resolution;
  278. }
  279. /**
  280. * Sets the line width correction
  281. *
  282. * @param correction the line width multiplying factor correction
  283. */
  284. public void setLineWidthCorrection(float correction) {
  285. if (log.isDebugEnabled()) {
  286. log.debug("line width correction set to: " + correction);
  287. }
  288. this.lineWidthCorrection = correction;
  289. }
  290. /**
  291. * Returns the output/device resolution.
  292. *
  293. * @return the resolution in dpi
  294. */
  295. public int getResolution() {
  296. return this.resolution;
  297. }
  298. /**
  299. * Returns the line width correction.
  300. * @return the correction
  301. */
  302. public float getLineWidthCorrection() {
  303. return this.lineWidthCorrection;
  304. }
  305. /**
  306. * Controls whether GOCA is enabled or disabled.
  307. * @param enabled true if GOCA is enabled, false if it is disabled
  308. */
  309. public void setGOCAEnabled(boolean enabled) {
  310. this.gocaEnabled = enabled;
  311. }
  312. /**
  313. * Indicates whether GOCA is enabled or disabled.
  314. * @return true if GOCA is enabled, false if GOCA is disabled
  315. */
  316. public boolean isGOCAEnabled() {
  317. return this.gocaEnabled;
  318. }
  319. /**
  320. * Controls whether to stroke text in GOCA mode or to use text operators where possible.
  321. * @param stroke true to stroke, false to paint with text operators where possible
  322. */
  323. public void setStrokeGOCAText(boolean stroke) {
  324. this.strokeGocaText = stroke;
  325. }
  326. /**
  327. * Indicates whether to stroke text in GOCA mode or to use text operators where possible.
  328. * @return true to stroke, false to paint with text operators where possible
  329. */
  330. public boolean isStrokeGOCAText() {
  331. return this.strokeGocaText;
  332. }
  333. /**
  334. * Whether FS11 and SF45 non-inline images should be wrapped in a page segment
  335. * @return true iff images should be wrapped
  336. */
  337. public boolean getWrapPSeg() {
  338. return pSeg;
  339. }
  340. /**
  341. * Sets whether FS11 and FS45 non-inline images should be wrapped in a page segment
  342. * @param pSeg true iff images should be wrapped
  343. */
  344. public void setWrapPSeg(boolean pSeg) {
  345. this.pSeg = pSeg;
  346. }
  347. /**
  348. * gets whether images should be FS45
  349. * @return true iff images should be FS45
  350. */
  351. public boolean getFS45() {
  352. return fs45;
  353. }
  354. /**
  355. * sets whether images should be FS45
  356. * @param fs45 true iff images should be FS45
  357. */
  358. public void setFS45(boolean fs45) {
  359. this.fs45 = fs45;
  360. }
  361. /** {@inheritDoc} */
  362. @Override
  363. protected AbstractData instantiateData() {
  364. return new AFPData();
  365. }
  366. /** {@inheritDoc} */
  367. @Override
  368. protected AbstractPaintingState instantiate() {
  369. return new AFPPaintingState();
  370. }
  371. /**
  372. * Returns the painting state of the current page
  373. *
  374. * @return the painting state of the current page
  375. */
  376. protected AFPPagePaintingState getPagePaintingState() {
  377. return this.pagePaintingState;
  378. }
  379. /**
  380. * Gets the current page fonts
  381. *
  382. * @return the current page fonts
  383. */
  384. public AFPPageFonts getPageFonts() {
  385. return pagePaintingState.getFonts();
  386. }
  387. /**
  388. * Sets the page width
  389. *
  390. * @param pageWidth
  391. * the page width
  392. */
  393. public void setPageWidth(int pageWidth) {
  394. pagePaintingState.setWidth(pageWidth);
  395. }
  396. /**
  397. * Returns the page width
  398. *
  399. * @return the page width
  400. */
  401. public int getPageWidth() {
  402. return pagePaintingState.getWidth();
  403. }
  404. /**
  405. * Sets the page height
  406. *
  407. * @param pageHeight
  408. * the page height
  409. */
  410. public void setPageHeight(int pageHeight) {
  411. pagePaintingState.setHeight(pageHeight);
  412. }
  413. /**
  414. * Returns the page height
  415. *
  416. * @return the page height
  417. */
  418. public int getPageHeight() {
  419. return pagePaintingState.getHeight();
  420. }
  421. /**
  422. * Returns the page rotation
  423. *
  424. * @return the page rotation
  425. */
  426. public int getPageRotation() {
  427. return pagePaintingState.getOrientation();
  428. }
  429. /**
  430. * Sets the uri of the current image
  431. *
  432. * @param uri
  433. * the uri of the current image
  434. */
  435. public void setImageUri(String uri) {
  436. ((AFPData) getData()).imageUri = uri;
  437. }
  438. /**
  439. * Gets the uri of the current image
  440. *
  441. * @return the uri of the current image
  442. */
  443. public String getImageUri() {
  444. return ((AFPData) getData()).imageUri;
  445. }
  446. /**
  447. * Returns the currently derived rotation
  448. *
  449. * @return the currently derived rotation
  450. */
  451. public int getRotation() {
  452. return getData().getDerivedRotation();
  453. }
  454. /**
  455. * Returns the unit converter
  456. *
  457. * @return the unit converter
  458. */
  459. public AFPUnitConverter getUnitConverter() {
  460. return this.unitConv;
  461. }
  462. /**
  463. * Returns a point on the current page, taking the current painting state
  464. * into account.
  465. *
  466. * @param x
  467. * the X-coordinate
  468. * @param y
  469. * the Y-coordinate
  470. * @return a point on the current page
  471. */
  472. public Point getPoint(int x, int y) {
  473. Point p = new Point();
  474. int rotation = getRotation();
  475. switch (rotation) {
  476. case 90:
  477. p.x = y;
  478. p.y = getPageWidth() - x;
  479. break;
  480. case 180:
  481. p.x = getPageWidth() - x;
  482. p.y = getPageHeight() - y;
  483. break;
  484. case 270:
  485. p.x = getPageHeight() - y;
  486. p.y = x;
  487. break;
  488. default:
  489. p.x = x;
  490. p.y = y;
  491. break;
  492. }
  493. return p;
  494. }
  495. /** {@inheritDoc} */
  496. @Override
  497. public Object clone() {
  498. AFPPaintingState paintingState = (AFPPaintingState) super.clone();
  499. paintingState.pagePaintingState = (AFPPagePaintingState) this.pagePaintingState.clone();
  500. paintingState.portraitRotation = this.portraitRotation;
  501. paintingState.landscapeRotation = this.landscapeRotation;
  502. paintingState.bitsPerPixel = this.bitsPerPixel;
  503. paintingState.colorImages = this.colorImages;
  504. paintingState.colorConverter = this.colorConverter;
  505. paintingState.resolution = this.resolution;
  506. return paintingState;
  507. }
  508. /** {@inheritDoc} */
  509. @Override
  510. public String toString() {
  511. return "AFPPaintingState{" + "portraitRotation=" + portraitRotation
  512. + ", landscapeRotation=" + landscapeRotation + ", colorImages=" + colorImages
  513. + ", bitsPerPixel=" + bitsPerPixel + ", resolution=" + resolution + ", pageState="
  514. + pagePaintingState + super.toString() + "}";
  515. }
  516. /**
  517. * Page level state data
  518. */
  519. private class AFPPagePaintingState implements Cloneable {
  520. /** page width */
  521. private int width;
  522. /** page height */
  523. private int height;
  524. /** page fonts */
  525. private AFPPageFonts fonts = new AFPPageFonts();
  526. /** page font count */
  527. private int fontCount;
  528. /** page orientation */
  529. private int orientation;
  530. /**
  531. * Returns the page width
  532. *
  533. * @return the page width
  534. */
  535. protected int getWidth() {
  536. return width;
  537. }
  538. /**
  539. * Sets the page width
  540. *
  541. * @param width
  542. * the page width
  543. */
  544. protected void setWidth(int width) {
  545. this.width = width;
  546. }
  547. /**
  548. * Returns the page height
  549. *
  550. * @return the page height
  551. */
  552. protected int getHeight() {
  553. return height;
  554. }
  555. /**
  556. * Sets the page height
  557. *
  558. * @param height
  559. * the page height
  560. */
  561. protected void setHeight(int height) {
  562. this.height = height;
  563. }
  564. /**
  565. * Returns the page fonts
  566. *
  567. * @return the page fonts
  568. */
  569. protected AFPPageFonts getFonts() {
  570. return fonts;
  571. }
  572. /**
  573. * Sets the current page fonts
  574. *
  575. * @param fonts
  576. * the current page fonts
  577. */
  578. protected void setFonts(AFPPageFonts fonts) {
  579. this.fonts = fonts;
  580. }
  581. /**
  582. * Increments and returns the current page font count
  583. *
  584. * @return increment and return the current page font count
  585. */
  586. protected int incrementFontCount() {
  587. return ++fontCount;
  588. }
  589. /**
  590. * Returns the current page orientation
  591. *
  592. * @return the current page orientation
  593. */
  594. protected int getOrientation() {
  595. return orientation;
  596. }
  597. /**
  598. * Sets the current page orientation
  599. *
  600. * @param orientation
  601. * the current page orientation
  602. */
  603. protected void setOrientation(int orientation) {
  604. this.orientation = orientation;
  605. }
  606. /** {@inheritDoc} */
  607. @Override
  608. public Object clone() {
  609. AFPPagePaintingState state = new AFPPagePaintingState();
  610. state.width = this.width;
  611. state.height = this.height;
  612. state.orientation = this.orientation;
  613. state.fonts = new AFPPageFonts(this.fonts);
  614. state.fontCount = this.fontCount;
  615. return state;
  616. }
  617. /** {@inheritDoc} */
  618. @Override
  619. public String toString() {
  620. return "AFPPagePaintingState{width=" + width + ", height=" + height + ", orientation="
  621. + orientation + ", fonts=" + fonts + ", fontCount=" + fontCount + "}";
  622. }
  623. }
  624. /**
  625. * Block level state data
  626. */
  627. private class AFPData extends org.apache.fop.util.AbstractPaintingState.AbstractData {
  628. private static final long serialVersionUID = -1789481244175275686L;
  629. /** The current fill status */
  630. private boolean filled;
  631. private String imageUri;
  632. /** {@inheritDoc} */
  633. @Override
  634. public Object clone() {
  635. AFPData obj = (AFPData) super.clone();
  636. obj.filled = this.filled;
  637. obj.imageUri = this.imageUri;
  638. return obj;
  639. }
  640. /** {@inheritDoc} */
  641. @Override
  642. public String toString() {
  643. return "AFPData{" + super.toString() + ", filled=" + filled + ", imageUri=" + imageUri
  644. + "}";
  645. }
  646. /** {@inheritDoc} */
  647. @Override
  648. protected AbstractData instantiate() {
  649. return new AFPData();
  650. }
  651. }
  652. }