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.

PtocaBuilder.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  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.ptoca;
  19. import java.awt.Color;
  20. import java.awt.color.ColorSpace;
  21. import java.io.IOException;
  22. import java.io.OutputStream;
  23. import org.apache.commons.io.output.ByteArrayOutputStream;
  24. import org.apache.fop.afp.fonts.CharactersetEncoder.EncodedChars;
  25. import org.apache.xmlgraphics.java2d.color.CIELabColorSpace;
  26. import org.apache.xmlgraphics.java2d.color.ColorUtil;
  27. import org.apache.xmlgraphics.java2d.color.ColorWithAlternatives;
  28. /**
  29. * Generator class for PTOCA data structures.
  30. */
  31. public abstract class PtocaBuilder implements PtocaConstants {
  32. private ByteArrayOutputStream baout = new ByteArrayOutputStream(256);
  33. /** the current x coordinate. */
  34. private int currentX = -1;
  35. /** the current y coordinate */
  36. private int currentY = -1;
  37. /** the current font */
  38. private int currentFont = Integer.MIN_VALUE;
  39. /** the current orientation */
  40. private int currentOrientation = 0;
  41. /** the current color */
  42. private Color currentColor = Color.BLACK;
  43. /** the current variable space increment */
  44. private int currentVariableSpaceCharacterIncrement = 0;
  45. /** the current inter character adjustment */
  46. private int currentInterCharacterAdjustment = 0;
  47. /**
  48. * Returns an {@link OutputStream} for the next control sequence. This gives a subclass a
  49. * chance to do chunking of control sequences into multiple presentation text data objects.
  50. * @param length the length of the following control sequence
  51. * @return the output stream where the control sequence will be written to
  52. */
  53. protected abstract OutputStream getOutputStreamForControlSequence(int length);
  54. private static byte chained(byte functionType) {
  55. return (byte)(functionType | CHAIN_BIT);
  56. }
  57. private void newControlSequence() {
  58. baout.reset();
  59. }
  60. private void commit(byte functionType) throws IOException {
  61. int length = baout.size() + 2;
  62. assert length < 256;
  63. OutputStream out = getOutputStreamForControlSequence(length);
  64. out.write(length);
  65. out.write(functionType);
  66. baout.writeTo(out);
  67. }
  68. private void write(byte[] data, int offset, int length) {
  69. baout.write(data, offset, length);
  70. }
  71. private void writeByte(int data) {
  72. baout.write(data);
  73. }
  74. private void writeShort(int data) {
  75. baout.write((data >>> 8) & 0xFF);
  76. baout.write(data & 0xFF);
  77. }
  78. /**
  79. * Writes the introducer for a chained control sequence.
  80. * @throws IOException if an I/O error occurs
  81. */
  82. public void writeIntroducer() throws IOException {
  83. OutputStream out = getOutputStreamForControlSequence(ESCAPE.length);
  84. out.write(ESCAPE);
  85. }
  86. /**
  87. * The Set Coded Font Local control sequence activates a coded font and
  88. * specifies the character attributes to be used.
  89. * <p>
  90. * This is a modal control sequence.
  91. *
  92. * @param font The font local identifier.
  93. * @throws IOException if an I/O error occurs
  94. */
  95. public void setCodedFont(byte font) throws IOException {
  96. // Avoid unnecessary specification of the font
  97. if (currentFont == font) {
  98. return;
  99. } else {
  100. currentFont = font;
  101. }
  102. newControlSequence();
  103. writeByte(font);
  104. commit(chained(SCFL));
  105. }
  106. /**
  107. * Establishes the current presentation position on the baseline at a new
  108. * I-axis coordinate, which is a specified number of measurement units from
  109. * the B-axis. There is no change to the current B-axis coordinate.
  110. *
  111. * @param coordinate The coordinate for the inline move.
  112. * @throws IOException if an I/O error occurs
  113. */
  114. public void absoluteMoveInline(int coordinate) throws IOException {
  115. if (coordinate == this.currentX) {
  116. return;
  117. }
  118. newControlSequence();
  119. writeShort(coordinate);
  120. commit(chained(AMI));
  121. currentX = coordinate;
  122. }
  123. /**
  124. * Moves the inline coordinate of the presentation position relative to the current
  125. * inline position.
  126. * @param increment the increment in 1/1440 inch units
  127. * @throws IOException if an I/O error occurs
  128. */
  129. public void relativeMoveInline(int increment) throws IOException {
  130. newControlSequence();
  131. writeShort(increment);
  132. commit(chained(RMI));
  133. }
  134. /**
  135. * Establishes the baseline and the current presentation position at a new
  136. * B-axis coordinate, which is a specified number of measurement units from
  137. * the I-axis. There is no change to the current I-axis coordinate.
  138. *
  139. * @param coordinate The coordinate for the baseline move.
  140. * @throws IOException if an I/O error occurs
  141. */
  142. public void absoluteMoveBaseline(int coordinate) throws IOException {
  143. if (coordinate == this.currentY) {
  144. return;
  145. }
  146. newControlSequence();
  147. writeShort(coordinate);
  148. commit(chained(AMB));
  149. currentY = coordinate;
  150. currentX = -1;
  151. }
  152. private static final int TRANSPARENT_MAX_SIZE = 253;
  153. /**
  154. * The Transparent Data control sequence contains a sequence of code points
  155. * that are presented without a scan for embedded control sequences. If the data is larger
  156. * than fits in one chunk, additional chunks are automatically generated.
  157. *
  158. * @param data The text data to add.
  159. * @throws IOException if an I/O error occurs
  160. */
  161. public void addTransparentData(EncodedChars encodedChars) throws IOException {
  162. // data size greater than TRANSPARENT_MAX_SIZE, so slice
  163. int numTransData = encodedChars.getLength() / TRANSPARENT_DATA_MAX_SIZE;
  164. int currIndex = 0;
  165. for (int transDataCnt = 0; transDataCnt < numTransData; transDataCnt++) {
  166. addTransparentDataChunk(encodedChars, currIndex, TRANSPARENT_DATA_MAX_SIZE);
  167. currIndex += TRANSPARENT_DATA_MAX_SIZE;
  168. }
  169. int left = encodedChars.getLength() - currIndex;
  170. addTransparentDataChunk(encodedChars, currIndex, left);
  171. }
  172. private void addTransparentDataChunk(EncodedChars encodedChars, int offset, int length) throws IOException {
  173. newControlSequence();
  174. encodedChars.writeTo(baout, offset, length);
  175. commit(chained(TRN));
  176. }
  177. /**
  178. * Draws a line of specified length and specified width in the B-direction
  179. * from the current presentation position. The location of the current
  180. * presentation position is unchanged.
  181. *
  182. * @param length The length of the rule.
  183. * @param width The width of the rule.
  184. * @throws IOException if an I/O error occurs
  185. */
  186. public void drawBaxisRule(int length, int width) throws IOException {
  187. newControlSequence();
  188. writeShort(length); // Rule length
  189. writeShort(width); // Rule width
  190. writeByte(0); // Rule width fraction is always null. enough?
  191. commit(chained(DBR));
  192. }
  193. /**
  194. * Draws a line of specified length and specified width in the I-direction
  195. * from the current presentation position. The location of the current
  196. * presentation position is unchanged.
  197. *
  198. * @param length The length of the rule.
  199. * @param width The width of the rule.
  200. * @throws IOException if an I/O error occurs
  201. */
  202. public void drawIaxisRule(int length, int width) throws IOException {
  203. newControlSequence();
  204. writeShort(length); // Rule length
  205. writeShort(width); // Rule width
  206. writeByte(0); // Rule width fraction is always null. enough?
  207. commit(chained(DIR));
  208. }
  209. /**
  210. * The Set Text Orientation control sequence establishes the I-direction and
  211. * B-direction for the subsequent text. This is a modal control sequence.
  212. *
  213. * Semantics: This control sequence specifies the I-axis and B-axis
  214. * orientations with respect to the Xp-axis for the current Presentation
  215. * Text object. The orientations are rotational values expressed in degrees
  216. * and minutes.
  217. *
  218. * @param orientation The text orientation (0, 90, 180, 270).
  219. * @throws IOException if an I/O error occurs
  220. */
  221. public void setTextOrientation(int orientation) throws IOException {
  222. if (orientation == this.currentOrientation) {
  223. return;
  224. }
  225. newControlSequence();
  226. switch (orientation) {
  227. case 90:
  228. writeByte(0x2D);
  229. writeByte(0x00);
  230. writeByte(0x5A);
  231. writeByte(0x00);
  232. break;
  233. case 180:
  234. writeByte(0x5A);
  235. writeByte(0x00);
  236. writeByte(0x87);
  237. writeByte(0x00);
  238. break;
  239. case 270:
  240. writeByte(0x87);
  241. writeByte(0x00);
  242. writeByte(0x00);
  243. writeByte(0x00);
  244. break;
  245. default:
  246. writeByte(0x00);
  247. writeByte(0x00);
  248. writeByte(0x2D);
  249. writeByte(0x00);
  250. break;
  251. }
  252. commit(chained(STO));
  253. this.currentOrientation = orientation;
  254. currentX = -1;
  255. currentY = -1;
  256. }
  257. /**
  258. * The Set Extended Text Color control sequence specifies a color value and
  259. * defines the color space and encoding for that value. The specified color
  260. * value is applied to foreground areas of the text presentation space.
  261. * <p>
  262. * This is a modal control sequence.
  263. *
  264. * @param col The color to be set.
  265. * @throws IOException if an I/O error occurs
  266. */
  267. public void setExtendedTextColor(Color col) throws IOException {
  268. if (ColorUtil.isSameColor(col, currentColor)) {
  269. return;
  270. }
  271. if (col instanceof ColorWithAlternatives) {
  272. ColorWithAlternatives cwa = (ColorWithAlternatives)col;
  273. Color alt = cwa.getFirstAlternativeOfType(ColorSpace.TYPE_CMYK);
  274. if (alt != null) {
  275. col = alt;
  276. }
  277. }
  278. ColorSpace cs = col.getColorSpace();
  279. newControlSequence();
  280. if (col.getColorSpace().getType() == ColorSpace.TYPE_CMYK) {
  281. writeByte(0x00); // Reserved; must be zero
  282. writeByte(0x04); // Color space - 0x04 = CMYK
  283. writeByte(0x00); // Reserved; must be zero
  284. writeByte(0x00); // Reserved; must be zero
  285. writeByte(0x00); // Reserved; must be zero
  286. writeByte(0x00); // Reserved; must be zero
  287. writeByte(8); // Number of bits in component 1
  288. writeByte(8); // Number of bits in component 2
  289. writeByte(8); // Number of bits in component 3
  290. writeByte(8); // Number of bits in component 4
  291. float[] comps = col.getColorComponents(null);
  292. assert comps.length == 4;
  293. for (int i = 0; i < 4; i++) {
  294. int component = Math.round(comps[i] * 255);
  295. writeByte(component);
  296. }
  297. } else if (cs instanceof CIELabColorSpace) {
  298. writeByte(0x00); // Reserved; must be zero
  299. writeByte(0x08); // Color space - 0x08 = CIELAB
  300. writeByte(0x00); // Reserved; must be zero
  301. writeByte(0x00); // Reserved; must be zero
  302. writeByte(0x00); // Reserved; must be zero
  303. writeByte(0x00); // Reserved; must be zero
  304. writeByte(8); // Number of bits in component 1
  305. writeByte(8); // Number of bits in component 2
  306. writeByte(8); // Number of bits in component 3
  307. writeByte(0); // Number of bits in component 4
  308. //Sadly, 16 bit components don't seem to work
  309. float[] colorComponents = col.getColorComponents(null);
  310. int l = Math.round(colorComponents[0] * 255f);
  311. int a = Math.round(colorComponents[1] * 255f) - 128;
  312. int b = Math.round(colorComponents[2] * 255f) - 128;
  313. writeByte(l); // L*
  314. writeByte(a); // a*
  315. writeByte(b); // b*
  316. } else {
  317. writeByte(0x00); // Reserved; must be zero
  318. writeByte(0x01); // Color space - 0x01 = RGB
  319. writeByte(0x00); // Reserved; must be zero
  320. writeByte(0x00); // Reserved; must be zero
  321. writeByte(0x00); // Reserved; must be zero
  322. writeByte(0x00); // Reserved; must be zero
  323. writeByte(8); // Number of bits in component 1
  324. writeByte(8); // Number of bits in component 2
  325. writeByte(8); // Number of bits in component 3
  326. writeByte(0); // Number of bits in component 4
  327. writeByte(col.getRed()); // Red intensity
  328. writeByte(col.getGreen()); // Green intensity
  329. writeByte(col.getBlue()); // Blue intensity
  330. }
  331. commit(chained(SEC));
  332. this.currentColor = col;
  333. }
  334. /**
  335. * Sets the variable space character increment.
  336. * <p>
  337. * This is a modal control sequence.
  338. *
  339. * @param incr The increment to be set (positive integer, 1/1440 inch)
  340. * @throws IOException if an I/O error occurs
  341. */
  342. public void setVariableSpaceCharacterIncrement(int incr) throws IOException {
  343. if (incr == this.currentVariableSpaceCharacterIncrement) {
  344. return;
  345. }
  346. assert incr >= 0 && incr < (1 << 16);
  347. newControlSequence();
  348. writeShort(Math.abs(incr)); //Increment
  349. commit(chained(SVI));
  350. this.currentVariableSpaceCharacterIncrement = incr;
  351. }
  352. /**
  353. * Sets the intercharacter adjustment (additional increment or decrement between graphic
  354. * characters).
  355. * <p>
  356. * This is a modal control sequence.
  357. *
  358. * @param incr The increment to be set (1/1440 inch)
  359. * @throws IOException if an I/O error occurs
  360. */
  361. public void setInterCharacterAdjustment(int incr) throws IOException {
  362. if (incr == this.currentInterCharacterAdjustment) {
  363. return;
  364. }
  365. assert incr >= Short.MIN_VALUE && incr <= Short.MAX_VALUE;
  366. newControlSequence();
  367. writeShort(Math.abs(incr)); //Increment
  368. writeByte(incr >= 0 ? 0 : 1); // Direction
  369. commit(chained(SIA));
  370. this.currentInterCharacterAdjustment = incr;
  371. }
  372. /**
  373. * A control sequence is a sequence of bytes that specifies a control
  374. * function. A control sequence consists of a control sequence introducer
  375. * and zero or more parameters. The control sequence can extend multiple
  376. * presentation text data objects, but must eventually be terminated. This
  377. * method terminates the control sequence (by using a NOP command).
  378. *
  379. * @throws IOException if an I/O error occurs
  380. */
  381. public void endChainedControlSequence() throws IOException {
  382. newControlSequence();
  383. commit(NOP);
  384. }
  385. }