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 34KB

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