/* ==================================================================== | |
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. | |
==================================================================== */ | |
package org.apache.poi.sl.draw; | |
import java.awt.Graphics2D; | |
import java.awt.font.TextLayout; | |
import java.awt.geom.AffineTransform; | |
import java.awt.geom.Rectangle2D; | |
import java.text.AttributedString; | |
import java.util.HashMap; | |
import java.util.Map; | |
import org.apache.poi.sl.usermodel.Background; | |
import org.apache.poi.sl.usermodel.ConnectorShape; | |
import org.apache.poi.sl.usermodel.FreeformShape; | |
import org.apache.poi.sl.usermodel.GraphicalFrame; | |
import org.apache.poi.sl.usermodel.GroupShape; | |
import org.apache.poi.sl.usermodel.MasterSheet; | |
import org.apache.poi.sl.usermodel.PictureShape; | |
import org.apache.poi.sl.usermodel.PlaceableShape; | |
import org.apache.poi.sl.usermodel.Shape; | |
import org.apache.poi.sl.usermodel.Sheet; | |
import org.apache.poi.sl.usermodel.Slide; | |
import org.apache.poi.sl.usermodel.TableShape; | |
import org.apache.poi.sl.usermodel.TextBox; | |
import org.apache.poi.sl.usermodel.TextParagraph; | |
import org.apache.poi.sl.usermodel.TextShape; | |
import org.apache.poi.util.JvmBugs; | |
public class DrawFactory { | |
protected static final ThreadLocal<DrawFactory> defaultFactory = new ThreadLocal<DrawFactory>(); | |
/** | |
* Set a custom draw factory for the current thread. | |
* This is a fallback, for operations where usercode can't set a graphics context. | |
* Preferably use the rendering hint {@link Drawable#DRAW_FACTORY} to set the factory. | |
* | |
* @param factory | |
*/ | |
public static void setDefaultFactory(DrawFactory factory) { | |
defaultFactory.set(factory); | |
} | |
/** | |
* Returns the DrawFactory, preferably via a graphics instance. | |
* If graphics is null, the current thread local is checked or | |
* if it is not set, a new factory is created. | |
* | |
* @param graphics the current graphics context or null | |
* @return the draw factory | |
*/ | |
public static DrawFactory getInstance(Graphics2D graphics) { | |
// first try to find the factory over the rendering hint | |
DrawFactory factory = null; | |
boolean isHint = false; | |
if (graphics != null) { | |
factory = (DrawFactory)graphics.getRenderingHint(Drawable.DRAW_FACTORY); | |
isHint = (factory != null); | |
} | |
// secondly try the thread local default | |
if (factory == null) { | |
factory = defaultFactory.get(); | |
} | |
// and at last, use the default factory | |
if (factory == null) { | |
factory = new DrawFactory(); | |
} | |
if (graphics != null && !isHint) { | |
graphics.setRenderingHint(Drawable.DRAW_FACTORY, factory); | |
} | |
return factory; | |
} | |
public Drawable getDrawable(Shape<?,?> shape) { | |
if (shape instanceof TextBox) { | |
return getDrawable((TextBox<?,?>)shape); | |
} else if (shape instanceof FreeformShape) { | |
return getDrawable((FreeformShape<?,?>)shape); | |
} else if (shape instanceof TextShape) { | |
return getDrawable((TextShape<?,?>)shape); | |
} else if (shape instanceof TableShape) { | |
return getDrawable((TableShape<?,?>)shape); | |
} else if (shape instanceof GroupShape) { | |
return getDrawable((GroupShape<?,?>)shape); | |
} else if (shape instanceof PictureShape) { | |
return getDrawable((PictureShape<?,?>)shape); | |
} else if (shape instanceof GraphicalFrame) { | |
return getDrawable((GraphicalFrame<?,?>)shape); | |
} else if (shape instanceof Background) { | |
return getDrawable((Background<?,?>)shape); | |
} else if (shape instanceof ConnectorShape) { | |
return getDrawable((ConnectorShape<?,?>)shape); | |
} else if (shape instanceof Slide) { | |
return getDrawable((Slide<?,?>)shape); | |
} else if (shape instanceof MasterSheet) { | |
return getDrawable((MasterSheet<?,?>)shape); | |
} else if (shape instanceof Sheet) { | |
return getDrawable((Sheet<?,?>)shape); | |
} else if (shape.getClass().isAnnotationPresent(DrawNotImplemented.class)) { | |
return new DrawNothing(shape); | |
} | |
throw new IllegalArgumentException("Unsupported shape type: "+shape.getClass()); | |
} | |
public DrawSlide getDrawable(Slide<?,?> sheet) { | |
return new DrawSlide(sheet); | |
} | |
public DrawSheet getDrawable(Sheet<?,?> sheet) { | |
return new DrawSheet(sheet); | |
} | |
public DrawMasterSheet getDrawable(MasterSheet<?,?> sheet) { | |
return new DrawMasterSheet(sheet); | |
} | |
public DrawTextBox getDrawable(TextBox<?,?> shape) { | |
return new DrawTextBox(shape); | |
} | |
public DrawFreeformShape getDrawable(FreeformShape<?,?> shape) { | |
return new DrawFreeformShape(shape); | |
} | |
public DrawConnectorShape getDrawable(ConnectorShape<?,?> shape) { | |
return new DrawConnectorShape(shape); | |
} | |
public DrawTableShape getDrawable(TableShape<?,?> shape) { | |
return new DrawTableShape(shape); | |
} | |
public DrawTextShape getDrawable(TextShape<?,?> shape) { | |
return new DrawTextShape(shape); | |
} | |
public DrawGroupShape getDrawable(GroupShape<?,?> shape) { | |
return new DrawGroupShape(shape); | |
} | |
public DrawPictureShape getDrawable(PictureShape<?,?> shape) { | |
return new DrawPictureShape(shape); | |
} | |
public DrawGraphicalFrame getDrawable(GraphicalFrame<?,?> shape) { | |
return new DrawGraphicalFrame(shape); | |
} | |
public DrawTextParagraph getDrawable(TextParagraph<?,?,?> paragraph) { | |
return new DrawTextParagraph(paragraph); | |
} | |
public DrawBackground getDrawable(Background<?,?> shape) { | |
return new DrawBackground(shape); | |
} | |
public DrawTextFragment getTextFragment(TextLayout layout, AttributedString str) { | |
return new DrawTextFragment(layout, str); | |
} | |
public DrawPaint getPaint(PlaceableShape<?,?> shape) { | |
return new DrawPaint(shape); | |
} | |
/** | |
* Convenience method for drawing single shapes. | |
* For drawing whole slides, use {@link Slide#draw(Graphics2D)} | |
* | |
* @param graphics the graphics context to draw to | |
* @param shape the shape | |
* @param bounds the bounds within the graphics context to draw to | |
*/ | |
public void drawShape(Graphics2D graphics, Shape<?,?> shape, Rectangle2D bounds) { | |
Rectangle2D shapeBounds = shape.getAnchor(); | |
if (shapeBounds.isEmpty() || (bounds != null && bounds.isEmpty())) { | |
return; | |
} | |
AffineTransform txg = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM); | |
AffineTransform tx = new AffineTransform(); | |
try { | |
if (bounds != null) { | |
double scaleX = bounds.getWidth()/shapeBounds.getWidth(); | |
double scaleY = bounds.getHeight()/shapeBounds.getHeight(); | |
tx.translate(bounds.getCenterX(), bounds.getCenterY()); | |
tx.scale(scaleX, scaleY); | |
tx.translate(-shapeBounds.getCenterX(), -shapeBounds.getCenterY()); | |
} | |
graphics.setRenderingHint(Drawable.GROUP_TRANSFORM, tx); | |
Drawable d = getDrawable(shape); | |
d.applyTransform(graphics); | |
d.draw(graphics); | |
} finally { | |
graphics.setRenderingHint(Drawable.GROUP_TRANSFORM, txg); | |
} | |
} | |
/** | |
* Replace font families for Windows JVM 6, which contains a font rendering error. | |
* This is likely to be removed, when POI upgrades to JDK 7 | |
* | |
* @param graphics the graphics context which will contain the font mapping | |
*/ | |
public void fixFonts(Graphics2D graphics) { | |
if (!JvmBugs.hasLineBreakMeasurerBug()) return; | |
@SuppressWarnings("unchecked") | |
Map<String,String> fontMap = (Map<String,String>)graphics.getRenderingHint(Drawable.FONT_MAP); | |
if (fontMap == null) { | |
fontMap = new HashMap<String,String>(); | |
graphics.setRenderingHint(Drawable.FONT_MAP, fontMap); | |
} | |
String fonts[][] = { { "Calibri", "Lucida Sans" }, { "Cambria", "Lucida Bright" } }; | |
for (String f[] : fonts) { | |
if (!fontMap.containsKey(f[0])) { | |
fontMap.put(f[0], f[1]); | |
} | |
} | |
} | |
} |