/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* $Id$ */ package org.apache.fop.render.rtf; import java.awt.Color; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.fop.apps.FOPException; import org.apache.fop.datatypes.Length; import org.apache.fop.datatypes.PercentBaseContext; import org.apache.fop.fo.Constants; import org.apache.fop.fo.FONode; import org.apache.fop.fo.FOText; import org.apache.fop.fo.flow.Block; import org.apache.fop.fo.flow.BlockContainer; import org.apache.fop.fo.flow.Inline; import org.apache.fop.fo.flow.Leader; import org.apache.fop.fo.flow.PageNumber; import org.apache.fop.fo.flow.table.TableCell; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.CommonFont; import org.apache.fop.fo.properties.CommonMarginBlock; import org.apache.fop.fo.properties.CommonTextDecoration; import org.apache.fop.fo.properties.PercentLength; import org.apache.fop.render.rtf.rtflib.rtfdoc.IBorderAttributes; import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfAttributes; import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfColorTable; import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfFontManager; import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfLeader; import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfText; /** * <p>Converts FO properties to RtfAttributes.</p> * * <p>This work was authored by Bertrand Delacretaz (bdelacretaz@codeconsult.ch), * Boris Poudérous (boris.pouderous@eads-telecom.com), * Peter Herweg (pherweg@web.de), * Normand Massé, * Christopher Scott (scottc@westinghouse.com), and * Roberto Marra (roberto@link-u.com).</p> */ final class TextAttributesConverter { private static Log log = LogFactory.getLog(TextAttributesConverter.class); /** * Constructor is private, because it's just a utility class. */ private TextAttributesConverter() { } /** * Converts all known text FO properties to RtfAttributes * @param fobj the FO for which the attributes are to be converted */ public static RtfAttributes convertAttributes(Block fobj) throws FOPException { FOPRtfAttributes attrib = new FOPRtfAttributes(); attrFont(fobj.getCommonFont(), attrib); attrFontColor(fobj.getColor(), attrib); //attrTextDecoration(fobj.getTextDecoration(), attrib); attrBlockBackgroundColor(fobj.getCommonBorderPaddingBackground(), attrib); attrBlockMargin(fobj.getCommonMarginBlock(), attrib); attrBlockTextAlign(fobj.getTextAlign(), attrib); attrBorder(fobj.getCommonBorderPaddingBackground(), attrib, fobj); attrBreak(fobj, attrib); return attrib; } private static void attrBreak(Block fobj, FOPRtfAttributes attrib) { int breakValue = fobj.getBreakBefore(); if (breakValue != Constants.EN_AUTO) { //"sect" Creates a new section and a page break, //a simple page break with control word "page" caused //some problems boolean bHasTableCellParent = false; FONode f = fobj; while (f.getParent() != null) { f = f.getParent(); if (f instanceof TableCell) { bHasTableCellParent = true; break; } } if (!bHasTableCellParent) { attrib.set("sect"); switch (breakValue) { case Constants.EN_EVEN_PAGE: attrib.set("sbkeven"); break; case Constants.EN_ODD_PAGE: attrib.set("sbkodd"); break; case Constants.EN_COLUMN: attrib.set("sbkcol"); break; default: attrib.set("sbkpage"); } } else { log.warn("Cannot create break-before for a block inside a table."); } } //Break after is handled in RtfCloseGroupMark } /** * Converts all known text FO properties to RtfAttributes * @param fobj FObj whose properties are to be converted */ public static RtfAttributes convertBlockContainerAttributes(BlockContainer fobj) throws FOPException { FOPRtfAttributes attrib = new FOPRtfAttributes(); attrBackgroundColor(fobj.getCommonBorderPaddingBackground(), attrib); attrBlockMargin(fobj.getCommonMarginBlock(), attrib); //attrBlockDimension(fobj, attrib); attrBorder(fobj.getCommonBorderPaddingBackground(), attrib, fobj); return attrib; } /** * Converts all character related FO properties to RtfAttributes. * @param fobj FObj whose properties are to be converted */ public static RtfAttributes convertCharacterAttributes( FOText fobj) throws FOPException { FOPRtfAttributes attrib = new FOPRtfAttributes(); attrFont(fobj.getCommonFont(), attrib); attrFontColor(fobj.getColor(), attrib); attrTextDecoration(fobj.getTextDecoration(), attrib); attrBaseLineShift(fobj.getBaseLineShift(), attrib); return attrib; } /** * Converts all character related FO properties to RtfAttributes. * @param fobj FObj whose properties are to be converted */ public static RtfAttributes convertCharacterAttributes( PageNumber fobj) throws FOPException { FOPRtfAttributes attrib = new FOPRtfAttributes(); attrFont(fobj.getCommonFont(), attrib); attrTextDecoration(fobj.getTextDecoration(), attrib); attrBackgroundColor(fobj.getCommonBorderPaddingBackground(), attrib); return attrib; } /** * Converts all character related FO properties to RtfAttributes. * @param fobj FObj whose properties are to be converted */ public static RtfAttributes convertCharacterAttributes( Inline fobj) throws FOPException { FOPRtfAttributes attrib = new FOPRtfAttributes(); attrFont(fobj.getCommonFont(), attrib); attrFontColor(fobj.getColor(), attrib); attrBackgroundColor(fobj.getCommonBorderPaddingBackground(), attrib); attrInlineBorder(fobj.getCommonBorderPaddingBackground(), attrib); return attrib; } /** * Converts FO properties used by RtfLeader to RtfAttributes. * @param fobj Leader * @param context PercentBaseContext * @return RtfAttributes * @throws FOPException */ public static RtfAttributes convertLeaderAttributes(Leader fobj, PercentBaseContext context) throws FOPException { boolean tab = false; FOPRtfAttributes attrib = new FOPRtfAttributes(); attrib.set(RtfText.ATTR_FONT_FAMILY, RtfFontManager.getInstance().getFontNumber(fobj.getCommonFont().getFirstFontFamily())); if (fobj.getLeaderLength() != null) { attrib.set(RtfLeader.LEADER_WIDTH, convertMptToTwips(fobj.getLeaderLength().getMaximum( context).getLength().getValue(context))); if (fobj.getLeaderLength().getMaximum(context) instanceof PercentLength) { if (((PercentLength)fobj.getLeaderLength().getMaximum(context)).getString().equals( "100.0%")) { // Use Tab instead of white spaces attrib.set(RtfLeader.LEADER_USETAB, 1); tab = true; } } } attrFontColor(fobj.getColor(), attrib); /* if (fobj.getLeaderPatternWidth() != null) { //TODO calculate pattern width not possible for white spaces, because its using //underlines for tab it would work with LEADER_PATTERN_WIDTH (expndtw) } */ switch(fobj.getLeaderPattern()) { case Constants.EN_DOTS: if (tab) { attrib.set(RtfLeader.LEADER_TABLEAD, RtfLeader.LEADER_TAB_DOTTED); } else { attrib.set(RtfLeader.LEADER_TABLEAD, RtfLeader.LEADER_DOTTED); } break; case Constants.EN_SPACE: //nothing has to be set for spaces break; case Constants.EN_RULE: //Things like start-indent, space-after, ... not supported? //Leader class does not offer these properties //TODO aggregate them with the leader width or // create a second - blank leader - before if (fobj.getRuleThickness() != null) { //TODO See inside RtfLeader, better calculation for //white spaces would be necessary //attrib.set(RtfLeader.LEADER_RULE_THICKNESS, // fobj.getRuleThickness().getValue(context)); log.warn("RTF: fo:leader rule-thickness not supported"); } switch (fobj.getRuleStyle()) { case Constants.EN_SOLID: if (tab) { attrib.set(RtfLeader.LEADER_TABLEAD, RtfLeader.LEADER_TAB_THICK); } else { attrib.set(RtfLeader.LEADER_TABLEAD, RtfLeader.LEADER_THICK); } break; case Constants.EN_DASHED: if (tab) { attrib.set(RtfLeader.LEADER_TABLEAD, RtfLeader.LEADER_TAB_MIDDLEDOTTED); } else { attrib.set(RtfLeader.LEADER_TABLEAD, RtfLeader.LEADER_MIDDLEDOTTED); } break; case Constants.EN_DOTTED: if (tab) { attrib.set(RtfLeader.LEADER_TABLEAD, RtfLeader.LEADER_TAB_DOTTED); } else { attrib.set(RtfLeader.LEADER_TABLEAD, RtfLeader.LEADER_DOTTED); } break; case Constants.EN_DOUBLE: if (tab) { attrib.set(RtfLeader.LEADER_TABLEAD, RtfLeader.LEADER_TAB_EQUAL); } else { attrib.set(RtfLeader.LEADER_TABLEAD, RtfLeader.LEADER_EQUAL); } break; case Constants.EN_GROOVE: if (tab) { attrib.set(RtfLeader.LEADER_TABLEAD, RtfLeader.LEADER_TAB_HYPHENS); } else { attrib.set(RtfLeader.LEADER_TABLEAD, RtfLeader.LEADER_HYPHENS); } break; case Constants.EN_RIDGE: if (tab) { attrib.set(RtfLeader.LEADER_TABLEAD, RtfLeader.LEADER_TAB_UNDERLINE); } else { attrib.set(RtfLeader.LEADER_TABLEAD, RtfLeader.LEADER_UNDERLINE); } break; default: break; } break; case Constants.EN_USECONTENT: log.warn("RTF: fo:leader use-content not supported"); break; default: break; } if (fobj.getLeaderAlignment() == Constants.EN_REFERENCE_AREA) { log.warn("RTF: fo:leader reference-area not supported"); } return attrib; } private static int convertMptToTwips(int mpt) { return Math.round(FoUnitsConverter.getInstance().convertMptToTwips(mpt)); } private static void attrFont(CommonFont font, FOPRtfAttributes rtfAttr) { rtfAttr.set(RtfText.ATTR_FONT_FAMILY, RtfFontManager.getInstance().getFontNumber(font.getFirstFontFamily())); rtfAttr.setHalfPoints(RtfText.ATTR_FONT_SIZE, font.fontSize); if (font.getFontWeight() == Constants.EN_700 || font.getFontWeight() == Constants.EN_800 || font.getFontWeight() == Constants.EN_900) { //Everything from 700 and above is declared as bold rtfAttr.set("b", 1); } else { rtfAttr.set("b", 0); } if (font.getFontStyle() == Constants.EN_ITALIC) { rtfAttr.set(RtfText.ATTR_ITALIC, 1); } else { rtfAttr.set(RtfText.ATTR_ITALIC, 0); } } private static void attrFontColor(Color colorType, RtfAttributes rtfAttr) { // Cell background color if (colorType != null) { if (colorType.getAlpha() != 0 || colorType.getRed() != 0 || colorType.getGreen() != 0 || colorType.getBlue() != 0) { rtfAttr.set(RtfText.ATTR_FONT_COLOR, convertFOPColorToRTF(colorType)); } } } private static void attrTextDecoration(CommonTextDecoration textDecoration, RtfAttributes rtfAttr) { if (textDecoration == null) { rtfAttr.set(RtfText.ATTR_UNDERLINE, 0); rtfAttr.set(RtfText.ATTR_STRIKETHROUGH, 0); return; } if (textDecoration.hasUnderline()) { rtfAttr.set(RtfText.ATTR_UNDERLINE, 1); } else { rtfAttr.set(RtfText.ATTR_UNDERLINE, 0); } if (textDecoration.hasLineThrough()) { rtfAttr.set(RtfText.ATTR_STRIKETHROUGH, 1); } else { rtfAttr.set(RtfText.ATTR_STRIKETHROUGH, 0); } } private static void attrBlockMargin(CommonMarginBlock cmb, FOPRtfAttributes rtfAttr) { rtfAttr.setTwips(RtfText.SPACE_BEFORE, cmb.spaceBefore.getOptimum(null).getLength()); rtfAttr.setTwips(RtfText.SPACE_AFTER, cmb.spaceAfter.getOptimum(null).getLength()); rtfAttr.setTwips(RtfText.LEFT_INDENT_BODY, cmb.startIndent); rtfAttr.setTwips(RtfText.RIGHT_INDENT_BODY, cmb.endIndent); } /* private static void attrBlockDimension(FObj fobj, FOPRtfAttributes rtfAttr) { Length ipd = fobj.getProperty(Constants.PR_INLINE_PROGRESSION_DIMENSION) .getLengthRange().getOptimum().getLength(); if (ipd.getEnum() != Constants.EN_AUTO) { rtfAttr.set(RtfText.FRAME_WIDTH, ipd); } Length bpd = fobj.getProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION) .getLengthRange().getOptimum().getLength(); if (bpd.getEnum() != Constants.EN_AUTO) { rtfAttr.set(RtfText.FRAME_HEIGHT, bpd); } } */ private static void attrBlockTextAlign(int alignment, RtfAttributes rtfAttr) { String rtfValue = null; switch (alignment) { case Constants.EN_CENTER: rtfValue = RtfText.ALIGN_CENTER; break; case Constants.EN_END: rtfValue = RtfText.ALIGN_RIGHT; break; case Constants.EN_JUSTIFY: rtfValue = RtfText.ALIGN_JUSTIFIED; break; default: rtfValue = RtfText.ALIGN_LEFT; break; } rtfAttr.set(rtfValue); } /** * Reads background-color for block from <code>bpb</code> and writes it to * <code>rtfAttr</code>. */ private static void attrBlockBackgroundColor( CommonBorderPaddingBackground bpb, RtfAttributes rtfAttr) { if (bpb.hasBackground()) { rtfAttr.set(RtfText.SHADING, RtfText.FULL_SHADING); rtfAttr.set(RtfText.SHADING_FRONT_COLOR, convertFOPColorToRTF(bpb.backgroundColor)); } } /** Adds border information from <code>bpb</code> to <code>rtrAttr</code>. */ private static void attrBorder(CommonBorderPaddingBackground bpb, RtfAttributes rtfAttr, FONode fobj) { if (hasBorder(fobj.getParent())) { attrInlineBorder(bpb, rtfAttr); return; } BorderAttributesConverter.makeBorder(bpb, CommonBorderPaddingBackground.BEFORE, rtfAttr, IBorderAttributes.BORDER_TOP); BorderAttributesConverter.makeBorder(bpb, CommonBorderPaddingBackground.AFTER, rtfAttr, IBorderAttributes.BORDER_BOTTOM); BorderAttributesConverter.makeBorder(bpb, CommonBorderPaddingBackground.START, rtfAttr, IBorderAttributes.BORDER_LEFT); BorderAttributesConverter.makeBorder(bpb, CommonBorderPaddingBackground.END, rtfAttr, IBorderAttributes.BORDER_RIGHT); } /** @return true, if element <code>node</code> has border. */ private static boolean hasBorder(FONode node) { while (node != null) { CommonBorderPaddingBackground commonBorderPaddingBackground = null; if (node instanceof Block) { Block block = (Block) node; commonBorderPaddingBackground = block.getCommonBorderPaddingBackground(); } else if (node instanceof BlockContainer) { BlockContainer container = (BlockContainer) node; commonBorderPaddingBackground = container.getCommonBorderPaddingBackground(); } if (commonBorderPaddingBackground != null && commonBorderPaddingBackground.hasBorder()) { return true; } node = node.getParent(); } return false; } /** Adds inline border information from <code>bpb</code> to <code>rtrAttr</code>. */ private static void attrInlineBorder(CommonBorderPaddingBackground bpb, RtfAttributes rtfAttr) { BorderAttributesConverter.makeBorder(bpb, CommonBorderPaddingBackground.BEFORE, rtfAttr, IBorderAttributes.BORDER_CHARACTER); } /** * Reads background-color from bl and writes it to rtfAttr. * * @param bpb the CommonBorderPaddingBackground from which the properties are read * @param rtfAttr the RtfAttributes object the attributes are written to */ private static void attrBackgroundColor(CommonBorderPaddingBackground bpb, RtfAttributes rtfAttr) { Color fopValue = bpb.backgroundColor; int rtfColor = 0; /* FOP uses a default background color of "transparent", which is actually a transparent black, which is generally not suitable as a default here. Changing FOP's default to "white" causes problems in PDF output, so we will look for the default here & change it to "auto". */ if ((fopValue == null) || ((fopValue.getRed() == 0) && (fopValue.getGreen() == 0) && (fopValue.getBlue() == 0) && (fopValue.getAlpha() == 0))) { return; } else { rtfColor = convertFOPColorToRTF(fopValue); } rtfAttr.set(RtfText.ATTR_BACKGROUND_COLOR, rtfColor); } private static void attrBaseLineShift(Length baselineShift, RtfAttributes rtfAttr) { int s = baselineShift.getEnum(); if (s == Constants.EN_SUPER) { rtfAttr.set(RtfText.ATTR_SUPERSCRIPT); } else if (s == Constants.EN_SUB) { rtfAttr.set(RtfText.ATTR_SUBSCRIPT); } } /** * Converts a FOP ColorType to the integer pointing into the RTF color table * @param fopColor the ColorType object to be converted * @return integer pointing into the RTF color table */ public static int convertFOPColorToRTF(Color fopColor) { // TODO: This code is duplicated in FOPRtfAttributesConverter int redComponent = fopColor.getRed(); int greenComponent = fopColor.getGreen(); int blueComponent = fopColor.getBlue(); return RtfColorTable.getInstance().getColorNumber(redComponent, greenComponent, blueComponent); } }