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.

IFParser.java 38KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894
  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.Point;
  22. import java.awt.Rectangle;
  23. import java.awt.geom.AffineTransform;
  24. import java.util.HashMap;
  25. import java.util.Locale;
  26. import java.util.Map;
  27. import java.util.Set;
  28. import javax.xml.transform.Source;
  29. import javax.xml.transform.Transformer;
  30. import javax.xml.transform.TransformerException;
  31. import javax.xml.transform.sax.SAXResult;
  32. import javax.xml.transform.sax.SAXTransformerFactory;
  33. import org.w3c.dom.DOMImplementation;
  34. import org.w3c.dom.Document;
  35. import org.xml.sax.Attributes;
  36. import org.xml.sax.ContentHandler;
  37. import org.xml.sax.SAXException;
  38. import org.xml.sax.helpers.AttributesImpl;
  39. import org.xml.sax.helpers.DefaultHandler;
  40. import org.apache.commons.logging.Log;
  41. import org.apache.commons.logging.LogFactory;
  42. import org.apache.xmlgraphics.util.QName;
  43. import org.apache.fop.accessibility.AccessibilityEventProducer;
  44. import org.apache.fop.accessibility.StructureTreeElement;
  45. import org.apache.fop.accessibility.StructureTreeEventHandler;
  46. import org.apache.fop.apps.FOUserAgent;
  47. import org.apache.fop.fo.ElementMapping;
  48. import org.apache.fop.fo.ElementMappingRegistry;
  49. import org.apache.fop.fo.expr.PropertyException;
  50. import org.apache.fop.fo.extensions.InternalElementMapping;
  51. import org.apache.fop.render.intermediate.extensions.DocumentNavigationExtensionConstants;
  52. import org.apache.fop.render.intermediate.extensions.DocumentNavigationHandler;
  53. import org.apache.fop.traits.BorderProps;
  54. import org.apache.fop.traits.RuleStyle;
  55. import org.apache.fop.util.ColorUtil;
  56. import org.apache.fop.util.ContentHandlerFactory;
  57. import org.apache.fop.util.ContentHandlerFactoryRegistry;
  58. import org.apache.fop.util.DOMBuilderContentHandlerFactory;
  59. import org.apache.fop.util.DefaultErrorListener;
  60. import org.apache.fop.util.LanguageTags;
  61. import org.apache.fop.util.XMLUtil;
  62. /**
  63. * This is a parser for the intermediate format XML which converts the intermediate file into
  64. * {@link IFPainter} events.
  65. */
  66. public class IFParser implements IFConstants {
  67. /** Logger instance */
  68. protected static final Log log = LogFactory.getLog(IFParser.class);
  69. private static SAXTransformerFactory tFactory
  70. = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
  71. private static Set<String> handledNamespaces = new java.util.HashSet<String>();
  72. static {
  73. handledNamespaces.add(XMLNS_NAMESPACE_URI);
  74. handledNamespaces.add(XML_NAMESPACE);
  75. handledNamespaces.add(NAMESPACE);
  76. handledNamespaces.add(XLINK_NAMESPACE);
  77. }
  78. /**
  79. * Parses an intermediate file and paints it.
  80. * @param src the Source instance pointing to the intermediate file
  81. * @param documentHandler the intermediate format document handler used to process the IF events
  82. * @param userAgent the user agent
  83. * @throws TransformerException if an error occurs while parsing the area tree XML
  84. * @throws IFException if an IF-related error occurs inside the target document handler
  85. */
  86. public void parse(Source src, IFDocumentHandler documentHandler, FOUserAgent userAgent)
  87. throws TransformerException, IFException {
  88. try {
  89. Transformer transformer = tFactory.newTransformer();
  90. transformer.setErrorListener(new DefaultErrorListener(log));
  91. SAXResult res = new SAXResult(getContentHandler(documentHandler, userAgent));
  92. transformer.transform(src, res);
  93. } catch (TransformerException te) {
  94. Throwable cause = te.getCause();
  95. //Unpack original IFException if applicable
  96. if (cause instanceof SAXException) {
  97. SAXException se = (SAXException) cause;
  98. cause = se.getCause();
  99. if (cause instanceof IFException) {
  100. throw (IFException) cause;
  101. }
  102. } else if (cause instanceof IFException) {
  103. throw (IFException) cause;
  104. }
  105. throw te;
  106. }
  107. }
  108. /**
  109. * Creates a new ContentHandler instance that you can send the area tree XML to. The parsed
  110. * pages are added to the AreaTreeModel instance you pass in as a parameter.
  111. * @param documentHandler the intermediate format document handler used to process the IF events
  112. * @param userAgent the user agent
  113. * @return the ContentHandler instance to receive the SAX stream from the area tree XML
  114. */
  115. public ContentHandler getContentHandler(IFDocumentHandler documentHandler,
  116. FOUserAgent userAgent) {
  117. ElementMappingRegistry elementMappingRegistry
  118. = userAgent.getElementMappingRegistry();
  119. return new Handler(documentHandler, userAgent, elementMappingRegistry);
  120. }
  121. private static class Handler extends DefaultHandler {
  122. private Map<String, ElementHandler> elementHandlers = new HashMap<String, ElementHandler>();
  123. private IFDocumentHandler documentHandler;
  124. private IFPainter painter;
  125. private FOUserAgent userAgent;
  126. private ElementMappingRegistry elementMappingRegistry;
  127. private Attributes lastAttributes;
  128. private StringBuffer content = new StringBuffer();
  129. private boolean ignoreCharacters = true;
  130. //private Stack delegateStack = new Stack();
  131. private int delegateDepth;
  132. private ContentHandler delegate;
  133. private boolean inForeignObject;
  134. private Document foreignObject;
  135. private ContentHandler navParser;
  136. private StructureTreeHandler structureTreeHandler;
  137. private Attributes pageSequenceAttributes;
  138. private Map<String, StructureTreeElement> structureTreeElements
  139. = new HashMap<String, StructureTreeElement>();
  140. private class StructureTreeHandler extends DefaultHandler {
  141. protected final StructureTreeEventHandler structureTreeEventHandler;
  142. StructureTreeHandler(StructureTreeEventHandler structureTreeEventHandler) {
  143. this.structureTreeEventHandler = structureTreeEventHandler;
  144. }
  145. void startStructureTree(String type) {
  146. }
  147. @Override
  148. public void startElement(String uri, String localName, String qName,
  149. Attributes attributes) throws SAXException {
  150. if (!"structure-tree".equals(localName)) {
  151. if (localName.equals("marked-content")) {
  152. localName = "#PCDATA";
  153. }
  154. StructureTreeElement parent = getStructureTreeElement(attributes);
  155. String structID = attributes.getValue(InternalElementMapping.URI,
  156. InternalElementMapping.STRUCT_ID);
  157. if (structID == null) {
  158. structureTreeEventHandler.startNode(localName, attributes, parent);
  159. } else if (localName.equals("external-graphic")
  160. || localName.equals("instream-foreign-object")) {
  161. StructureTreeElement structureTreeElement
  162. = structureTreeEventHandler.startImageNode(localName, attributes, parent);
  163. structureTreeElements.put(structID, structureTreeElement);
  164. } else {
  165. StructureTreeElement structureTreeElement = structureTreeEventHandler
  166. .startReferencedNode(localName, attributes, parent);
  167. structureTreeElements.put(structID, structureTreeElement);
  168. }
  169. }
  170. }
  171. @Override
  172. public void endElement(String uri, String localName, String arqNameg2)
  173. throws SAXException {
  174. if (!"structure-tree".equals(localName)) {
  175. structureTreeEventHandler.endNode(localName);
  176. }
  177. }
  178. }
  179. private class MainStructureTreeHandler extends StructureTreeHandler {
  180. private final Locale pageSequenceLanguage;
  181. MainStructureTreeHandler(StructureTreeEventHandler structureTreeEventHandler,
  182. Locale pageSequenceLanguage) throws SAXException {
  183. super(structureTreeEventHandler);
  184. this.pageSequenceLanguage = pageSequenceLanguage;
  185. }
  186. @Override
  187. void startStructureTree(String type) {
  188. structureTreeEventHandler.startPageSequence(pageSequenceLanguage, type);
  189. }
  190. public void endDocument() throws SAXException {
  191. startIFElement(EL_PAGE_SEQUENCE, pageSequenceAttributes);
  192. pageSequenceAttributes = null;
  193. }
  194. }
  195. public Handler(IFDocumentHandler documentHandler, FOUserAgent userAgent,
  196. ElementMappingRegistry elementMappingRegistry) {
  197. this.documentHandler = documentHandler;
  198. this.userAgent = userAgent;
  199. this.elementMappingRegistry = elementMappingRegistry;
  200. elementHandlers.put(EL_DOCUMENT, new DocumentHandler());
  201. elementHandlers.put(EL_HEADER, new DocumentHeaderHandler());
  202. elementHandlers.put(EL_LOCALE, new LocaleHandler());
  203. elementHandlers.put(EL_TRAILER, new DocumentTrailerHandler());
  204. elementHandlers.put(EL_PAGE_SEQUENCE, new PageSequenceHandler());
  205. elementHandlers.put(EL_PAGE, new PageHandler());
  206. elementHandlers.put(EL_PAGE_HEADER, new PageHeaderHandler());
  207. elementHandlers.put(EL_PAGE_CONTENT, new PageContentHandler());
  208. elementHandlers.put(EL_PAGE_TRAILER, new PageTrailerHandler());
  209. //Page content
  210. elementHandlers.put(EL_VIEWPORT, new ViewportHandler());
  211. elementHandlers.put(EL_GROUP, new GroupHandler());
  212. elementHandlers.put(EL_ID, new IDHandler());
  213. elementHandlers.put(EL_FONT, new FontHandler());
  214. elementHandlers.put(EL_TEXT, new TextHandler());
  215. elementHandlers.put(EL_CLIP_RECT, new ClipRectHandler());
  216. elementHandlers.put(EL_RECT, new RectHandler());
  217. elementHandlers.put(EL_LINE, new LineHandler());
  218. elementHandlers.put(EL_BORDER_RECT, new BorderRectHandler());
  219. elementHandlers.put(EL_IMAGE, new ImageHandler());
  220. }
  221. private void establishForeignAttributes(Map<QName, String> foreignAttributes) {
  222. documentHandler.getContext().setForeignAttributes(foreignAttributes);
  223. }
  224. private void resetForeignAttributes() {
  225. documentHandler.getContext().resetForeignAttributes();
  226. }
  227. /** {@inheritDoc} */
  228. public void startElement(String uri, String localName, String qName, Attributes attributes)
  229. throws SAXException {
  230. if (delegate != null) {
  231. delegateDepth++;
  232. delegate.startElement(uri, localName, qName, attributes);
  233. } else {
  234. boolean handled = true;
  235. if (NAMESPACE.equals(uri)) {
  236. if (localName.equals(EL_PAGE_SEQUENCE) && userAgent.isAccessibilityEnabled()) {
  237. pageSequenceAttributes = new AttributesImpl(attributes);
  238. Locale language = getLanguage(attributes);
  239. structureTreeHandler = new MainStructureTreeHandler(
  240. userAgent.getStructureTreeEventHandler(), language);
  241. } else if (localName.equals(EL_STRUCTURE_TREE)) {
  242. if (userAgent.isAccessibilityEnabled()) {
  243. String type = attributes.getValue("type");
  244. structureTreeHandler.startStructureTree(type);
  245. delegate = structureTreeHandler;
  246. } else {
  247. /* Delegate to a handler that does nothing */
  248. delegate = new DefaultHandler();
  249. }
  250. delegateDepth++;
  251. delegate.startDocument();
  252. delegate.startElement(uri, localName, qName, attributes);
  253. } else {
  254. if (pageSequenceAttributes != null) {
  255. /*
  256. * This means that no structure-element tag was
  257. * found in the XML, otherwise a
  258. * StructureTreeBuilderWrapper object would have
  259. * been created, which would have reset the
  260. * pageSequenceAttributes field.
  261. */
  262. AccessibilityEventProducer.Provider
  263. .get(userAgent.getEventBroadcaster())
  264. .noStructureTreeInXML(this);
  265. }
  266. handled = startIFElement(localName, attributes);
  267. }
  268. } else if (DocumentNavigationExtensionConstants.NAMESPACE.equals(uri)) {
  269. if (this.navParser == null) {
  270. this.navParser = new DocumentNavigationHandler(
  271. this.documentHandler.getDocumentNavigationHandler(),
  272. structureTreeElements);
  273. }
  274. delegate = this.navParser;
  275. delegateDepth++;
  276. delegate.startDocument();
  277. delegate.startElement(uri, localName, qName, attributes);
  278. } else {
  279. ContentHandlerFactoryRegistry registry
  280. = userAgent.getContentHandlerFactoryRegistry();
  281. ContentHandlerFactory factory = registry.getFactory(uri);
  282. if (factory == null) {
  283. DOMImplementation domImplementation
  284. = elementMappingRegistry.getDOMImplementationForNamespace(uri);
  285. if (domImplementation == null) {
  286. domImplementation = ElementMapping.getDefaultDOMImplementation();
  287. /*
  288. throw new SAXException("No DOMImplementation could be"
  289. + " identified to handle namespace: " + uri);
  290. */
  291. }
  292. factory = new DOMBuilderContentHandlerFactory(uri, domImplementation);
  293. }
  294. delegate = factory.createContentHandler();
  295. delegateDepth++;
  296. delegate.startDocument();
  297. delegate.startElement(uri, localName, qName, attributes);
  298. }
  299. if (!handled) {
  300. if (uri == null || uri.length() == 0) {
  301. throw new SAXException("Unhandled element " + localName
  302. + " in namespace: " + uri);
  303. } else {
  304. log.warn("Unhandled element " + localName
  305. + " in namespace: " + uri);
  306. }
  307. }
  308. }
  309. }
  310. private static Locale getLanguage(Attributes attributes) {
  311. String xmllang = attributes.getValue(XML_NAMESPACE, "lang");
  312. return (xmllang == null) ? null : LanguageTags.toLocale(xmllang);
  313. }
  314. private boolean startIFElement(String localName, Attributes attributes)
  315. throws SAXException {
  316. lastAttributes = new AttributesImpl(attributes);
  317. ElementHandler elementHandler = elementHandlers.get(localName);
  318. content.setLength(0);
  319. ignoreCharacters = true;
  320. if (elementHandler != null) {
  321. ignoreCharacters = elementHandler.ignoreCharacters();
  322. try {
  323. elementHandler.startElement(attributes);
  324. } catch (IFException ife) {
  325. handleIFException(ife);
  326. }
  327. return true;
  328. } else {
  329. return false;
  330. }
  331. }
  332. private void handleIFException(IFException ife) throws SAXException {
  333. Throwable cause = ife.getCause();
  334. if (cause instanceof SAXException) {
  335. //unwrap
  336. throw (SAXException) cause;
  337. } else {
  338. //wrap
  339. throw new SAXException(ife);
  340. }
  341. }
  342. /** {@inheritDoc} */
  343. public void endElement(String uri, String localName, String qName) throws SAXException {
  344. if (delegate != null) {
  345. delegate.endElement(uri, localName, qName);
  346. delegateDepth--;
  347. if (delegateDepth == 0) {
  348. delegate.endDocument();
  349. if (delegate instanceof ContentHandlerFactory.ObjectSource) {
  350. Object obj = ((ContentHandlerFactory.ObjectSource)delegate).getObject();
  351. if (inForeignObject) {
  352. this.foreignObject = (Document)obj;
  353. } else {
  354. handleExternallyGeneratedObject(obj);
  355. }
  356. }
  357. delegate = null; //Sub-document is processed, return to normal processing
  358. }
  359. } else {
  360. if (NAMESPACE.equals(uri)) {
  361. ElementHandler elementHandler = elementHandlers.get(localName);
  362. if (elementHandler != null) {
  363. try {
  364. elementHandler.endElement();
  365. } catch (IFException ife) {
  366. handleIFException(ife);
  367. }
  368. content.setLength(0);
  369. }
  370. ignoreCharacters = true;
  371. } else {
  372. if (log.isTraceEnabled()) {
  373. log.trace("Ignoring " + localName + " in namespace: " + uri);
  374. }
  375. }
  376. }
  377. }
  378. // ============== Element handlers for the intermediate format =============
  379. private interface ElementHandler {
  380. void startElement(Attributes attributes) throws IFException, SAXException;
  381. void endElement() throws IFException;
  382. boolean ignoreCharacters();
  383. }
  384. private abstract class AbstractElementHandler implements ElementHandler {
  385. public void startElement(Attributes attributes) throws IFException, SAXException {
  386. //nop
  387. }
  388. public void endElement() throws IFException {
  389. //nop
  390. }
  391. public boolean ignoreCharacters() {
  392. return true;
  393. }
  394. }
  395. private class DocumentHandler extends AbstractElementHandler {
  396. public void startElement(Attributes attributes) throws IFException {
  397. documentHandler.startDocument();
  398. }
  399. public void endElement() throws IFException {
  400. documentHandler.endDocument();
  401. }
  402. }
  403. private class DocumentHeaderHandler extends AbstractElementHandler {
  404. public void startElement(Attributes attributes) throws IFException {
  405. documentHandler.startDocumentHeader();
  406. }
  407. public void endElement() throws IFException {
  408. documentHandler.endDocumentHeader();
  409. }
  410. }
  411. private class LocaleHandler extends AbstractElementHandler {
  412. public void startElement(Attributes attributes) throws IFException {
  413. documentHandler.setDocumentLocale(getLanguage(attributes));
  414. }
  415. }
  416. private class DocumentTrailerHandler extends AbstractElementHandler {
  417. public void startElement(Attributes attributes) throws IFException {
  418. documentHandler.startDocumentTrailer();
  419. }
  420. public void endElement() throws IFException {
  421. documentHandler.endDocumentTrailer();
  422. }
  423. }
  424. private class PageSequenceHandler extends AbstractElementHandler {
  425. public void startElement(Attributes attributes) throws IFException {
  426. String id = attributes.getValue("id");
  427. Locale language = getLanguage(attributes);
  428. if (language != null) {
  429. documentHandler.getContext().setLanguage(language);
  430. }
  431. Map<QName, String> foreignAttributes = getForeignAttributes(lastAttributes);
  432. establishForeignAttributes(foreignAttributes);
  433. documentHandler.startPageSequence(id);
  434. resetForeignAttributes();
  435. }
  436. public void endElement() throws IFException {
  437. documentHandler.endPageSequence();
  438. documentHandler.getContext().setLanguage(null);
  439. }
  440. }
  441. private class PageHandler extends AbstractElementHandler {
  442. public void startElement(Attributes attributes) throws IFException {
  443. int index = Integer.parseInt(attributes.getValue("index"));
  444. String name = attributes.getValue("name");
  445. String pageMasterName = attributes.getValue("page-master-name");
  446. int width = Integer.parseInt(attributes.getValue("width"));
  447. int height = Integer.parseInt(attributes.getValue("height"));
  448. Map<QName, String> foreignAttributes = getForeignAttributes(lastAttributes);
  449. establishForeignAttributes(foreignAttributes);
  450. documentHandler.startPage(index, name, pageMasterName,
  451. new Dimension(width, height));
  452. documentHandler.getContext().setPageNumber(index + 1);
  453. resetForeignAttributes();
  454. }
  455. public void endElement() throws IFException {
  456. documentHandler.endPage();
  457. }
  458. }
  459. private class PageHeaderHandler extends AbstractElementHandler {
  460. public void startElement(Attributes attributes) throws IFException {
  461. documentHandler.startPageHeader();
  462. structureTreeHandler = new StructureTreeHandler(userAgent.getStructureTreeEventHandler());
  463. }
  464. public void endElement() throws IFException {
  465. documentHandler.endPageHeader();
  466. }
  467. }
  468. private class PageContentHandler extends AbstractElementHandler {
  469. public void startElement(Attributes attributes) throws IFException {
  470. painter = documentHandler.startPageContent();
  471. }
  472. public void endElement() throws IFException {
  473. painter = null;
  474. documentHandler.getContext().setID("");
  475. documentHandler.endPageContent();
  476. }
  477. }
  478. private class PageTrailerHandler extends AbstractElementHandler {
  479. public void startElement(Attributes attributes) throws IFException {
  480. documentHandler.startPageTrailer();
  481. }
  482. public void endElement() throws IFException {
  483. documentHandler.endPageTrailer();
  484. }
  485. }
  486. private class ViewportHandler extends AbstractElementHandler {
  487. public void startElement(Attributes attributes) throws IFException {
  488. String transform = attributes.getValue("transform");
  489. AffineTransform[] transforms
  490. = AffineTransformArrayParser.createAffineTransform(transform);
  491. int width = Integer.parseInt(attributes.getValue("width"));
  492. int height = Integer.parseInt(attributes.getValue("height"));
  493. Rectangle clipRect = XMLUtil.getAttributeAsRectangle(attributes, "clip-rect");
  494. painter.startViewport(transforms, new Dimension(width, height), clipRect);
  495. }
  496. public void endElement() throws IFException {
  497. painter.endViewport();
  498. }
  499. }
  500. private class GroupHandler extends AbstractElementHandler {
  501. public void startElement(Attributes attributes) throws IFException {
  502. String transform = attributes.getValue("transform");
  503. AffineTransform[] transforms
  504. = AffineTransformArrayParser.createAffineTransform(transform);
  505. String layer = attributes.getValue("layer");
  506. painter.startGroup(transforms, layer);
  507. }
  508. public void endElement() throws IFException {
  509. painter.endGroup();
  510. }
  511. }
  512. private class IDHandler extends AbstractElementHandler {
  513. @Override
  514. public void startElement(Attributes attributes) throws IFException, SAXException {
  515. String id = attributes.getValue("name");
  516. documentHandler.getContext().setID(id);
  517. }
  518. }
  519. private class FontHandler extends AbstractElementHandler {
  520. public void startElement(Attributes attributes) throws IFException {
  521. String family = attributes.getValue("family");
  522. String style = attributes.getValue("style");
  523. Integer weight = XMLUtil.getAttributeAsInteger(attributes, "weight");
  524. String variant = attributes.getValue("variant");
  525. Integer size = XMLUtil.getAttributeAsInteger(attributes, "size");
  526. Color color;
  527. try {
  528. color = getAttributeAsColor(attributes, "color");
  529. } catch (PropertyException pe) {
  530. throw new IFException("Error parsing the color attribute", pe);
  531. }
  532. painter.setFont(family, style, weight, variant, size, color);
  533. }
  534. }
  535. private class TextHandler extends AbstractElementHandler {
  536. public void endElement() throws IFException {
  537. int x = Integer.parseInt(lastAttributes.getValue("x"));
  538. int y = Integer.parseInt(lastAttributes.getValue("y"));
  539. String s = lastAttributes.getValue("letter-spacing");
  540. int letterSpacing = (s != null ? Integer.parseInt(s) : 0);
  541. s = lastAttributes.getValue("word-spacing");
  542. int wordSpacing = (s != null ? Integer.parseInt(s) : 0);
  543. int[] dx = XMLUtil.getAttributeAsIntArray(lastAttributes, "dx");
  544. int[][] dp = XMLUtil.getAttributeAsPositionAdjustments(lastAttributes, "dp");
  545. // if only DX present, then convert DX to DP; otherwise use only DP,
  546. // effectively ignoring DX
  547. if ((dp == null) && (dx != null)) {
  548. dp = IFUtil.convertDXToDP(dx);
  549. }
  550. establishStructureTreeElement(lastAttributes);
  551. boolean isHyphenated = Boolean.valueOf(lastAttributes.getValue("hyphenated"));
  552. if (isHyphenated) {
  553. documentHandler.getContext().setHyphenated(isHyphenated);
  554. }
  555. boolean nextIsSpace = Boolean.valueOf(lastAttributes.getValue("next-is-space"));
  556. painter.drawText(x, y, letterSpacing, wordSpacing, dp, content.toString(), nextIsSpace);
  557. documentHandler.getContext().setHyphenated(false);
  558. resetStructureTreeElement();
  559. }
  560. public boolean ignoreCharacters() {
  561. return false;
  562. }
  563. }
  564. private class ClipRectHandler extends AbstractElementHandler {
  565. public void startElement(Attributes attributes) throws IFException {
  566. int x = Integer.parseInt(attributes.getValue("x"));
  567. int y = Integer.parseInt(attributes.getValue("y"));
  568. int width = Integer.parseInt(attributes.getValue("width"));
  569. int height = Integer.parseInt(attributes.getValue("height"));
  570. BorderProps[] borders = new BorderProps[4];
  571. for (int i = 0; i < 4; i++) {
  572. String b = attributes.getValue(SIDES[i]);
  573. if (b != null) {
  574. borders[i] = BorderProps.valueOf(userAgent, b);
  575. }
  576. }
  577. if (!(borders[0] == null && borders[1] == null
  578. && borders[2] == null && borders[3] == null)) {
  579. painter.clipBackground(new Rectangle(x, y, width, height),
  580. borders[0], borders[1], borders[2], borders[3]);
  581. }
  582. painter.clipRect(new Rectangle(x, y, width, height));
  583. }
  584. }
  585. private class RectHandler extends AbstractElementHandler {
  586. public void startElement(Attributes attributes) throws IFException {
  587. int x = Integer.parseInt(attributes.getValue("x"));
  588. int y = Integer.parseInt(attributes.getValue("y"));
  589. int width = Integer.parseInt(attributes.getValue("width"));
  590. int height = Integer.parseInt(attributes.getValue("height"));
  591. Color fillColor;
  592. try {
  593. fillColor = getAttributeAsColor(attributes, "fill");
  594. } catch (PropertyException pe) {
  595. throw new IFException("Error parsing the fill attribute", pe);
  596. }
  597. Rectangle rectangularArea = new Rectangle(x, y, width, height);
  598. //TODO should rect be overloaded to include rounded corners
  599. // or should we introduce a new IF element?
  600. BorderProps[] borders = new BorderProps[4];
  601. for (int i = 0; i < 4; i++) {
  602. String b = attributes.getValue(SIDES[i]);
  603. if (b != null) {
  604. borders[i] = BorderProps.valueOf(userAgent, b);
  605. }
  606. }
  607. if (!(borders[0] == null && borders[1] == null
  608. && borders[2] == null && borders[3] == null)) {
  609. painter.clipBackground(rectangularArea,
  610. borders[0], borders[1], borders[2], borders[3]);
  611. }
  612. painter.fillRect(rectangularArea , fillColor);
  613. }
  614. }
  615. private class LineHandler extends AbstractElementHandler {
  616. public void startElement(Attributes attributes) throws IFException {
  617. int x1 = Integer.parseInt(attributes.getValue("x1"));
  618. int y1 = Integer.parseInt(attributes.getValue("y1"));
  619. int x2 = Integer.parseInt(attributes.getValue("x2"));
  620. int y2 = Integer.parseInt(attributes.getValue("y2"));
  621. int width = Integer.parseInt(attributes.getValue("stroke-width"));
  622. Color color;
  623. try {
  624. color = getAttributeAsColor(attributes, "color");
  625. } catch (PropertyException pe) {
  626. throw new IFException("Error parsing the fill attribute", pe);
  627. }
  628. RuleStyle style = RuleStyle.valueOf(attributes.getValue("style"));
  629. painter.drawLine(new Point(x1, y1), new Point(x2, y2), width, color, style);
  630. }
  631. }
  632. private static final String[] SIDES = new String[] {"top", "bottom", "left", "right"};
  633. private class BorderRectHandler extends AbstractElementHandler {
  634. public void startElement(Attributes attributes) throws IFException {
  635. int x = Integer.parseInt(attributes.getValue("x"));
  636. int y = Integer.parseInt(attributes.getValue("y"));
  637. int width = Integer.parseInt(attributes.getValue("width"));
  638. int height = Integer.parseInt(attributes.getValue("height"));
  639. BorderProps[] borders = new BorderProps[4];
  640. for (int i = 0; i < 4; i++) {
  641. String b = attributes.getValue(SIDES[i]);
  642. if (b != null) {
  643. borders[i] = BorderProps.valueOf(userAgent, b);
  644. }
  645. }
  646. Color backgroundColor;
  647. try {
  648. backgroundColor = getAttributeAsColor(attributes, "inner-background-color");
  649. } catch (PropertyException pe) {
  650. throw new IFException("Error parsing the color attribute", pe);
  651. }
  652. painter.drawBorderRect(new Rectangle(x, y, width, height),
  653. borders[0], borders[1], borders[2], borders[3], backgroundColor);
  654. }
  655. }
  656. private class ImageHandler extends AbstractElementHandler {
  657. public void startElement(Attributes attributes) throws IFException {
  658. inForeignObject = true;
  659. }
  660. public void endElement() throws IFException {
  661. int x = Integer.parseInt(lastAttributes.getValue("x"));
  662. int y = Integer.parseInt(lastAttributes.getValue("y"));
  663. int width = Integer.parseInt(lastAttributes.getValue("width"));
  664. int height = Integer.parseInt(lastAttributes.getValue("height"));
  665. Map<QName, String> foreignAttributes = getForeignAttributes(lastAttributes);
  666. establishForeignAttributes(foreignAttributes);
  667. establishStructureTreeElement(lastAttributes);
  668. if (foreignObject != null) {
  669. painter.drawImage(foreignObject,
  670. new Rectangle(x, y, width, height));
  671. foreignObject = null;
  672. } else {
  673. String uri = lastAttributes.getValue(
  674. XLINK_HREF.getNamespaceURI(), XLINK_HREF.getLocalName());
  675. if (uri == null) {
  676. throw new IFException("xlink:href is missing on image", null);
  677. }
  678. painter.drawImage(uri, new Rectangle(x, y, width, height));
  679. }
  680. resetStructureTreeElement();
  681. resetForeignAttributes();
  682. inForeignObject = false;
  683. }
  684. public boolean ignoreCharacters() {
  685. return false;
  686. }
  687. }
  688. // ====================================================================
  689. /**
  690. * Handles objects created by "sub-parsers" that implement the ObjectSource interface.
  691. * An example of object handled here are ExtensionAttachments.
  692. * @param obj the Object to be handled.
  693. * @throws SAXException if an error occurs while handling the extension object
  694. */
  695. protected void handleExternallyGeneratedObject(Object obj) throws SAXException {
  696. try {
  697. documentHandler.handleExtensionObject(obj);
  698. } catch (IFException ife) {
  699. handleIFException(ife);
  700. }
  701. }
  702. private Color getAttributeAsColor(Attributes attributes, String name)
  703. throws PropertyException {
  704. String s = attributes.getValue(name);
  705. if (s == null) {
  706. return null;
  707. } else {
  708. return ColorUtil.parseColorString(userAgent, s);
  709. }
  710. }
  711. private static Map<QName, String> getForeignAttributes(Attributes atts) {
  712. Map<QName, String> foreignAttributes = null;
  713. for (int i = 0, c = atts.getLength(); i < c; i++) {
  714. String ns = atts.getURI(i);
  715. if (ns.length() > 0) {
  716. if (handledNamespaces.contains(ns)) {
  717. continue;
  718. }
  719. if (foreignAttributes == null) {
  720. foreignAttributes = new java.util.HashMap<QName, String>();
  721. }
  722. QName qname = new QName(ns, atts.getQName(i));
  723. foreignAttributes.put(qname, atts.getValue(i));
  724. }
  725. }
  726. return foreignAttributes;
  727. }
  728. private void establishStructureTreeElement(Attributes attributes) {
  729. StructureTreeElement element = getStructureTreeElement(attributes);
  730. if (element != null) {
  731. documentHandler.getContext().setStructureTreeElement(element);
  732. }
  733. }
  734. private StructureTreeElement getStructureTreeElement(Attributes attributes) {
  735. String structRef = attributes.getValue(InternalElementMapping.URI, InternalElementMapping.STRUCT_REF);
  736. if (structRef != null && structRef.length() > 0) {
  737. assert structureTreeElements.containsKey(structRef);
  738. return structureTreeElements.get(structRef);
  739. } else {
  740. return null;
  741. }
  742. }
  743. private void resetStructureTreeElement() {
  744. documentHandler.getContext().resetStructureTreeElement();
  745. }
  746. /** {@inheritDoc} */
  747. public void characters(char[] ch, int start, int length) throws SAXException {
  748. if (delegate != null) {
  749. delegate.characters(ch, start, length);
  750. } else if (!ignoreCharacters) {
  751. this.content.append(ch, start, length);
  752. }
  753. }
  754. }
  755. }