Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

AreaTreeParser.java 49KB

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