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 18 /* $Id: CharacterLayoutManager.java 1835810 2018-07-13 10:29:57Z ssteiner $ */ 19 20 package org.apache.fop.layoutmgr.inline; 21 22 import java.util.LinkedList; 23 import java.util.List; 24 25 import org.apache.fop.area.Trait; 26 import org.apache.fop.area.inline.InlineArea; 27 import org.apache.fop.area.inline.TextArea; 28 import org.apache.fop.fo.flow.Character; 29 import org.apache.fop.fo.properties.CommonBorderPaddingBackground; 30 import org.apache.fop.fonts.Font; 31 import org.apache.fop.fonts.FontSelector; 32 import org.apache.fop.layoutmgr.InlineKnuthSequence; 33 import org.apache.fop.layoutmgr.KnuthElement; 34 import org.apache.fop.layoutmgr.KnuthGlue; 35 import org.apache.fop.layoutmgr.KnuthPenalty; 36 import org.apache.fop.layoutmgr.KnuthSequence; 37 import org.apache.fop.layoutmgr.LayoutContext; 38 import org.apache.fop.layoutmgr.LeafPosition; 39 import org.apache.fop.layoutmgr.Position; 40 import org.apache.fop.layoutmgr.TraitSetter; 41 import org.apache.fop.traits.MinOptMax; 42 import org.apache.fop.traits.SpaceVal; 43 import org.apache.fop.util.CharUtilities; 44 45 /** 46 * LayoutManager for the fo:character formatting object 47 */ 48 public class CharacterLayoutManager extends LeafNodeLayoutManager { 49 private MinOptMax letterSpaceIPD; 50 private int hyphIPD; 51 private Font font; 52 private CommonBorderPaddingBackground borderProps; 53 54 /** 55 * Constructor 56 * 57 * @param node the fo:character formatting object 58 */ CharacterLayoutManager(Character node)59 public CharacterLayoutManager(Character node) { 60 super(node); 61 } 62 63 /** {@inheritDoc} */ 64 @Override initialize()65 public void initialize() { 66 Character fobj = (Character)this.fobj; 67 font = FontSelector.selectFontForCharacter(fobj, this); 68 SpaceVal ls = SpaceVal.makeLetterSpacing(fobj.getLetterSpacing()); 69 letterSpaceIPD = ls.getSpace(); 70 hyphIPD = fobj.getCommonHyphenation().getHyphIPD(font); 71 borderProps = fobj.getCommonBorderPaddingBackground(); 72 setCommonBorderPaddingBackground(borderProps); 73 } 74 createCharacterArea()75 private TextArea createCharacterArea() { 76 Character fobj = (Character) this.fobj; 77 TextArea text = new TextArea(); 78 text.setChangeBarList(getChangeBarList()); 79 char ch = fobj.getCharacter(); 80 int ipd = font.getCharWidth(ch); 81 int blockProgressionOffset = 0; 82 int level = fobj.getBidiLevel(); 83 if (CharUtilities.isAnySpace(ch)) { 84 // add space unless it's zero-width: 85 if (!CharUtilities.isZeroWidthSpace(ch)) { 86 text.addSpace(ch, ipd, CharUtilities.isAdjustableSpace(ch), 87 blockProgressionOffset, level); 88 } 89 } else { 90 int[] levels = (level >= 0) ? new int[] {level} : null; 91 text.addWord(String.valueOf(ch), ipd, null, levels, null, blockProgressionOffset); 92 } 93 94 TraitSetter.setProducerID(text, fobj.getId()); 95 TraitSetter.addTextDecoration(text, fobj.getTextDecoration()); 96 text.setIPD(font.getCharWidth(fobj.getCharacter())); 97 text.setBPD(font.getAscender() - font.getDescender()); 98 text.setBaselineOffset(font.getAscender()); 99 TraitSetter.addFontTraits(text, font); 100 text.addTrait(Trait.COLOR, fobj.getColor()); 101 return text; 102 } 103 104 @Override getEffectiveArea(LayoutContext layoutContext)105 protected InlineArea getEffectiveArea(LayoutContext layoutContext) { 106 InlineArea area = createCharacterArea(); 107 if (!layoutContext.treatAsArtifact()) { 108 TraitSetter.addStructureTreeElement(area, ((Character) fobj).getStructureTreeElement()); 109 } 110 return area; 111 } 112 113 /** {@inheritDoc} */ getNextKnuthElements(LayoutContext context, int alignment)114 public List getNextKnuthElements(LayoutContext context, int alignment) { 115 Character fobj = (Character) this.fobj; 116 117 // TODO: may need some special handling for fo:character 118 alignmentContext = new AlignmentContext(font 119 , font.getFontSize() 120 , fobj.getAlignmentAdjust() 121 , fobj.getAlignmentBaseline() 122 , fobj.getBaselineShift() 123 , fobj.getDominantBaseline() 124 , context.getAlignmentContext()); 125 126 KnuthSequence seq = new InlineKnuthSequence(); 127 addKnuthElementsForBorderPaddingStart(seq); 128 129 // create the AreaInfo object to store the computed values 130 MinOptMax ipd = MinOptMax.getInstance(font.getCharWidth(fobj.getCharacter())); 131 areaInfo = new AreaInfo((short) 0, ipd, false, alignmentContext); 132 133 // node is a fo:Character 134 if (letterSpaceIPD.isStiff()) { 135 // constant letter space, only return a box 136 seq.add(new KnuthInlineBox(areaInfo.ipdArea.getOpt(), areaInfo.alignmentContext, 137 notifyPos(new LeafPosition(this, 0)), false)); 138 } else { 139 // adjustable letter space, return a sequence of elements; 140 // at the moment the character is supposed to have no letter spaces, 141 // but returning this sequence allows us to change only one element 142 // if addALetterSpaceTo() is called 143 seq.add(new KnuthInlineBox(areaInfo.ipdArea.getOpt(), areaInfo.alignmentContext, 144 notifyPos(new LeafPosition(this, 0)), false)); 145 seq.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, 146 new LeafPosition(this, -1), true)); 147 seq.add(new KnuthGlue(0, 0, 0, 148 new LeafPosition(this, -1), true)); 149 seq.add(new KnuthInlineBox(0, null, 150 notifyPos(new LeafPosition(this, -1)), true)); 151 } 152 153 addKnuthElementsForBorderPaddingEnd(seq); 154 155 LinkedList<KnuthSequence> returnList = new LinkedList<KnuthSequence>(); 156 returnList.add(seq); 157 setFinished(true); 158 return returnList; 159 } 160 161 /** {@inheritDoc} */ 162 @Override getWordChars(Position pos)163 public String getWordChars(Position pos) { 164 return String.valueOf(((Character) fobj).getCharacter()); 165 } 166 167 /** {@inheritDoc} */ 168 @Override hyphenate(Position pos, HyphContext hc)169 public void hyphenate(Position pos, HyphContext hc) { 170 if (hc.getNextHyphPoint() == 1) { 171 // the character ends a syllable 172 areaInfo.isHyphenated = true; 173 somethingChanged = true; 174 } else { 175 // hc.getNextHyphPoint() returned -1 (no more hyphenation points) 176 // or a number > 1; 177 // the character does not end a syllable 178 } 179 hc.updateOffset(1); 180 } 181 182 /** {@inheritDoc} */ 183 @Override applyChanges(List oldList)184 public boolean applyChanges(List oldList) { 185 setFinished(false); 186 return somethingChanged; 187 } 188 189 /** {@inheritDoc} */ 190 @Override getChangedKnuthElements(List oldList, int alignment)191 public List getChangedKnuthElements(List oldList, int alignment) { 192 if (isFinished()) { 193 return null; 194 } 195 196 LinkedList<KnuthElement> returnList = new LinkedList<KnuthElement>(); 197 198 addKnuthElementsForBorderPaddingStart(returnList); 199 200 if (letterSpaceIPD.isStiff() || areaInfo.letterSpaces == 0) { 201 // constant letter space, or no letter space 202 returnList.add(new KnuthInlineBox(areaInfo.ipdArea.getOpt(), 203 areaInfo.alignmentContext, 204 notifyPos(new LeafPosition(this, 0)), false)); 205 if (areaInfo.isHyphenated) { 206 returnList.add(new KnuthPenalty(hyphIPD, KnuthPenalty.FLAGGED_PENALTY, true, 207 new LeafPosition(this, -1), false)); 208 } 209 } else { 210 // adjustable letter space 211 returnList.add(new KnuthInlineBox(areaInfo.ipdArea.getOpt() 212 - areaInfo.letterSpaces * letterSpaceIPD.getOpt(), areaInfo.alignmentContext, 213 notifyPos(new LeafPosition(this, 0)), false)); 214 returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, 215 new LeafPosition(this, -1), true)); 216 returnList.add(new KnuthGlue(letterSpaceIPD.mult(areaInfo.letterSpaces), 217 new LeafPosition(this, -1), true)); 218 returnList.add( 219 new KnuthInlineBox(0, null, notifyPos(new LeafPosition(this, -1)), true)); 220 if (areaInfo.isHyphenated) { 221 returnList.add(new KnuthPenalty(hyphIPD, KnuthPenalty.FLAGGED_PENALTY, true, 222 new LeafPosition(this, -1), false)); 223 } 224 } 225 226 addKnuthElementsForBorderPaddingEnd(returnList); 227 228 setFinished(true); 229 return returnList; 230 } 231 232 } 233