您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

AreaTreeParser.java 45KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046
  1. /*
  2. * Copyright 2006 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /* $Id$ */
  17. package org.apache.fop.area;
  18. import java.awt.geom.Rectangle2D;
  19. import java.util.List;
  20. import java.util.Map;
  21. import java.util.Stack;
  22. import java.util.StringTokenizer;
  23. import javax.xml.transform.Source;
  24. import javax.xml.transform.Transformer;
  25. import javax.xml.transform.TransformerConfigurationException;
  26. import javax.xml.transform.TransformerException;
  27. import javax.xml.transform.dom.DOMResult;
  28. import javax.xml.transform.sax.SAXResult;
  29. import javax.xml.transform.sax.SAXTransformerFactory;
  30. import javax.xml.transform.sax.TransformerHandler;
  31. import org.apache.commons.logging.Log;
  32. import org.apache.commons.logging.LogFactory;
  33. import org.apache.fop.apps.FOUserAgent;
  34. import org.apache.fop.area.Trait.Background;
  35. import org.apache.fop.area.Trait.Color;
  36. import org.apache.fop.area.inline.AbstractTextArea;
  37. import org.apache.fop.area.inline.Character;
  38. import org.apache.fop.area.inline.ForeignObject;
  39. import org.apache.fop.area.inline.Image;
  40. import org.apache.fop.area.inline.InlineBlockParent;
  41. import org.apache.fop.area.inline.InlineParent;
  42. import org.apache.fop.area.inline.Leader;
  43. import org.apache.fop.area.inline.Space;
  44. import org.apache.fop.area.inline.SpaceArea;
  45. import org.apache.fop.area.inline.TextArea;
  46. import org.apache.fop.area.inline.Viewport;
  47. import org.apache.fop.area.inline.WordArea;
  48. import org.apache.fop.fo.Constants;
  49. import org.apache.fop.fo.ElementMappingRegistry;
  50. import org.apache.fop.fo.extensions.ExtensionAttachment;
  51. import org.apache.fop.fonts.Font;
  52. import org.apache.fop.fonts.FontInfo;
  53. import org.apache.fop.image.FopImage;
  54. import org.apache.fop.image.ImageFactory;
  55. import org.apache.fop.traits.BorderProps;
  56. import org.apache.fop.util.ContentHandlerFactory;
  57. import org.apache.fop.util.ContentHandlerFactoryRegistry;
  58. import org.apache.fop.util.DefaultErrorListener;
  59. import org.w3c.dom.DOMImplementation;
  60. import org.w3c.dom.Document;
  61. import org.xml.sax.Attributes;
  62. import org.xml.sax.ContentHandler;
  63. import org.xml.sax.SAXException;
  64. import org.xml.sax.helpers.DefaultHandler;
  65. /**
  66. * This is a parser for the area tree XML (intermediate format) which is used to reread an area
  67. * tree (or part of it) into memory again for rendering to the final output format.
  68. */
  69. public class AreaTreeParser {
  70. /** Logger instance */
  71. protected static Log log = LogFactory.getLog(AreaTreeParser.class);
  72. private static SAXTransformerFactory tFactory
  73. = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
  74. /**
  75. * Parses an intermediate file (area tree XML) into an AreaTreeModel instance by adding
  76. * pages to it.
  77. * @param src the Source instance pointing to the intermediate file
  78. * @param treeModel the AreaTreeModel that the parsed pages are added to
  79. * @param userAgent the user agent
  80. * @throws TransformerException if an error occurs while parsing the area tree XML
  81. */
  82. public void parse(Source src, AreaTreeModel treeModel, FOUserAgent userAgent)
  83. throws TransformerException {
  84. Transformer transformer = tFactory.newTransformer();
  85. transformer.setErrorListener(new DefaultErrorListener(log));
  86. SAXResult res = new SAXResult(getContentHandler(treeModel, userAgent));
  87. transformer.transform(src, res);
  88. }
  89. /**
  90. * Creates a new ContentHandler instance that you can send the area tree XML to. The parsed
  91. * pages are added to the AreaTreeModel instance you pass in as a parameter.
  92. * @param treeModel the AreaTreeModel that the parsed pages are added to
  93. * @param userAgent the user agent
  94. * @return the ContentHandler instance to receive the SAX stream from the area tree XML
  95. */
  96. public ContentHandler getContentHandler(AreaTreeModel treeModel, FOUserAgent userAgent) {
  97. ElementMappingRegistry elementMappingRegistry
  98. = userAgent.getFactory().getElementMappingRegistry();
  99. return new Handler(treeModel, userAgent, elementMappingRegistry);
  100. }
  101. private static class Handler extends DefaultHandler {
  102. private Map makers = new java.util.HashMap();
  103. private AreaTreeModel treeModel;
  104. private FOUserAgent userAgent;
  105. private ElementMappingRegistry elementMappingRegistry;
  106. private Attributes lastAttributes;
  107. private StringBuffer content = new StringBuffer();
  108. private PageViewport currentPageViewport;
  109. private Stack areaStack = new Stack();
  110. private boolean firstFlow;
  111. private boolean pendingStartPageSequence;
  112. private Stack delegateStack = new Stack();
  113. private ContentHandler delegate;
  114. private DOMImplementation domImplementation;
  115. public Handler(AreaTreeModel treeModel, FOUserAgent userAgent,
  116. ElementMappingRegistry elementMappingRegistry) {
  117. this.treeModel = treeModel;
  118. this.userAgent = userAgent;
  119. this.elementMappingRegistry = elementMappingRegistry;
  120. makers.put("areaTree", new AreaTreeMaker());
  121. makers.put("page", new PageMaker());
  122. makers.put("pageSequence", new PageSequenceMaker());
  123. makers.put("title", new TitleMaker());
  124. makers.put("pageViewport", new PageViewportMaker());
  125. makers.put("regionViewport", new RegionViewportMaker());
  126. makers.put("regionBefore", new RegionBeforeMaker());
  127. makers.put("regionAfter", new RegionAfterMaker());
  128. makers.put("regionStart", new RegionStartMaker());
  129. makers.put("regionEnd", new RegionEndMaker());
  130. makers.put("regionBody", new RegionBodyMaker());
  131. makers.put("flow", new FlowMaker());
  132. makers.put("mainReference", new MainReferenceMaker());
  133. makers.put("span", new SpanMaker());
  134. makers.put("footnote", new FootnoteMaker());
  135. makers.put("beforeFloat", new BeforeFloatMaker());
  136. makers.put("block", new BlockMaker());
  137. makers.put("lineArea", new LineAreaMaker());
  138. makers.put("inlineparent", new InlineParentMaker());
  139. makers.put("inlineblockparent", new InlineBlockParentMaker());
  140. makers.put("text", new TextMaker());
  141. makers.put("word", new WordMaker());
  142. makers.put("space", new SpaceMaker());
  143. makers.put("char", new CharMaker());
  144. makers.put("leader", new LeaderMaker());
  145. makers.put("viewport", new ViewportMaker());
  146. makers.put("image", new ImageMaker());
  147. makers.put("foreignObject", new ForeignObjectMaker());
  148. }
  149. private static Rectangle2D parseRect(String rect) {
  150. StringTokenizer tokenizer = new StringTokenizer(rect, " ");
  151. return new Rectangle2D.Double(
  152. Double.parseDouble(tokenizer.nextToken()),
  153. Double.parseDouble(tokenizer.nextToken()),
  154. Double.parseDouble(tokenizer.nextToken()),
  155. Double.parseDouble(tokenizer.nextToken()));
  156. }
  157. private Area findAreaType(Class clazz) {
  158. if (areaStack.size() > 0) {
  159. int pos = areaStack.size() - 1;
  160. Object obj = null;
  161. while (pos >= 0 && !(clazz.isInstance(obj = areaStack.get(pos)))) {
  162. pos--;
  163. }
  164. if (pos >= 0) {
  165. return (Area)obj;
  166. }
  167. }
  168. return null;
  169. }
  170. private RegionViewport getCurrentRegionViewport() {
  171. return (RegionViewport)findAreaType(RegionViewport.class);
  172. }
  173. private BodyRegion getCurrentBodyRegion() {
  174. return (BodyRegion)findAreaType(BodyRegion.class);
  175. }
  176. private BlockParent getCurrentBlockParent() {
  177. return (BlockParent)findAreaType(BlockParent.class);
  178. }
  179. private AbstractTextArea getCurrentText() {
  180. return (AbstractTextArea)findAreaType(AbstractTextArea.class);
  181. }
  182. private Viewport getCurrentViewport() {
  183. return (Viewport)findAreaType(Viewport.class);
  184. }
  185. /** @see org.xml.sax.helpers.DefaultHandler */
  186. public void startElement(String uri, String localName, String qName, Attributes attributes)
  187. throws SAXException {
  188. if (delegate != null) {
  189. delegateStack.push(qName);
  190. delegate.startElement(uri, localName, qName, attributes);
  191. } else if (domImplementation != null) {
  192. //domImplementation is set so we need to start a new DOM building sub-process
  193. TransformerHandler handler;
  194. try {
  195. handler = tFactory.newTransformerHandler();
  196. } catch (TransformerConfigurationException e) {
  197. throw new SAXException("Error creating a new TransformerHandler", e);
  198. }
  199. Document doc = domImplementation.createDocument(uri, qName, null);
  200. //It's easier to work with an empty document, so remove the root element
  201. doc.removeChild(doc.getDocumentElement());
  202. handler.setResult(new DOMResult(doc));
  203. Area parent = (Area)areaStack.peek();
  204. ((ForeignObject)parent).setDocument(doc);
  205. //activate delegate for nested foreign document
  206. domImplementation = null; //Not needed anymore now
  207. this.delegate = handler;
  208. delegateStack.push(qName);
  209. delegate.startDocument();
  210. delegate.startElement(uri, localName, qName, attributes);
  211. } else {
  212. lastAttributes = attributes;
  213. boolean handled = true;
  214. if ("".equals(uri)) {
  215. Maker maker = (Maker)makers.get(localName);
  216. if (maker != null) {
  217. maker.startElement(attributes);
  218. } else if ("extension-attachments".equals(localName)) {
  219. //TODO implement me
  220. } else {
  221. handled = false;
  222. }
  223. } else {
  224. ContentHandlerFactoryRegistry registry
  225. = userAgent.getFactory().getContentHandlerFactoryRegistry();
  226. ContentHandlerFactory factory = registry.getFactory(uri);
  227. if (factory != null) {
  228. delegate = factory.createContentHandler();
  229. delegateStack.push(qName);
  230. delegate.startDocument();
  231. delegate.startElement(uri, localName, qName, attributes);
  232. } else {
  233. handled = false;
  234. }
  235. }
  236. if (!handled) {
  237. if (uri == null || uri.length() == 0) {
  238. throw new SAXException("Unhandled element " + localName
  239. + " in namespace: " + uri);
  240. } else {
  241. log.warn("Unhandled element " + localName
  242. + " in namespace: " + uri);
  243. }
  244. }
  245. }
  246. }
  247. /** @see org.xml.sax.helpers.DefaultHandler */
  248. public void endElement(String uri, String localName, String qName) throws SAXException {
  249. if (delegate != null) {
  250. delegate.endElement(uri, localName, qName);
  251. delegateStack.pop();
  252. if (delegateStack.size() == 0) {
  253. delegate.endDocument();
  254. if (delegate instanceof ContentHandlerFactory.ObjectSource) {
  255. Object obj = ((ContentHandlerFactory.ObjectSource)delegate).getObject();
  256. handleExternallyGeneratedObject(obj);
  257. }
  258. delegate = null; //Sub-document is processed, return to normal processing
  259. }
  260. } else {
  261. if ("".equals(uri)) {
  262. Maker maker = (Maker)makers.get(localName);
  263. if (maker != null) {
  264. maker.endElement();
  265. }
  266. } else {
  267. //log.debug("Ignoring " + localName + " in namespace: " + uri);
  268. }
  269. content.setLength(0); //Reset text buffer (see characters())
  270. }
  271. }
  272. // ============== Maker classes for the area tree objects =============
  273. private static interface Maker {
  274. void startElement(Attributes attributes) throws SAXException;
  275. void endElement();
  276. }
  277. private abstract class AbstractMaker implements Maker {
  278. public void startElement(Attributes attributes) throws SAXException {
  279. //nop
  280. }
  281. public void endElement() {
  282. //nop
  283. }
  284. }
  285. private class AreaTreeMaker extends AbstractMaker {
  286. //no overrides
  287. }
  288. private class PageSequenceMaker extends AbstractMaker {
  289. public void startElement(Attributes attributes) {
  290. pendingStartPageSequence = true;
  291. //treeModel.startPageSequence(null); Done after title or on the first viewport
  292. }
  293. }
  294. private class TitleMaker extends AbstractMaker {
  295. public void startElement(Attributes attributes) {
  296. LineArea line = new LineArea();
  297. areaStack.push(line);
  298. }
  299. public void endElement() {
  300. LineArea line = (LineArea)areaStack.pop();
  301. treeModel.startPageSequence(line);
  302. pendingStartPageSequence = false;
  303. }
  304. }
  305. private class PageViewportMaker extends AbstractMaker {
  306. public void startElement(Attributes attributes) {
  307. if (pendingStartPageSequence) {
  308. treeModel.startPageSequence(null);
  309. pendingStartPageSequence = false;
  310. }
  311. if (currentPageViewport != null) {
  312. throw new IllegalStateException("currentPageViewport must be null");
  313. }
  314. Rectangle2D viewArea = parseRect(attributes.getValue("bounds"));
  315. int pageNumber = getAttributeAsInteger(attributes, "nr", -1);
  316. String pageNumberString = attributes.getValue("formatted-nr");
  317. String pageMaster = attributes.getValue("simple-page-master-name");
  318. boolean blank = getAttributeAsBoolean(attributes, "blank", false);
  319. currentPageViewport = new PageViewport(viewArea,
  320. pageNumber, pageNumberString,
  321. pageMaster, blank);
  322. }
  323. }
  324. private class PageMaker extends AbstractMaker {
  325. public void startElement(Attributes attributes) {
  326. Page p = new Page();
  327. currentPageViewport.setPage(p);
  328. }
  329. public void endElement() {
  330. treeModel.addPage(currentPageViewport);
  331. currentPageViewport = null;
  332. }
  333. }
  334. private class RegionViewportMaker extends AbstractMaker {
  335. public void startElement(Attributes attributes) {
  336. RegionViewport rv = getCurrentRegionViewport();
  337. if (rv != null) {
  338. throw new IllegalStateException("Current RegionViewport must be null");
  339. }
  340. Rectangle2D viewArea = parseRect(attributes.getValue("rect"));
  341. rv = new RegionViewport(viewArea);
  342. rv.setClip(getAttributeAsBoolean(attributes, "clipped", false));
  343. setAreaAttributes(attributes, rv);
  344. setTraits(attributes, rv, SUBSET_COMMON);
  345. setTraits(attributes, rv, SUBSET_BOX);
  346. setTraits(attributes, rv, SUBSET_COLOR);
  347. areaStack.push(rv);
  348. }
  349. public void endElement() {
  350. assertObjectOfClass(areaStack.pop(), RegionViewport.class);
  351. }
  352. }
  353. private class RegionBeforeMaker extends AbstractMaker {
  354. public void startElement(Attributes attributes) {
  355. pushNewRegionReference(attributes, Constants.FO_REGION_BEFORE);
  356. }
  357. public void endElement() {
  358. assertObjectOfClass(areaStack.pop(), RegionReference.class);
  359. }
  360. }
  361. private class RegionAfterMaker extends AbstractMaker {
  362. public void startElement(Attributes attributes) {
  363. pushNewRegionReference(attributes, Constants.FO_REGION_AFTER);
  364. }
  365. public void endElement() {
  366. assertObjectOfClass(areaStack.pop(), RegionReference.class);
  367. }
  368. }
  369. private class RegionStartMaker extends AbstractMaker {
  370. public void startElement(Attributes attributes) {
  371. pushNewRegionReference(attributes, Constants.FO_REGION_START);
  372. }
  373. public void endElement() {
  374. assertObjectOfClass(areaStack.pop(), RegionReference.class);
  375. }
  376. }
  377. private class RegionEndMaker extends AbstractMaker {
  378. public void startElement(Attributes attributes) {
  379. pushNewRegionReference(attributes, Constants.FO_REGION_END);
  380. }
  381. public void endElement() {
  382. assertObjectOfClass(areaStack.pop(), RegionReference.class);
  383. }
  384. }
  385. private class RegionBodyMaker extends AbstractMaker {
  386. public void startElement(Attributes attributes) {
  387. BodyRegion body = getCurrentBodyRegion();
  388. if (body != null) {
  389. throw new IllegalStateException("Current BodyRegion must be null");
  390. }
  391. String regionName = attributes.getValue("name");
  392. int columnCount = getAttributeAsInteger(attributes, "columnCount", 1);
  393. int columnGap = getAttributeAsInteger(attributes, "columnGap", 0);
  394. RegionViewport rv = getCurrentRegionViewport();
  395. body = new BodyRegion(Constants.FO_REGION_BODY,
  396. regionName, rv, columnCount, columnGap);
  397. body.setCTM(getAttributeAsCTM(attributes, "ctm"));
  398. setAreaAttributes(attributes, body);
  399. rv.setRegionReference(body);
  400. currentPageViewport.getPage().setRegionViewport(
  401. Constants.FO_REGION_BODY, rv);
  402. areaStack.push(body);
  403. }
  404. public void endElement() {
  405. assertObjectOfClass(areaStack.pop(), BodyRegion.class);
  406. }
  407. }
  408. private class FlowMaker extends AbstractMaker {
  409. public void startElement(Attributes attributes) {
  410. BodyRegion body = getCurrentBodyRegion();
  411. if (!firstFlow) {
  412. body.getMainReference().getCurrentSpan().moveToNextFlow();
  413. } else {
  414. firstFlow = false;
  415. }
  416. NormalFlow flow = body.getMainReference().getCurrentSpan().getCurrentFlow();
  417. setAreaAttributes(attributes, flow);
  418. areaStack.push(flow);
  419. }
  420. public void endElement() {
  421. assertObjectOfClass(areaStack.pop(), NormalFlow.class);
  422. }
  423. }
  424. private class MainReferenceMaker extends AbstractMaker {
  425. public void startElement(Attributes attributes) {
  426. //mainReference is created by the BodyRegion
  427. setAreaAttributes(attributes, getCurrentBodyRegion().getMainReference());
  428. }
  429. }
  430. private class SpanMaker extends AbstractMaker {
  431. public void startElement(Attributes attributes) {
  432. int ipd = getAttributeAsInteger(attributes, "ipd", 0);
  433. int columnCount = getAttributeAsInteger(attributes, "columnCount", 1);
  434. BodyRegion body = getCurrentBodyRegion();
  435. Span span = new Span(columnCount,
  436. body.getColumnGap(), ipd);
  437. setAreaAttributes(attributes, span);
  438. body.getMainReference().getSpans().add(span);
  439. firstFlow = true;
  440. }
  441. }
  442. private class FootnoteMaker extends AbstractMaker {
  443. public void startElement(Attributes attributes) {
  444. areaStack.push(getCurrentBodyRegion().getFootnote());
  445. }
  446. public void endElement() {
  447. assertObjectOfClass(areaStack.pop(), Footnote.class);
  448. }
  449. }
  450. private class BeforeFloatMaker extends AbstractMaker {
  451. public void startElement(Attributes attributes) {
  452. areaStack.push(getCurrentBodyRegion().getBeforeFloat());
  453. }
  454. public void endElement() {
  455. assertObjectOfClass(areaStack.pop(), BeforeFloat.class);
  456. }
  457. }
  458. private class BlockMaker extends AbstractMaker {
  459. public void startElement(Attributes attributes) {
  460. boolean isViewport = getAttributeAsBoolean(attributes,
  461. "is-viewport-area", false);
  462. Block block;
  463. if (isViewport) {
  464. BlockViewport bv = new BlockViewport();
  465. bv.setClip(getAttributeAsBoolean(attributes, "clipped", false));
  466. bv.setCTM(getAttributeAsCTM(attributes, "ctm"));
  467. if (bv.getPositioning() != BlockViewport.RELATIVE) {
  468. bv.setXOffset(
  469. getAttributeAsInteger(attributes, "left-position", 0));
  470. bv.setYOffset(
  471. getAttributeAsInteger(attributes, "top-position", 0));
  472. }
  473. block = bv;
  474. } else {
  475. block = new Block();
  476. }
  477. String positioning = attributes.getValue("positioning");
  478. if ("absolute".equalsIgnoreCase(positioning)) {
  479. block.setPositioning(Block.ABSOLUTE);
  480. } else if ("fixed".equalsIgnoreCase(positioning)) {
  481. block.setPositioning(Block.FIXED);
  482. } else if ("relative".equalsIgnoreCase(positioning)) {
  483. block.setPositioning(Block.RELATIVE);
  484. } else {
  485. block.setPositioning(Block.STACK);
  486. }
  487. if (attributes.getValue("left-offset") != null) {
  488. block.setXOffset(getAttributeAsInteger(attributes, "left-offset", 0));
  489. }
  490. if (attributes.getValue("top-offset") != null) {
  491. block.setYOffset(getAttributeAsInteger(attributes, "top-offset", 0));
  492. }
  493. setAreaAttributes(attributes, block);
  494. setTraits(attributes, block, SUBSET_COMMON);
  495. setTraits(attributes, block, SUBSET_BOX);
  496. setTraits(attributes, block, SUBSET_COLOR);
  497. Area parent = (Area)areaStack.peek();
  498. //BlockParent parent = getCurrentBlockParent();
  499. parent.addChildArea(block);
  500. areaStack.push(block);
  501. }
  502. public void endElement() {
  503. assertObjectOfClass(areaStack.pop(), Block.class);
  504. }
  505. }
  506. private class LineAreaMaker extends AbstractMaker {
  507. public void startElement(Attributes attributes) {
  508. LineArea line = new LineArea();
  509. setAreaAttributes(attributes, line);
  510. setTraits(attributes, line, SUBSET_COMMON);
  511. setTraits(attributes, line, SUBSET_BOX);
  512. setTraits(attributes, line, SUBSET_COLOR);
  513. BlockParent parent = getCurrentBlockParent();
  514. parent.addChildArea(line);
  515. areaStack.push(line);
  516. }
  517. public void endElement() {
  518. assertObjectOfClass(areaStack.pop(), LineArea.class);
  519. }
  520. }
  521. private class InlineParentMaker extends AbstractMaker {
  522. public void startElement(Attributes attributes) {
  523. InlineParent ip = new InlineParent();
  524. ip.setOffset(getAttributeAsInteger(attributes, "offset", 0));
  525. setAreaAttributes(attributes, ip);
  526. setTraits(attributes, ip, SUBSET_COMMON);
  527. setTraits(attributes, ip, SUBSET_BOX);
  528. setTraits(attributes, ip, SUBSET_COLOR);
  529. setTraits(attributes, ip, SUBSET_LINK);
  530. Area parent = (Area)areaStack.peek();
  531. parent.addChildArea(ip);
  532. areaStack.push(ip);
  533. }
  534. public void endElement() {
  535. assertObjectOfClass(areaStack.pop(), InlineParent.class);
  536. }
  537. }
  538. private class InlineBlockParentMaker extends AbstractMaker {
  539. public void startElement(Attributes attributes) {
  540. InlineBlockParent ibp = new InlineBlockParent();
  541. ibp.setOffset(getAttributeAsInteger(attributes, "offset", 0));
  542. setAreaAttributes(attributes, ibp);
  543. setTraits(attributes, ibp, SUBSET_COMMON);
  544. setTraits(attributes, ibp, SUBSET_BOX);
  545. setTraits(attributes, ibp, SUBSET_COLOR);
  546. Area parent = (Area)areaStack.peek();
  547. parent.addChildArea(ibp);
  548. areaStack.push(ibp);
  549. }
  550. public void endElement() {
  551. assertObjectOfClass(areaStack.pop(), InlineBlockParent.class);
  552. }
  553. }
  554. private class TextMaker extends AbstractMaker {
  555. public void startElement(Attributes attributes) {
  556. if (getCurrentText() != null) {
  557. throw new IllegalStateException("Current Text must be null");
  558. }
  559. TextArea text = new TextArea();
  560. setAreaAttributes(attributes, text);
  561. setTraits(attributes, text, SUBSET_COMMON);
  562. setTraits(attributes, text, SUBSET_BOX);
  563. setTraits(attributes, text, SUBSET_COLOR);
  564. setTraits(attributes, text, SUBSET_FONT);
  565. text.setBaselineOffset(getAttributeAsInteger(attributes, "baseline", 0));
  566. text.setOffset(getAttributeAsInteger(attributes, "offset", 0));
  567. text.setTextLetterSpaceAdjust(getAttributeAsInteger(attributes,
  568. "tlsadjust", 0));
  569. text.setTextWordSpaceAdjust(getAttributeAsInteger(attributes,
  570. "twsadjust", 0));
  571. Area parent = (Area)areaStack.peek();
  572. parent.addChildArea(text);
  573. areaStack.push(text);
  574. }
  575. public void endElement() {
  576. assertObjectOfClass(areaStack.pop(), TextArea.class);
  577. }
  578. }
  579. private class WordMaker extends AbstractMaker {
  580. private int[] toIntArray(String s) {
  581. if (s == null || s.length() == 0) {
  582. return null;
  583. }
  584. StringTokenizer tokenizer = new StringTokenizer(s, " ");
  585. List values = new java.util.ArrayList();
  586. while (tokenizer.hasMoreTokens()) {
  587. values.add(new Integer(tokenizer.nextToken()));
  588. }
  589. int[] res = new int[values.size()];
  590. for (int i = 0, c = res.length; i < c; i++) {
  591. res[i] = ((Integer)values.get(i)).intValue();
  592. }
  593. return res;
  594. }
  595. public void endElement() {
  596. int offset = getAttributeAsInteger(lastAttributes, "offset", 0);
  597. int[] letterAdjust = toIntArray(lastAttributes.getValue("letter-adjust"));
  598. String txt = content.toString();
  599. WordArea word = new WordArea(txt, offset, letterAdjust);
  600. AbstractTextArea text = getCurrentText();
  601. word.setParentArea(text);
  602. text.addChildArea(word);
  603. }
  604. }
  605. private class SpaceMaker extends AbstractMaker {
  606. public void endElement() {
  607. int offset = getAttributeAsInteger(lastAttributes, "offset", 0);
  608. String txt = content.toString();
  609. //TODO the isAdjustable parameter is currently not used/implemented
  610. if (txt.length() > 0) {
  611. boolean adjustable = getAttributeAsBoolean(lastAttributes, "adj", true);
  612. SpaceArea space = new SpaceArea(txt.charAt(0), offset, adjustable);
  613. AbstractTextArea text = getCurrentText();
  614. space.setParentArea(text);
  615. text.addChildArea(space);
  616. } else {
  617. Space space = new Space();
  618. setAreaAttributes(lastAttributes, space);
  619. setTraits(lastAttributes, space, SUBSET_COMMON);
  620. setTraits(lastAttributes, space, SUBSET_BOX);
  621. setTraits(lastAttributes, space, SUBSET_COLOR);
  622. space.setOffset(offset);
  623. Area parent = (Area)areaStack.peek();
  624. parent.addChildArea(space);
  625. }
  626. }
  627. }
  628. private class CharMaker extends AbstractMaker {
  629. public void endElement() {
  630. String txt = content.toString();
  631. Character ch = new Character(txt.charAt(0));
  632. setAreaAttributes(lastAttributes, ch);
  633. setTraits(lastAttributes, ch, SUBSET_COMMON);
  634. setTraits(lastAttributes, ch, SUBSET_BOX);
  635. setTraits(lastAttributes, ch, SUBSET_COLOR);
  636. setTraits(lastAttributes, ch, SUBSET_FONT);
  637. ch.setOffset(getAttributeAsInteger(lastAttributes, "offset", 0));
  638. ch.setBaselineOffset(getAttributeAsInteger(lastAttributes, "baseline", 0));
  639. Area parent = (Area)areaStack.peek();
  640. parent.addChildArea(ch);
  641. }
  642. }
  643. private class LeaderMaker extends AbstractMaker {
  644. public void startElement(Attributes attributes) {
  645. Leader leader = new Leader();
  646. setAreaAttributes(attributes, leader);
  647. setTraits(attributes, leader, SUBSET_COMMON);
  648. setTraits(attributes, leader, SUBSET_BOX);
  649. setTraits(attributes, leader, SUBSET_COLOR);
  650. setTraits(attributes, leader, SUBSET_FONT);
  651. leader.setOffset(getAttributeAsInteger(attributes, "offset", 0));
  652. String ruleStyle = attributes.getValue("ruleStyle");
  653. if (ruleStyle != null) {
  654. leader.setRuleStyle(ruleStyle);
  655. }
  656. leader.setRuleThickness(
  657. getAttributeAsInteger(attributes, "ruleThickness", 0));
  658. Area parent = (Area)areaStack.peek();
  659. parent.addChildArea(leader);
  660. }
  661. public void endElement() {
  662. }
  663. }
  664. private class ViewportMaker extends AbstractMaker {
  665. public void startElement(Attributes attributes) {
  666. Viewport viewport = new Viewport(null);
  667. setAreaAttributes(attributes, viewport);
  668. setTraits(attributes, viewport, SUBSET_COMMON);
  669. setTraits(attributes, viewport, SUBSET_BOX);
  670. setTraits(attributes, viewport, SUBSET_COLOR);
  671. viewport.setContentPosition(getAttributeAsRectangle2D(attributes, "pos"));
  672. viewport.setClip(getAttributeAsBoolean(attributes, "clip", false));
  673. viewport.setOffset(getAttributeAsInteger(attributes, "offset", 0));
  674. Area parent = (Area)areaStack.peek();
  675. parent.addChildArea(viewport);
  676. areaStack.push(viewport);
  677. }
  678. public void endElement() {
  679. assertObjectOfClass(areaStack.pop(), Viewport.class);
  680. }
  681. }
  682. private class ImageMaker extends AbstractMaker {
  683. public void startElement(Attributes attributes) {
  684. String url = attributes.getValue("url");
  685. Image image = new Image(url);
  686. setAreaAttributes(attributes, image);
  687. setTraits(attributes, image, SUBSET_COMMON);
  688. getCurrentViewport().setContent(image);
  689. }
  690. }
  691. private class ForeignObjectMaker extends AbstractMaker {
  692. public void startElement(Attributes attributes) throws SAXException {
  693. String ns = attributes.getValue("ns");
  694. domImplementation
  695. = elementMappingRegistry.getDOMImplementationForNamespace(ns);
  696. if (domImplementation == null) {
  697. throw new SAXException("No DOMImplementation could be"
  698. + " identified to handle namespace: " + ns);
  699. }
  700. ForeignObject foreign = new ForeignObject(ns);
  701. setAreaAttributes(attributes, foreign);
  702. setTraits(attributes, foreign, SUBSET_COMMON);
  703. getCurrentViewport().setContent(foreign);
  704. areaStack.push(foreign);
  705. }
  706. public void endElement() {
  707. assertObjectOfClass(areaStack.pop(), ForeignObject.class);
  708. }
  709. }
  710. /*
  711. private class ?Maker extends AbstractMaker {
  712. public void startElement(Attributes attributes) {
  713. }
  714. public void endElement() {
  715. }
  716. }
  717. */
  718. // ====================================================================
  719. private void pushNewRegionReference(Attributes attributes, int side) {
  720. String regionName = attributes.getValue("name");
  721. RegionViewport rv = getCurrentRegionViewport();
  722. RegionReference reg = new RegionReference(side,
  723. regionName, rv);
  724. reg.setCTM(getAttributeAsCTM(attributes, "ctm"));
  725. setAreaAttributes(attributes, reg);
  726. rv.setRegionReference(reg);
  727. currentPageViewport.getPage().setRegionViewport(
  728. side, rv);
  729. areaStack.push(reg);
  730. }
  731. private void assertObjectOfClass(Object obj, Class clazz) {
  732. if (!clazz.isInstance(obj)) {
  733. throw new IllegalStateException("Object is not an instance of "
  734. + clazz.getName() + " but of " + obj.getClass().getName());
  735. }
  736. }
  737. /**
  738. * Handles objects created by "sub-parsers" that implement the ObjectSource interface.
  739. * An example of object handled here are ExtensionAttachments.
  740. * @param obj the Object to be handled.
  741. */
  742. protected void handleExternallyGeneratedObject(Object obj) {
  743. if (areaStack.size() == 0 && obj instanceof ExtensionAttachment) {
  744. ExtensionAttachment attachment = (ExtensionAttachment)obj;
  745. if (this.currentPageViewport == null) {
  746. this.treeModel.handleOffDocumentItem(
  747. new OffDocumentExtensionAttachment(attachment));
  748. } else {
  749. this.currentPageViewport.addExtensionAttachment(attachment);
  750. }
  751. } else {
  752. log.warn("Don't know how to handle externally generated object: " + obj);
  753. }
  754. }
  755. private void setAreaAttributes(Attributes attributes, Area area) {
  756. area.setIPD(Integer.parseInt(attributes.getValue("ipd")));
  757. area.setBPD(Integer.parseInt(attributes.getValue("bpd")));
  758. }
  759. private static final Object[] SUBSET_COMMON = new Object[] {
  760. Trait.PROD_ID};
  761. private static final Object[] SUBSET_LINK = new Object[] {
  762. Trait.INTERNAL_LINK, Trait.EXTERNAL_LINK};
  763. private static final Object[] SUBSET_COLOR = new Object[] {
  764. Trait.BACKGROUND, Trait.COLOR};
  765. private static final Object[] SUBSET_FONT = new Object[] {
  766. Trait.FONT, Trait.FONT_SIZE, Trait.BLINK,
  767. Trait.OVERLINE, Trait.OVERLINE_COLOR,
  768. Trait.LINETHROUGH, Trait.LINETHROUGH_COLOR,
  769. Trait.UNDERLINE, Trait.UNDERLINE_COLOR};
  770. private static final Object[] SUBSET_BOX = new Object[] {
  771. Trait.BORDER_BEFORE, Trait.BORDER_AFTER, Trait.BORDER_START, Trait.BORDER_END,
  772. Trait.SPACE_BEFORE, Trait.SPACE_AFTER, Trait.SPACE_START, Trait.SPACE_END,
  773. Trait.PADDING_BEFORE, Trait.PADDING_AFTER, Trait.PADDING_START, Trait.PADDING_END,
  774. Trait.START_INDENT, Trait.END_INDENT,
  775. Trait.IS_REFERENCE_AREA, Trait.IS_VIEWPORT_AREA};
  776. private void setTraits(Attributes attributes, Area area, Object[] traitSubset) {
  777. for (int i = 0, c = traitSubset.length; i < c; i++) {
  778. Object trait = traitSubset[i];
  779. String traitName = Trait.getTraitName(trait);
  780. String value = attributes.getValue(traitName);
  781. if (value != null) {
  782. Class cl = Trait.getTraitClass(trait);
  783. if (cl == Integer.class) {
  784. //if (value != null) {
  785. area.addTrait(trait, new Integer(value));
  786. //}
  787. } else if (cl == Boolean.class) {
  788. //String value = attributes.getValue(Trait.getTraitName(trait));
  789. //if (value != null) {
  790. area.addTrait(trait, Boolean.valueOf(value));
  791. //}
  792. } else if (cl == String.class) {
  793. //String value = attributes.getValue(Trait.getTraitName(trait));
  794. //if (value != null) {
  795. area.addTrait(trait, value);
  796. //}
  797. } else if (cl == Color.class) {
  798. //String value = attributes.getValue(Trait.getTraitName(trait));
  799. //if (value != null) {
  800. area.addTrait(trait, Color.valueOf(value));
  801. //}
  802. } else if (cl == Background.class) {
  803. //String value = attributes.getValue(Trait.getTraitName(trait));
  804. //if (value != null) {
  805. Background bkg = new Background();
  806. Color col = Color.valueOf(attributes.getValue("bkg-color"));
  807. if (col != null) {
  808. bkg.setColor(col);
  809. }
  810. String url = attributes.getValue("bkg-img");
  811. if (url != null) {
  812. bkg.setURL(url);
  813. ImageFactory fact = userAgent.getFactory().getImageFactory();
  814. FopImage img = fact.getImage(url, userAgent);
  815. if (img == null) {
  816. log.error("Background image not available: " + url);
  817. } else {
  818. // load dimensions
  819. if (!img.load(FopImage.DIMENSIONS)) {
  820. log.error("Cannot read background image dimensions: "
  821. + url);
  822. }
  823. }
  824. bkg.setFopImage(img);
  825. String repeat = attributes.getValue("bkg-repeat");
  826. if (repeat != null) {
  827. bkg.setRepeat(repeat);
  828. }
  829. bkg.setHoriz(getAttributeAsInteger(attributes,
  830. "bkg-horz-offset", 0));
  831. bkg.setVertical(getAttributeAsInteger(attributes,
  832. "bkg-vert-offset", 0));
  833. }
  834. area.addTrait(trait, bkg);
  835. //}
  836. } else if (cl == BorderProps.class) {
  837. //String value = attributes.getValue(Trait.getTraitName(trait));
  838. //if (value != null) {
  839. area.addTrait(trait, BorderProps.valueOf(value));
  840. //}
  841. }
  842. } else {
  843. //Class cl = Trait.getTraitClass(trait);
  844. if (trait == Trait.FONT) {
  845. String fontName = attributes.getValue("font-name");
  846. if (fontName != null) {
  847. String fontStyle = attributes.getValue("font-style");
  848. int fontWeight = getAttributeAsInteger(
  849. attributes, "font-weight", Font.NORMAL);
  850. area.addTrait(trait,
  851. FontInfo.createFontKey(fontName, fontStyle, fontWeight));
  852. }
  853. }
  854. }
  855. }
  856. }
  857. private boolean getAttributeAsBoolean(Attributes attributes, String name,
  858. boolean defaultValue) {
  859. String s = attributes.getValue(name);
  860. if (s == null) {
  861. return defaultValue;
  862. } else {
  863. return Boolean.valueOf(s).booleanValue();
  864. }
  865. }
  866. private int getAttributeAsInteger(Attributes attributes, String name,
  867. int defaultValue) {
  868. String s = attributes.getValue(name);
  869. if (s == null) {
  870. return defaultValue;
  871. } else {
  872. return Integer.parseInt(s);
  873. }
  874. }
  875. private CTM getAttributeAsCTM(Attributes attributes, String name) {
  876. String s = attributes.getValue(name).trim();
  877. if (s.startsWith("[") && s.endsWith("]")) {
  878. s = s.substring(1, s.length() - 1);
  879. StringTokenizer tokenizer = new StringTokenizer(s, " ");
  880. double[] values = new double[] {
  881. Double.parseDouble(tokenizer.nextToken()),
  882. Double.parseDouble(tokenizer.nextToken()),
  883. Double.parseDouble(tokenizer.nextToken()),
  884. Double.parseDouble(tokenizer.nextToken()),
  885. Double.parseDouble(tokenizer.nextToken()),
  886. Double.parseDouble(tokenizer.nextToken())};
  887. return new CTM(values[0], values[1], values[2], values[3], values[4], values[5]);
  888. } else {
  889. throw new IllegalArgumentException("CTM must be surrounded by square brackets");
  890. }
  891. }
  892. private Rectangle2D getAttributeAsRectangle2D(Attributes attributes, String name) {
  893. String s = attributes.getValue(name).trim();
  894. StringTokenizer tokenizer = new StringTokenizer(s, " ");
  895. double[] values = new double[] {
  896. Double.parseDouble(tokenizer.nextToken()),
  897. Double.parseDouble(tokenizer.nextToken()),
  898. Double.parseDouble(tokenizer.nextToken()),
  899. Double.parseDouble(tokenizer.nextToken())};
  900. return new Rectangle2D.Double(values[0], values[1], values[2], values[3]);
  901. }
  902. /** @see org.xml.sax.ContentHandler#characters(char[], int, int) */
  903. public void characters(char[] ch, int start, int length) throws SAXException {
  904. if (delegate != null) {
  905. delegate.characters(ch, start, length);
  906. } else {
  907. content.append(ch, start, length);
  908. }
  909. }
  910. }
  911. }