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.

AreaTreeParser.java 53KB

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