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.

IFSerializer.java 29KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820
  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.render.intermediate;
  19. import java.awt.Color;
  20. import java.awt.Dimension;
  21. import java.awt.Paint;
  22. import java.awt.Point;
  23. import java.awt.Rectangle;
  24. import java.awt.geom.AffineTransform;
  25. import java.util.Iterator;
  26. import java.util.List;
  27. import java.util.Locale;
  28. import java.util.Map;
  29. import org.w3c.dom.Document;
  30. import org.xml.sax.SAXException;
  31. import org.xml.sax.helpers.AttributesImpl;
  32. import org.apache.xmlgraphics.util.QName;
  33. import org.apache.xmlgraphics.util.XMLizable;
  34. import org.apache.fop.accessibility.StructureTreeEventHandler;
  35. import org.apache.fop.fonts.FontInfo;
  36. import org.apache.fop.render.PrintRendererConfigurator;
  37. import org.apache.fop.render.RenderingContext;
  38. import org.apache.fop.render.intermediate.extensions.AbstractAction;
  39. import org.apache.fop.render.intermediate.extensions.Bookmark;
  40. import org.apache.fop.render.intermediate.extensions.BookmarkTree;
  41. import org.apache.fop.render.intermediate.extensions.DocumentNavigationExtensionConstants;
  42. import org.apache.fop.render.intermediate.extensions.Link;
  43. import org.apache.fop.render.intermediate.extensions.NamedDestination;
  44. import org.apache.fop.traits.BorderProps;
  45. import org.apache.fop.traits.RuleStyle;
  46. import org.apache.fop.util.ColorUtil;
  47. import org.apache.fop.util.DOM2SAX;
  48. import org.apache.fop.util.LanguageTags;
  49. import org.apache.fop.util.XMLConstants;
  50. import org.apache.fop.util.XMLUtil;
  51. /**
  52. * IFPainter implementation that serializes the intermediate format to XML.
  53. */
  54. public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
  55. implements IFConstants, IFPainter, IFDocumentNavigationHandler {
  56. private IFDocumentHandler mimicHandler;
  57. private int pageSequenceIndex; // used for accessibility
  58. /** Holds the intermediate format state */
  59. private IFState state;
  60. private String currentID = "";
  61. private IFStructureTreeBuilder structureTreeBuilder;
  62. /** {@inheritDoc} */
  63. @Override
  64. protected String getMainNamespace() {
  65. return NAMESPACE;
  66. }
  67. /** {@inheritDoc} */
  68. public boolean supportsPagesOutOfOrder() {
  69. return false;
  70. //Theoretically supported but disabled to improve performance when
  71. //rendering the IF to the final format later on
  72. }
  73. /** {@inheritDoc} */
  74. public String getMimeType() {
  75. return MIME_TYPE;
  76. }
  77. /** {@inheritDoc} */
  78. public IFDocumentHandlerConfigurator getConfigurator() {
  79. if (this.mimicHandler != null) {
  80. return getMimickedDocumentHandler().getConfigurator();
  81. } else {
  82. return new PrintRendererConfigurator(getUserAgent());
  83. }
  84. }
  85. /** {@inheritDoc} */
  86. @Override
  87. public IFDocumentNavigationHandler getDocumentNavigationHandler() {
  88. return this;
  89. }
  90. /**
  91. * Tells this serializer to mimic the given document handler (mostly applies to the font set
  92. * that is used during layout).
  93. * @param targetHandler the document handler to mimic
  94. */
  95. public void mimicDocumentHandler(IFDocumentHandler targetHandler) {
  96. this.mimicHandler = targetHandler;
  97. }
  98. /**
  99. * Returns the document handler that is being mimicked by this serializer.
  100. * @return the mimicked document handler or null if no such document handler has been set
  101. */
  102. public IFDocumentHandler getMimickedDocumentHandler() {
  103. return this.mimicHandler;
  104. }
  105. /** {@inheritDoc} */
  106. public FontInfo getFontInfo() {
  107. if (this.mimicHandler != null) {
  108. return this.mimicHandler.getFontInfo();
  109. } else {
  110. return null;
  111. }
  112. }
  113. /** {@inheritDoc} */
  114. public void setFontInfo(FontInfo fontInfo) {
  115. if (this.mimicHandler != null) {
  116. this.mimicHandler.setFontInfo(fontInfo);
  117. }
  118. }
  119. /** {@inheritDoc} */
  120. public void setDefaultFontInfo(FontInfo fontInfo) {
  121. if (this.mimicHandler != null) {
  122. this.mimicHandler.setDefaultFontInfo(fontInfo);
  123. }
  124. }
  125. @Override
  126. public StructureTreeEventHandler getStructureTreeEventHandler() {
  127. if (structureTreeBuilder == null) {
  128. structureTreeBuilder = new IFStructureTreeBuilder();
  129. }
  130. return structureTreeBuilder;
  131. }
  132. /** {@inheritDoc} */
  133. @Override
  134. public void startDocument() throws IFException {
  135. super.startDocument();
  136. try {
  137. handler.startDocument();
  138. handler.startPrefixMapping("", NAMESPACE);
  139. handler.startPrefixMapping(XLINK_PREFIX, XLINK_NAMESPACE);
  140. handler.startPrefixMapping(DocumentNavigationExtensionConstants.PREFIX,
  141. DocumentNavigationExtensionConstants.NAMESPACE);
  142. handler.startElement(EL_DOCUMENT);
  143. } catch (SAXException e) {
  144. throw new IFException("SAX error in startDocument()", e);
  145. }
  146. }
  147. @Override
  148. public void setDocumentLocale(Locale locale) {
  149. AttributesImpl atts = new AttributesImpl();
  150. atts.addAttribute(XML_NAMESPACE, "lang", "xml:lang", XMLUtil.CDATA,
  151. LanguageTags.toLanguageTag(locale));
  152. try {
  153. handler.startElement(EL_LOCALE, atts);
  154. handler.endElement(EL_LOCALE);
  155. } catch (SAXException e) {
  156. throw new RuntimeException("Unable to create the " + EL_LOCALE + " element.", e);
  157. }
  158. }
  159. /** {@inheritDoc} */
  160. @Override
  161. public void startDocumentHeader() throws IFException {
  162. try {
  163. handler.startElement(EL_HEADER);
  164. } catch (SAXException e) {
  165. throw new IFException("SAX error in startDocumentHeader()", e);
  166. }
  167. }
  168. /** {@inheritDoc} */
  169. @Override
  170. public void endDocumentHeader() throws IFException {
  171. try {
  172. handler.endElement(EL_HEADER);
  173. } catch (SAXException e) {
  174. throw new IFException("SAX error in startDocumentHeader()", e);
  175. }
  176. }
  177. /** {@inheritDoc} */
  178. @Override
  179. public void startDocumentTrailer() throws IFException {
  180. try {
  181. handler.startElement(EL_TRAILER);
  182. } catch (SAXException e) {
  183. throw new IFException("SAX error in startDocumentTrailer()", e);
  184. }
  185. }
  186. /** {@inheritDoc} */
  187. @Override
  188. public void endDocumentTrailer() throws IFException {
  189. try {
  190. handler.endElement(EL_TRAILER);
  191. } catch (SAXException e) {
  192. throw new IFException("SAX error in endDocumentTrailer()", e);
  193. }
  194. }
  195. /** {@inheritDoc} */
  196. public void endDocument() throws IFException {
  197. try {
  198. handler.endElement(EL_DOCUMENT);
  199. handler.endDocument();
  200. finishDocumentNavigation();
  201. } catch (SAXException e) {
  202. throw new IFException("SAX error in endDocument()", e);
  203. }
  204. }
  205. /** {@inheritDoc} */
  206. public void startPageSequence(String id) throws IFException {
  207. try {
  208. AttributesImpl atts = new AttributesImpl();
  209. if (id != null) {
  210. atts.addAttribute(XML_NAMESPACE, "id", "xml:id", XMLUtil.CDATA, id);
  211. }
  212. Locale lang = getContext().getLanguage();
  213. if (lang != null) {
  214. atts.addAttribute(XML_NAMESPACE, "lang", "xml:lang", XMLUtil.CDATA,
  215. LanguageTags.toLanguageTag(lang));
  216. }
  217. XMLUtil.addAttribute(atts, XMLConstants.XML_SPACE, "preserve");
  218. addForeignAttributes(atts);
  219. handler.startElement(EL_PAGE_SEQUENCE, atts);
  220. if (this.getUserAgent().isAccessibilityEnabled()) {
  221. assert (structureTreeBuilder != null);
  222. structureTreeBuilder.replayEventsForPageSequence(handler, pageSequenceIndex++);
  223. }
  224. } catch (SAXException e) {
  225. throw new IFException("SAX error in startPageSequence()", e);
  226. }
  227. }
  228. /** {@inheritDoc} */
  229. public void endPageSequence() throws IFException {
  230. try {
  231. handler.endElement(EL_PAGE_SEQUENCE);
  232. } catch (SAXException e) {
  233. throw new IFException("SAX error in endPageSequence()", e);
  234. }
  235. }
  236. /** {@inheritDoc} */
  237. public void startPage(int index, String name, String pageMasterName, Dimension size)
  238. throws IFException {
  239. try {
  240. AttributesImpl atts = new AttributesImpl();
  241. addAttribute(atts, "index", Integer.toString(index));
  242. addAttribute(atts, "name", name);
  243. addAttribute(atts, "page-master-name", pageMasterName);
  244. addAttribute(atts, "width", Integer.toString(size.width));
  245. addAttribute(atts, "height", Integer.toString(size.height));
  246. addForeignAttributes(atts);
  247. handler.startElement(EL_PAGE, atts);
  248. } catch (SAXException e) {
  249. throw new IFException("SAX error in startPage()", e);
  250. }
  251. }
  252. /** {@inheritDoc} */
  253. @Override
  254. public void startPageHeader() throws IFException {
  255. try {
  256. handler.startElement(EL_PAGE_HEADER);
  257. } catch (SAXException e) {
  258. throw new IFException("SAX error in startPageHeader()", e);
  259. }
  260. }
  261. /** {@inheritDoc} */
  262. @Override
  263. public void endPageHeader() throws IFException {
  264. try {
  265. handler.endElement(EL_PAGE_HEADER);
  266. } catch (SAXException e) {
  267. throw new IFException("SAX error in endPageHeader()", e);
  268. }
  269. }
  270. /** {@inheritDoc} */
  271. public IFPainter startPageContent() throws IFException {
  272. try {
  273. handler.startElement(EL_PAGE_CONTENT);
  274. this.state = IFState.create();
  275. return this;
  276. } catch (SAXException e) {
  277. throw new IFException("SAX error in startPageContent()", e);
  278. }
  279. }
  280. /** {@inheritDoc} */
  281. public void endPageContent() throws IFException {
  282. try {
  283. this.state = null;
  284. currentID = "";
  285. handler.endElement(EL_PAGE_CONTENT);
  286. } catch (SAXException e) {
  287. throw new IFException("SAX error in endPageContent()", e);
  288. }
  289. }
  290. /** {@inheritDoc} */
  291. @Override
  292. public void startPageTrailer() throws IFException {
  293. try {
  294. handler.startElement(EL_PAGE_TRAILER);
  295. } catch (SAXException e) {
  296. throw new IFException("SAX error in startPageTrailer()", e);
  297. }
  298. }
  299. /** {@inheritDoc} */
  300. @Override
  301. public void endPageTrailer() throws IFException {
  302. try {
  303. commitNavigation();
  304. handler.endElement(EL_PAGE_TRAILER);
  305. } catch (SAXException e) {
  306. throw new IFException("SAX error in endPageTrailer()", e);
  307. }
  308. }
  309. /** {@inheritDoc} */
  310. public void endPage() throws IFException {
  311. try {
  312. handler.endElement(EL_PAGE);
  313. } catch (SAXException e) {
  314. throw new IFException("SAX error in endPage()", e);
  315. }
  316. }
  317. //---=== IFPainter ===---
  318. /** {@inheritDoc} */
  319. public void startViewport(AffineTransform transform, Dimension size, Rectangle clipRect)
  320. throws IFException {
  321. startViewport(IFUtil.toString(transform), size, clipRect);
  322. }
  323. /** {@inheritDoc} */
  324. public void startViewport(AffineTransform[] transforms, Dimension size, Rectangle clipRect)
  325. throws IFException {
  326. startViewport(IFUtil.toString(transforms), size, clipRect);
  327. }
  328. private void startViewport(String transform, Dimension size, Rectangle clipRect)
  329. throws IFException {
  330. try {
  331. AttributesImpl atts = new AttributesImpl();
  332. if (transform != null && transform.length() > 0) {
  333. addAttribute(atts, "transform", transform);
  334. }
  335. addAttribute(atts, "width", Integer.toString(size.width));
  336. addAttribute(atts, "height", Integer.toString(size.height));
  337. if (clipRect != null) {
  338. addAttribute(atts, "clip-rect", IFUtil.toString(clipRect));
  339. }
  340. handler.startElement(EL_VIEWPORT, atts);
  341. } catch (SAXException e) {
  342. throw new IFException("SAX error in startViewport()", e);
  343. }
  344. }
  345. /** {@inheritDoc} */
  346. public void endViewport() throws IFException {
  347. try {
  348. handler.endElement(EL_VIEWPORT);
  349. } catch (SAXException e) {
  350. throw new IFException("SAX error in endViewport()", e);
  351. }
  352. }
  353. /** {@inheritDoc} */
  354. public void startGroup(AffineTransform[] transforms) throws IFException {
  355. startGroup(IFUtil.toString(transforms));
  356. }
  357. /** {@inheritDoc} */
  358. public void startGroup(AffineTransform transform) throws IFException {
  359. startGroup(IFUtil.toString(transform));
  360. }
  361. private void startGroup(String transform) throws IFException {
  362. try {
  363. AttributesImpl atts = new AttributesImpl();
  364. if (transform != null && transform.length() > 0) {
  365. addAttribute(atts, "transform", transform);
  366. }
  367. handler.startElement(EL_GROUP, atts);
  368. } catch (SAXException e) {
  369. throw new IFException("SAX error in startGroup()", e);
  370. }
  371. }
  372. /** {@inheritDoc} */
  373. public void endGroup() throws IFException {
  374. try {
  375. handler.endElement(EL_GROUP);
  376. } catch (SAXException e) {
  377. throw new IFException("SAX error in endGroup()", e);
  378. }
  379. }
  380. /** {@inheritDoc} */
  381. public void drawImage(String uri, Rectangle rect) throws IFException {
  382. try {
  383. addID();
  384. AttributesImpl atts = new AttributesImpl();
  385. addAttribute(atts, XLINK_HREF, uri);
  386. addAttribute(atts, "x", Integer.toString(rect.x));
  387. addAttribute(atts, "y", Integer.toString(rect.y));
  388. addAttribute(atts, "width", Integer.toString(rect.width));
  389. addAttribute(atts, "height", Integer.toString(rect.height));
  390. addForeignAttributes(atts);
  391. addStructurePointerAttribute(atts);
  392. handler.element(EL_IMAGE, atts);
  393. } catch (SAXException e) {
  394. throw new IFException("SAX error in startGroup()", e);
  395. }
  396. }
  397. private void addForeignAttributes(AttributesImpl atts) throws SAXException {
  398. Map foreignAttributes = getContext().getForeignAttributes();
  399. if (!foreignAttributes.isEmpty()) {
  400. Iterator iter = foreignAttributes.entrySet().iterator();
  401. while (iter.hasNext()) {
  402. Map.Entry entry = (Map.Entry)iter.next();
  403. addAttribute(atts, (QName)entry.getKey(), entry.getValue().toString());
  404. }
  405. }
  406. }
  407. /** {@inheritDoc} */
  408. public void drawImage(Document doc, Rectangle rect) throws IFException {
  409. try {
  410. addID();
  411. AttributesImpl atts = new AttributesImpl();
  412. addAttribute(atts, "x", Integer.toString(rect.x));
  413. addAttribute(atts, "y", Integer.toString(rect.y));
  414. addAttribute(atts, "width", Integer.toString(rect.width));
  415. addAttribute(atts, "height", Integer.toString(rect.height));
  416. addForeignAttributes(atts);
  417. addStructurePointerAttribute(atts);
  418. handler.startElement(EL_IMAGE, atts);
  419. new DOM2SAX(handler).writeDocument(doc, true);
  420. handler.endElement(EL_IMAGE);
  421. } catch (SAXException e) {
  422. throw new IFException("SAX error in startGroup()", e);
  423. }
  424. }
  425. private static String toString(Paint paint) {
  426. if (paint instanceof Color) {
  427. return ColorUtil.colorToString((Color)paint);
  428. } else {
  429. throw new UnsupportedOperationException("Paint not supported: " + paint);
  430. }
  431. }
  432. /** {@inheritDoc} */
  433. public void clipRect(Rectangle rect) throws IFException {
  434. try {
  435. AttributesImpl atts = new AttributesImpl();
  436. addAttribute(atts, "x", Integer.toString(rect.x));
  437. addAttribute(atts, "y", Integer.toString(rect.y));
  438. addAttribute(atts, "width", Integer.toString(rect.width));
  439. addAttribute(atts, "height", Integer.toString(rect.height));
  440. handler.element(EL_CLIP_RECT, atts);
  441. } catch (SAXException e) {
  442. throw new IFException("SAX error in clipRect()", e);
  443. }
  444. }
  445. /** {@inheritDoc} */
  446. public void fillRect(Rectangle rect, Paint fill) throws IFException {
  447. if (fill == null) {
  448. return;
  449. }
  450. try {
  451. AttributesImpl atts = new AttributesImpl();
  452. addAttribute(atts, "x", Integer.toString(rect.x));
  453. addAttribute(atts, "y", Integer.toString(rect.y));
  454. addAttribute(atts, "width", Integer.toString(rect.width));
  455. addAttribute(atts, "height", Integer.toString(rect.height));
  456. addAttribute(atts, "fill", toString(fill));
  457. handler.element(EL_RECT, atts);
  458. } catch (SAXException e) {
  459. throw new IFException("SAX error in fillRect()", e);
  460. }
  461. }
  462. /** {@inheritDoc} */
  463. public void drawBorderRect(Rectangle rect, BorderProps before, BorderProps after,
  464. BorderProps start, BorderProps end) throws IFException {
  465. if (before == null && after == null && start == null && end == null) {
  466. return;
  467. }
  468. try {
  469. AttributesImpl atts = new AttributesImpl();
  470. addAttribute(atts, "x", Integer.toString(rect.x));
  471. addAttribute(atts, "y", Integer.toString(rect.y));
  472. addAttribute(atts, "width", Integer.toString(rect.width));
  473. addAttribute(atts, "height", Integer.toString(rect.height));
  474. if (before != null) {
  475. addAttribute(atts, "before", before.toString());
  476. }
  477. if (after != null) {
  478. addAttribute(atts, "after", after.toString());
  479. }
  480. if (start != null) {
  481. addAttribute(atts, "start", start.toString());
  482. }
  483. if (end != null) {
  484. addAttribute(atts, "end", end.toString());
  485. }
  486. handler.element(EL_BORDER_RECT, atts);
  487. } catch (SAXException e) {
  488. throw new IFException("SAX error in drawBorderRect()", e);
  489. }
  490. }
  491. /** {@inheritDoc} */
  492. public void drawLine(Point start, Point end, int width, Color color, RuleStyle style)
  493. throws IFException {
  494. try {
  495. addID();
  496. AttributesImpl atts = new AttributesImpl();
  497. addAttribute(atts, "x1", Integer.toString(start.x));
  498. addAttribute(atts, "y1", Integer.toString(start.y));
  499. addAttribute(atts, "x2", Integer.toString(end.x));
  500. addAttribute(atts, "y2", Integer.toString(end.y));
  501. addAttribute(atts, "stroke-width", Integer.toString(width));
  502. addAttribute(atts, "color", ColorUtil.colorToString(color));
  503. addAttribute(atts, "style", style.getName());
  504. handler.element(EL_LINE, atts);
  505. } catch (SAXException e) {
  506. throw new IFException("SAX error in drawLine()", e);
  507. }
  508. }
  509. /** {@inheritDoc} */
  510. public void drawText(int x, int y, int letterSpacing, int wordSpacing,
  511. int[] dx, String text) throws IFException {
  512. try {
  513. addID();
  514. AttributesImpl atts = new AttributesImpl();
  515. addAttribute(atts, "x", Integer.toString(x));
  516. addAttribute(atts, "y", Integer.toString(y));
  517. if (letterSpacing != 0) {
  518. addAttribute(atts, "letter-spacing", Integer.toString(letterSpacing));
  519. }
  520. if (wordSpacing != 0) {
  521. addAttribute(atts, "word-spacing", Integer.toString(wordSpacing));
  522. }
  523. if (dx != null) {
  524. addAttribute(atts, "dx", IFUtil.toString(dx));
  525. }
  526. addStructurePointerAttribute(atts);
  527. handler.startElement(EL_TEXT, atts);
  528. char[] chars = text.toCharArray();
  529. handler.characters(chars, 0, chars.length);
  530. handler.endElement(EL_TEXT);
  531. } catch (SAXException e) {
  532. throw new IFException("SAX error in setFont()", e);
  533. }
  534. }
  535. /** {@inheritDoc} */
  536. public void setFont(String family, String style, Integer weight, String variant, Integer size,
  537. Color color) throws IFException {
  538. try {
  539. AttributesImpl atts = new AttributesImpl();
  540. boolean changed;
  541. if (family != null) {
  542. changed = !family.equals(state.getFontFamily());
  543. if (changed) {
  544. state.setFontFamily(family);
  545. addAttribute(atts, "family", family);
  546. }
  547. }
  548. if (style != null) {
  549. changed = !style.equals(state.getFontStyle());
  550. if (changed) {
  551. state.setFontStyle(style);
  552. addAttribute(atts, "style", style);
  553. }
  554. }
  555. if (weight != null) {
  556. changed = (weight.intValue() != state.getFontWeight());
  557. if (changed) {
  558. state.setFontWeight(weight.intValue());
  559. addAttribute(atts, "weight", weight.toString());
  560. }
  561. }
  562. if (variant != null) {
  563. changed = !variant.equals(state.getFontVariant());
  564. if (changed) {
  565. state.setFontVariant(variant);
  566. addAttribute(atts, "variant", variant);
  567. }
  568. }
  569. if (size != null) {
  570. changed = (size.intValue() != state.getFontSize());
  571. if (changed) {
  572. state.setFontSize(size.intValue());
  573. addAttribute(atts, "size", size.toString());
  574. }
  575. }
  576. if (color != null) {
  577. changed = !org.apache.xmlgraphics.java2d.color.ColorUtil.isSameColor(
  578. color, state.getTextColor());
  579. if (changed) {
  580. state.setTextColor(color);
  581. addAttribute(atts, "color", toString(color));
  582. }
  583. }
  584. if (atts.getLength() > 0) {
  585. handler.element(EL_FONT, atts);
  586. }
  587. } catch (SAXException e) {
  588. throw new IFException("SAX error in setFont()", e);
  589. }
  590. }
  591. /** {@inheritDoc} */
  592. public void handleExtensionObject(Object extension) throws IFException {
  593. if (extension instanceof XMLizable) {
  594. try {
  595. ((XMLizable)extension).toSAX(this.handler);
  596. } catch (SAXException e) {
  597. throw new IFException("SAX error while handling extension object", e);
  598. }
  599. } else {
  600. throw new UnsupportedOperationException(
  601. "Extension must implement XMLizable: "
  602. + extension + " (" + extension.getClass().getName() + ")");
  603. }
  604. }
  605. /**
  606. * @return a new rendering context
  607. * @throws IllegalStateException unless overridden
  608. */
  609. protected RenderingContext createRenderingContext() throws IllegalStateException {
  610. throw new IllegalStateException("Should never be called!");
  611. }
  612. private void addAttribute(AttributesImpl atts,
  613. org.apache.xmlgraphics.util.QName attribute, String value) throws SAXException {
  614. handler.startPrefixMapping(attribute.getPrefix(), attribute.getNamespaceURI());
  615. XMLUtil.addAttribute(atts, attribute, value);
  616. }
  617. private void addAttribute(AttributesImpl atts, String localName, String value) {
  618. XMLUtil.addAttribute(atts, localName, value);
  619. }
  620. private void addStructurePointerAttribute(AttributesImpl atts) {
  621. String ptr = getContext().getStructurePointer();
  622. if (ptr != null) {
  623. addAttribute(atts, "ptr", ptr);
  624. }
  625. }
  626. private void addID() throws SAXException {
  627. String id = getContext().getID();
  628. if (!currentID.equals(id)) {
  629. AttributesImpl atts = new AttributesImpl();
  630. addAttribute(atts, "name", id);
  631. handler.startElement(EL_ID, atts);
  632. handler.endElement(EL_ID);
  633. currentID = id;
  634. }
  635. }
  636. private Map incompleteActions = new java.util.HashMap();
  637. private List completeActions = new java.util.LinkedList();
  638. private void noteAction(AbstractAction action) {
  639. if (action == null) {
  640. throw new NullPointerException("action must not be null");
  641. }
  642. if (!action.isComplete()) {
  643. assert action.hasID();
  644. incompleteActions.put(action.getID(), action);
  645. }
  646. }
  647. /** {@inheritDoc} */
  648. public void renderNamedDestination(NamedDestination destination) throws IFException {
  649. noteAction(destination.getAction());
  650. AttributesImpl atts = new AttributesImpl();
  651. atts.addAttribute(null, "name", "name", XMLConstants.CDATA, destination.getName());
  652. try {
  653. handler.startElement(DocumentNavigationExtensionConstants.NAMED_DESTINATION, atts);
  654. serializeXMLizable(destination.getAction());
  655. handler.endElement(DocumentNavigationExtensionConstants.NAMED_DESTINATION);
  656. } catch (SAXException e) {
  657. throw new IFException("SAX error serializing named destination", e);
  658. }
  659. }
  660. /** {@inheritDoc} */
  661. public void renderBookmarkTree(BookmarkTree tree) throws IFException {
  662. AttributesImpl atts = new AttributesImpl();
  663. try {
  664. handler.startElement(DocumentNavigationExtensionConstants.BOOKMARK_TREE, atts);
  665. Iterator iter = tree.getBookmarks().iterator();
  666. while (iter.hasNext()) {
  667. Bookmark b = (Bookmark)iter.next();
  668. if (b.getAction() != null) {
  669. serializeBookmark(b);
  670. }
  671. }
  672. handler.endElement(DocumentNavigationExtensionConstants.BOOKMARK_TREE);
  673. } catch (SAXException e) {
  674. throw new IFException("SAX error serializing bookmark tree", e);
  675. }
  676. }
  677. private void serializeBookmark(Bookmark bookmark) throws SAXException, IFException {
  678. noteAction(bookmark.getAction());
  679. AttributesImpl atts = new AttributesImpl();
  680. atts.addAttribute(null, "title", "title", XMLUtil.CDATA, bookmark.getTitle());
  681. atts.addAttribute(null, "starting-state", "starting-state",
  682. XMLUtil.CDATA, bookmark.isShown() ? "show" : "hide");
  683. handler.startElement(DocumentNavigationExtensionConstants.BOOKMARK, atts);
  684. serializeXMLizable(bookmark.getAction());
  685. Iterator iter = bookmark.getChildBookmarks().iterator();
  686. while (iter.hasNext()) {
  687. Bookmark b = (Bookmark)iter.next();
  688. if (b.getAction() != null) {
  689. serializeBookmark(b);
  690. }
  691. }
  692. handler.endElement(DocumentNavigationExtensionConstants.BOOKMARK);
  693. }
  694. /** {@inheritDoc} */
  695. public void renderLink(Link link) throws IFException {
  696. noteAction(link.getAction());
  697. AttributesImpl atts = new AttributesImpl();
  698. atts.addAttribute(null, "rect", "rect",
  699. XMLConstants.CDATA, IFUtil.toString(link.getTargetRect()));
  700. if (getUserAgent().isAccessibilityEnabled()) {
  701. addAttribute(atts, "ptr", link.getAction().getStructurePointer());
  702. }
  703. try {
  704. handler.startElement(DocumentNavigationExtensionConstants.LINK, atts);
  705. serializeXMLizable(link.getAction());
  706. handler.endElement(DocumentNavigationExtensionConstants.LINK);
  707. } catch (SAXException e) {
  708. throw new IFException("SAX error serializing link", e);
  709. }
  710. }
  711. /** {@inheritDoc} */
  712. public void addResolvedAction(AbstractAction action) throws IFException {
  713. assert action.isComplete();
  714. assert action.hasID();
  715. AbstractAction noted = (AbstractAction)incompleteActions.remove(action.getID());
  716. if (noted != null) {
  717. completeActions.add(action);
  718. } else {
  719. //ignore as it was already complete when it was first used.
  720. }
  721. }
  722. private void commitNavigation() throws IFException {
  723. Iterator iter = this.completeActions.iterator();
  724. while (iter.hasNext()) {
  725. AbstractAction action = (AbstractAction)iter.next();
  726. iter.remove();
  727. serializeXMLizable(action);
  728. }
  729. assert this.completeActions.size() == 0;
  730. }
  731. private void finishDocumentNavigation() {
  732. assert this.incompleteActions.size() == 0 : "Still holding incomplete actions!";
  733. }
  734. private void serializeXMLizable(XMLizable object) throws IFException {
  735. try {
  736. object.toSAX(handler);
  737. } catch (SAXException e) {
  738. throw new IFException("SAX error serializing object", e);
  739. }
  740. }
  741. }