Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

PropertyList.java 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  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.fo;
  19. // Java
  20. import org.xml.sax.Attributes;
  21. import org.apache.commons.logging.Log;
  22. import org.apache.commons.logging.LogFactory;
  23. import org.apache.xmlgraphics.util.QName;
  24. import org.apache.fop.apps.FopFactory;
  25. import org.apache.fop.fo.expr.PropertyException;
  26. import org.apache.fop.fo.properties.CommonAbsolutePosition;
  27. import org.apache.fop.fo.properties.CommonAccessibility;
  28. import org.apache.fop.fo.properties.CommonAural;
  29. import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
  30. import org.apache.fop.fo.properties.CommonFont;
  31. import org.apache.fop.fo.properties.CommonHyphenation;
  32. import org.apache.fop.fo.properties.CommonMarginBlock;
  33. import org.apache.fop.fo.properties.CommonMarginInline;
  34. import org.apache.fop.fo.properties.CommonRelativePosition;
  35. import org.apache.fop.fo.properties.CommonTextDecoration;
  36. import org.apache.fop.fo.properties.Property;
  37. import org.apache.fop.fo.properties.PropertyMaker;
  38. /**
  39. * Class containing the collection of properties for a given FObj.
  40. */
  41. public abstract class PropertyList {
  42. // writing-mode index
  43. private int writingMode;
  44. private static boolean[] inheritableProperty;
  45. /** reference to the parent FO's propertyList **/
  46. protected PropertyList parentPropertyList = null;
  47. private FObj fobj = null;
  48. private static Log log = LogFactory.getLog(PropertyList.class);
  49. /**
  50. * Basic constructor.
  51. * @param fObjToAttach the FO this PropertyList should be attached to
  52. * @param parentPropertyList the PropertyList belonging to the new objects
  53. * parent
  54. */
  55. public PropertyList(FObj fObjToAttach, PropertyList parentPropertyList) {
  56. this.fobj = fObjToAttach;
  57. this.parentPropertyList = parentPropertyList;
  58. }
  59. /**
  60. * @return the FObj object to which this propertyList is attached
  61. */
  62. public FObj getFObj() {
  63. return this.fobj;
  64. }
  65. /**
  66. * @return the FObj object attached to the parentPropertyList
  67. */
  68. public FObj getParentFObj() {
  69. if (parentPropertyList != null) {
  70. return parentPropertyList.getFObj();
  71. } else {
  72. return null;
  73. }
  74. }
  75. /**
  76. * @return the FObj object attached to the parentPropetyList
  77. */
  78. public PropertyList getParentPropertyList() {
  79. return parentPropertyList;
  80. }
  81. /**
  82. * Return the value explicitly specified on this FO.
  83. * @param propId The id of the property whose value is desired.
  84. * @return The value if the property is explicitly set or set by
  85. * a shorthand property, otherwise null.
  86. * @throws PropertyException ...
  87. */
  88. public Property getExplicitOrShorthand(int propId) throws PropertyException {
  89. /* Handle request for one part of a compound property */
  90. Property p = getExplicit(propId);
  91. if (p == null) {
  92. p = getShorthand(propId);
  93. }
  94. return p;
  95. }
  96. /**
  97. * Return the value explicitly specified on this FO.
  98. * @param propId The ID of the property whose value is desired.
  99. * @return The value if the property is explicitly set, otherwise null.
  100. */
  101. public abstract Property getExplicit(int propId);
  102. /**
  103. * Set an value defined explicitly on this FO.
  104. * @param propId The ID of the property to set.
  105. * @param value The value of the property.
  106. */
  107. public abstract void putExplicit(int propId, Property value);
  108. /**
  109. * Return the value of this property inherited by this FO.
  110. * Implements the inherited-property-value function.
  111. * The property must be inheritable!
  112. * @param propId The ID of the property whose value is desired.
  113. * @return The inherited value, otherwise null.
  114. * @throws PropertyException ...
  115. */
  116. public Property getInherited(int propId) throws PropertyException {
  117. if (isInherited(propId)) {
  118. return getFromParent(propId);
  119. } else {
  120. // return the "initial" value
  121. return makeProperty(propId);
  122. }
  123. }
  124. /**
  125. * Return the property on the current FlowObject. If it isn't set explicitly,
  126. * this will try to compute it based on other properties, or if it is
  127. * inheritable, to return the inherited value. If all else fails, it returns
  128. * the default value.
  129. * @param propId The Constants ID of the property whose value is desired.
  130. * @return the Property corresponding to that name
  131. * @throws PropertyException if there is a problem evaluating the property
  132. */
  133. public Property get(int propId) throws PropertyException {
  134. return get(propId, true, true);
  135. }
  136. /**
  137. * Return the property on the current FlowObject. Depending on the passed flags,
  138. * this will try to compute it based on other properties, or if it is
  139. * inheritable, to return the inherited value. If all else fails, it returns
  140. * the default value.
  141. * @param propId the property's id
  142. * @param bTryInherit true for inherited properties, or when the inherited
  143. * value is needed
  144. * @param bTryDefault true when the default value may be used as a last resort
  145. * @return the property
  146. * @throws PropertyException if there is a problem evaluating the property
  147. */
  148. public Property get(int propId, boolean bTryInherit,
  149. boolean bTryDefault) throws PropertyException {
  150. PropertyMaker propertyMaker = findMaker(propId & Constants.PROPERTY_MASK);
  151. if (propertyMaker != null) {
  152. return propertyMaker.get(propId & Constants.COMPOUND_MASK, this,
  153. bTryInherit, bTryDefault);
  154. }
  155. return null;
  156. }
  157. /**
  158. * Return the "nearest" specified value for the given property.
  159. * Implements the from-nearest-specified-value function.
  160. * @param propId The ID of the property whose value is desired.
  161. * @return The computed value if the property is explicitly set on some
  162. * ancestor of the current FO, else the initial value.
  163. * @throws PropertyException if there an error occurred when getting the property
  164. */
  165. public Property getNearestSpecified(int propId) throws PropertyException {
  166. Property p = null;
  167. PropertyList pList = parentPropertyList;
  168. while (pList != null) {
  169. p = pList.getExplicit(propId);
  170. if (p != null) {
  171. return p;
  172. } else {
  173. pList = pList.parentPropertyList;
  174. }
  175. }
  176. // If no explicit value found on any of the ancestor-nodes,
  177. // return initial (default) value.
  178. return makeProperty(propId);
  179. }
  180. /**
  181. * Return the value of this property on the parent of this FO.
  182. * Implements the from-parent function.
  183. * @param propId The Constants ID of the property whose value is desired.
  184. * @return The computed value on the parent or the initial value if this
  185. * FO is the root or is in a different namespace from its parent.
  186. * @throws PropertyException ...
  187. */
  188. public Property getFromParent(int propId) throws PropertyException {
  189. if (parentPropertyList != null) {
  190. return parentPropertyList.get(propId);
  191. } else {
  192. return makeProperty(propId);
  193. }
  194. }
  195. /**
  196. * Set writing mode for this FO.
  197. * Use that from the nearest ancestor, including self, which generates
  198. * reference areas, or from root FO if no ancestor found.
  199. * @throws PropertyException ...
  200. */
  201. public void setWritingMode() throws PropertyException {
  202. FObj p = fobj.findNearestAncestorFObj();
  203. // If this is a reference area or the root, use the property value.
  204. if (fobj.generatesReferenceAreas() || p == null) {
  205. writingMode = get(Constants.PR_WRITING_MODE).getEnum();
  206. } else {
  207. // Otherwise get the writing mode value from the parent.
  208. writingMode = getParentPropertyList().getWritingMode();
  209. }
  210. }
  211. /**
  212. * Return the "writing-mode" property value.
  213. * @return the "writing-mode" property value.
  214. */
  215. public int getWritingMode() {
  216. return writingMode;
  217. }
  218. /**
  219. * Uses the stored writingMode.
  220. * @param lrtb the property ID to return under lrtb writingmode.
  221. * @param rltb the property ID to return under rltb writingmode.
  222. * @param tbrl the property ID to return under tbrl writingmode.
  223. * @return one of the property IDs, depending on the writing mode.
  224. */
  225. public int getWritingMode(int lrtb, int rltb, int tbrl) {
  226. switch (writingMode) {
  227. case Constants.EN_LR_TB: return lrtb;
  228. case Constants.EN_RL_TB: return rltb;
  229. case Constants.EN_TB_RL: return tbrl;
  230. default:
  231. //nop
  232. }
  233. return -1;
  234. }
  235. /**
  236. * Adds the attributes, passed in by the parser to the PropertyList
  237. *
  238. * @param attributes Collection of attributes passed to us from the parser.
  239. * @throws ValidationException if there is an attribute that does not
  240. * map to a property id (strict validation only)
  241. */
  242. public void addAttributesToList(Attributes attributes)
  243. throws ValidationException {
  244. /*
  245. * If column-number/number-columns-spanned are specified, then we
  246. * need them before all others (possible from-table-column() on any
  247. * other property further in the list...
  248. */
  249. String attributeName = "column-number";
  250. String attributeValue = attributes.getValue(attributeName);
  251. convertAttributeToProperty(attributes, attributeName,
  252. attributeValue);
  253. attributeName = "number-columns-spanned";
  254. attributeValue = attributes.getValue(attributeName);
  255. convertAttributeToProperty(attributes, attributeName,
  256. attributeValue);
  257. /*
  258. * If font-size is set on this FO, must set it first, since
  259. * other attributes specified in terms of "ems" depend on it.
  260. */
  261. attributeName = "font";
  262. attributeValue = attributes.getValue(attributeName);
  263. convertAttributeToProperty(attributes, attributeName,
  264. attributeValue);
  265. if (attributeValue == null) {
  266. /*
  267. * font shorthand wasn't specified, so still need to process
  268. * explicit font-size
  269. */
  270. attributeName = "font-size";
  271. attributeValue = attributes.getValue(attributeName);
  272. convertAttributeToProperty(attributes, attributeName,
  273. attributeValue);
  274. }
  275. String attributeNS;
  276. FopFactory factory = getFObj().getUserAgent().getFactory();
  277. for (int i = 0; i < attributes.getLength(); i++) {
  278. /* convert all attributes with the same namespace as the fo element
  279. * the "xml:lang" property is a special case */
  280. attributeNS = attributes.getURI(i);
  281. attributeName = attributes.getQName(i);
  282. attributeValue = attributes.getValue(i);
  283. if (attributeNS == null || attributeNS.length() == 0
  284. || "xml:lang".equals(attributeName)) {
  285. convertAttributeToProperty(attributes, attributeName, attributeValue);
  286. } else if (!factory.isNamespaceIgnored(attributeNS)) {
  287. ElementMapping mapping = factory.getElementMappingRegistry().getElementMapping(
  288. attributeNS);
  289. QName attr = new QName(attributeNS, attributeName);
  290. if (mapping != null) {
  291. if (mapping.isAttributeProperty(attr)
  292. && mapping.getStandardPrefix() != null) {
  293. convertAttributeToProperty(attributes,
  294. mapping.getStandardPrefix() + ":" + attr.getLocalName(),
  295. attributeValue);
  296. } else {
  297. getFObj().addForeignAttribute(attr, attributeValue);
  298. }
  299. } else {
  300. handleInvalidProperty(attr);
  301. }
  302. }
  303. }
  304. }
  305. /**
  306. * Validates a property name.
  307. * @param propertyName the property name to check
  308. * @return true if the base property name and the subproperty name (if any)
  309. * can be correctly mapped to an id
  310. */
  311. protected boolean isValidPropertyName(String propertyName) {
  312. int propId = FOPropertyMapping.getPropertyId(
  313. findBasePropertyName(propertyName));
  314. int subpropId = FOPropertyMapping.getSubPropertyId(
  315. findSubPropertyName(propertyName));
  316. if (propId == -1
  317. || (subpropId == -1
  318. && findSubPropertyName(propertyName) != null)) {
  319. return false;
  320. }
  321. return true;
  322. }
  323. /**
  324. *
  325. * @param attributes Collection of attributes
  326. * @param attributeName Attribute name to convert
  327. * @param attributeValue Attribute value to assign to property
  328. * @throws ValidationException in case the property name is invalid
  329. * for the FO namespace
  330. */
  331. private void convertAttributeToProperty(Attributes attributes,
  332. String attributeName,
  333. String attributeValue)
  334. throws ValidationException {
  335. if (attributeValue != null) {
  336. if (attributeName.startsWith("xmlns:")) {
  337. //Ignore namespace declarations
  338. return;
  339. }
  340. /* Handle "compound" properties, ex. space-before.minimum */
  341. String basePropertyName = findBasePropertyName(attributeName);
  342. String subPropertyName = findSubPropertyName(attributeName);
  343. int propId = FOPropertyMapping.getPropertyId(basePropertyName);
  344. int subpropId = FOPropertyMapping.getSubPropertyId(subPropertyName);
  345. if (propId == -1
  346. || (subpropId == -1 && subPropertyName != null)) {
  347. handleInvalidProperty(new QName(null, attributeName));
  348. }
  349. FObj parentFO = fobj.findNearestAncestorFObj();
  350. PropertyMaker propertyMaker = findMaker(propId);
  351. if (propertyMaker == null) {
  352. log.warn("No PropertyMaker registered for " + attributeName
  353. + ". Ignoring property.");
  354. return;
  355. }
  356. try {
  357. Property prop = null;
  358. if (subPropertyName == null) { // base attribute only found
  359. /* Do nothing if the base property has already been created.
  360. * This is e.g. the case when a compound attribute was
  361. * specified before the base attribute; in these cases
  362. * the base attribute was already created in
  363. * findBaseProperty()
  364. */
  365. if (getExplicit(propId) != null) {
  366. return;
  367. }
  368. prop = propertyMaker.make(this, attributeValue, parentFO);
  369. } else { // e.g. "leader-length.maximum"
  370. Property baseProperty
  371. = findBaseProperty(attributes, parentFO, propId,
  372. basePropertyName, propertyMaker);
  373. prop = propertyMaker.make(baseProperty, subpropId,
  374. this, attributeValue, parentFO);
  375. }
  376. if (prop != null) {
  377. putExplicit(propId, prop);
  378. }
  379. } catch (PropertyException e) {
  380. fobj.getFOValidationEventProducer().invalidPropertyValue(this, fobj.getName(),
  381. attributeName, attributeValue, e, fobj.locator);
  382. }
  383. }
  384. }
  385. private Property findBaseProperty(Attributes attributes,
  386. FObj parentFO,
  387. int propId,
  388. String basePropertyName,
  389. PropertyMaker propertyMaker)
  390. throws PropertyException {
  391. /* If the baseProperty has already been created, return it
  392. * e.g. <fo:leader xxxx="120pt" xxxx.maximum="200pt"... />
  393. */
  394. Property baseProperty = getExplicit(propId);
  395. if (baseProperty != null) {
  396. return baseProperty;
  397. }
  398. /* Otherwise If it is specified later in this list of Attributes, create it now
  399. * e.g. <fo:leader xxxx.maximum="200pt" xxxx="200pt"... />
  400. */
  401. String basePropertyValue = attributes.getValue(basePropertyName);
  402. if (basePropertyValue != null && propertyMaker != null) {
  403. baseProperty = propertyMaker.make(this, basePropertyValue,
  404. parentFO);
  405. return baseProperty;
  406. }
  407. return null; // could not find base property
  408. }
  409. /**
  410. * Handles an invalid property.
  411. * @param attr the invalid attribute
  412. * @throws ValidationException if an exception needs to be thrown depending on the
  413. * validation settings
  414. */
  415. protected void handleInvalidProperty(QName attr)
  416. throws ValidationException {
  417. if (!attr.getQName().startsWith("xmlns")) {
  418. fobj.getFOValidationEventProducer().invalidProperty(this, fobj.getName(),
  419. attr, true, fobj.locator);
  420. }
  421. }
  422. /**
  423. * Finds the first or base part (up to any period) of an attribute name.
  424. * For example, if input is "space-before.minimum", should return
  425. * "space-before".
  426. * @param attributeName String to be atomized
  427. * @return the base portion of the attribute
  428. */
  429. protected static String findBasePropertyName(String attributeName) {
  430. int separatorCharIndex = attributeName.indexOf('.');
  431. String basePropertyName = attributeName;
  432. if (separatorCharIndex > -1) {
  433. basePropertyName = attributeName.substring(0, separatorCharIndex);
  434. }
  435. return basePropertyName;
  436. }
  437. /**
  438. * Finds the second or sub part (portion past any period) of an attribute
  439. * name. For example, if input is "space-before.minimum", should return
  440. * "minimum".
  441. * @param attributeName String to be atomized
  442. * @return the sub portion of the attribute
  443. */
  444. protected static String findSubPropertyName(String attributeName) {
  445. int separatorCharIndex = attributeName.indexOf('.');
  446. String subpropertyName = null;
  447. if (separatorCharIndex > -1) {
  448. subpropertyName = attributeName.substring(separatorCharIndex + 1);
  449. }
  450. return subpropertyName;
  451. }
  452. /**
  453. * @param propId ID of property
  454. * @return new Property object
  455. * @throws PropertyException if there's a problem while processing the property
  456. */
  457. private Property getShorthand(int propId) throws PropertyException {
  458. PropertyMaker propertyMaker = findMaker(propId);
  459. if (propertyMaker != null) {
  460. return propertyMaker.getShorthand(this);
  461. } else {
  462. //log.error("no Maker for " + propertyName);
  463. return null;
  464. }
  465. }
  466. /**
  467. * @param propID ID of property
  468. * @return new Property object
  469. * @throws PropertyException if there's a problem while processing the property
  470. */
  471. private Property makeProperty(int propId) throws PropertyException {
  472. PropertyMaker propertyMaker = findMaker(propId);
  473. if (propertyMaker != null) {
  474. return propertyMaker.make(this);
  475. } else {
  476. //log.error("property " + propertyName
  477. // + " ignored");
  478. }
  479. return null;
  480. }
  481. /**
  482. * @param propId ID of property
  483. * @return isInherited value from the requested Property.Maker
  484. */
  485. private boolean isInherited(int propId) {
  486. if (inheritableProperty == null) {
  487. inheritableProperty = new boolean[Constants.PROPERTY_COUNT + 1];
  488. PropertyMaker maker = null;
  489. for (int prop = 1; prop <= Constants.PROPERTY_COUNT; prop++) {
  490. maker = findMaker(prop);
  491. inheritableProperty[prop] = (maker != null && maker.isInherited());
  492. }
  493. }
  494. return inheritableProperty[propId];
  495. }
  496. /**
  497. * @param propId Id of property
  498. * @return the Property.Maker for this property
  499. */
  500. private PropertyMaker findMaker(int propId) {
  501. if (propId < 1 || propId > Constants.PROPERTY_COUNT) {
  502. return null;
  503. } else {
  504. return FObj.getPropertyMakerFor(propId);
  505. }
  506. }
  507. /**
  508. * Constructs a BorderAndPadding object.
  509. * @return a BorderAndPadding object
  510. * @throws PropertyException if there's a problem while processing the properties
  511. */
  512. public CommonBorderPaddingBackground getBorderPaddingBackgroundProps()
  513. throws PropertyException {
  514. return new CommonBorderPaddingBackground(this);
  515. }
  516. /**
  517. * Constructs a CommonHyphenation object.
  518. * @return the CommonHyphenation object
  519. * @throws PropertyException if there's a problem while processing the properties
  520. */
  521. public CommonHyphenation getHyphenationProps() throws PropertyException {
  522. return CommonHyphenation.getInstance(this);
  523. }
  524. /**
  525. * Constructs a CommonMarginBlock object.
  526. * @return the CommonMarginBlock object
  527. * @throws PropertyException if there's a problem while processing the properties
  528. */
  529. public CommonMarginBlock getMarginBlockProps() throws PropertyException {
  530. return new CommonMarginBlock(this);
  531. }
  532. /**
  533. * Constructs a CommonMarginInline object.
  534. * @return the CommonMarginInline object
  535. * @throws PropertyException if there's a problem while processing the properties
  536. */
  537. public CommonMarginInline getMarginInlineProps() throws PropertyException {
  538. return new CommonMarginInline(this);
  539. }
  540. /**
  541. * Constructs a CommonAccessibility object.
  542. * @return the CommonAccessibility object
  543. * @throws PropertyException if there's a problem while processing the properties
  544. */
  545. public CommonAccessibility getAccessibilityProps() throws PropertyException {
  546. return new CommonAccessibility(this);
  547. }
  548. /**
  549. * Constructs a CommonAural object.
  550. * @return the CommonAural object
  551. * @throws PropertyException if there's a problem while processing the properties
  552. */
  553. public CommonAural getAuralProps() throws PropertyException {
  554. CommonAural props = new CommonAural(this);
  555. return props;
  556. }
  557. /**
  558. * Constructs a RelativePositionProps objects.
  559. * @return a RelativePositionProps object
  560. * @throws PropertyException if there's a problem while processing the properties
  561. */
  562. public CommonRelativePosition getRelativePositionProps() throws PropertyException {
  563. return new CommonRelativePosition(this);
  564. }
  565. /**
  566. * Constructs a CommonAbsolutePosition object.
  567. * @return the CommonAbsolutePosition object
  568. * @throws PropertyException if there's a problem while processing the properties
  569. */
  570. public CommonAbsolutePosition getAbsolutePositionProps() throws PropertyException {
  571. return new CommonAbsolutePosition(this);
  572. }
  573. /**
  574. * Constructs a CommonFont object.
  575. *
  576. * @return A CommonFont object
  577. * @throws PropertyException if there's a problem while processing the properties
  578. */
  579. public CommonFont getFontProps() throws PropertyException {
  580. return CommonFont.getInstance(this);
  581. }
  582. /**
  583. * Constructs a CommonTextDecoration object.
  584. * @return a CommonTextDecoration object
  585. * @throws PropertyException if there's a problem while processing the properties
  586. */
  587. public CommonTextDecoration getTextDecorationProps() throws PropertyException {
  588. return CommonTextDecoration.createFromPropertyList(this);
  589. }
  590. }