org.apache.xmlgraphics.java2d.ps.PSTilingPattern Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xmlgraphics-commons Show documentation
Show all versions of xmlgraphics-commons Show documentation
Apache XML Graphics Commons is a library that consists of several reusable
components used by Apache Batik and Apache FOP. Many of these components
can easily be used separately outside the domains of SVG and XSL-FO.
/*
* 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: PSTilingPattern.java 1809627 2017-09-25 13:42:08Z ssteiner $ */
package org.apache.xmlgraphics.java2d.ps;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.TexturePaint;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.List;
/**
* This class is implementation of PostScript tiling pattern. It allows to make a pattern
* with defined PaintProc or texture.
*
* Originally authored by Jiri Kunhart.
*/
public class PSTilingPattern {
/**
* A code identifying the pattern type that this dictionary describes;
* must be 1 for a tiling pattern
*/
public static final int PATTERN_TYPE_TILING = 1;
/** PostScript constant for a shading pattern (unsupported) */
public static final int PATTERN_TYPE_SHADING = 2;
/** the pattern type of this pattern */
protected int patternType = PATTERN_TYPE_TILING;
//TODO To be moved to a super class once shading patterns are implemented.
/**
* The name of the pattern (for example: "Pattern1" )
*/
protected String patternName;
/**
* The XUID is an extended unique ID -- an array of integers that provides for
* distributed, hierarchical management of the space of unique ID numbers
* (optional)
*/
protected List xUID;
/**
* A PostScript procedure for painting the pattern cell
*/
protected StringBuffer paintProc;
/**
* An array of four numbers in the pattern coordinate system, giving
* the coordinates of the left, bottom, right, and top edges, respectively, of the
* pattern cell's bounding box
*/
protected Rectangle2D bBox;
/**
* The desired horizontal spacing between pattern cells, measured in
* the pattern coordinate system
*/
protected double xStep;
/**
* The desired vertical spacing between pattern cells, measured in
* the pattern coordinate system
*/
protected double yStep;
/**
* A code that determines how the color of the pattern cell is to be
* specified: 1 for colored pattern, 2 for uncolored pattern
*/
protected int paintType = 2;
/**
* A code that controls adjustments to the spacing of tiles relative to
* the device pixel grid:
* 1 for constant spacing,
* 2 for no distortion
* 3 for constant spacing and faster tiling.
*/
protected int tilingType = 1;
/**
* A texture is used for filling shapes
*/
protected TexturePaint texture;
/**
* Constructor for the creation of pattern with defined PaintProc
*
* @param patternName the name of the pattern (for example: "Pattern1" ), if
* the name is null, the pattern should be stored in PSPatternStorage, where the pattern
* gets a name (the pattern without name cannot be use in PS file)
* @param paintProc a postscript procedure for painting the pattern cell
* @param bBox a pattern cell's bounding box
* @param xStep the desired horizontal spacing between pattern cells
* @param yStep the desired vertical spacing between pattern cells
* @param paintType 1 for colored pattern, 2 for uncolored pattern
* @param tilingType adjustments to the spacing of tiles relative to
* the device pixel grid (1,2 or 3)
* @param xUID an extended unique ID (optional)
*/
public PSTilingPattern(String patternName, StringBuffer paintProc, Rectangle bBox,
double xStep, double yStep,
int paintType, int tilingType, List xUID) {
// check the parameters
this.patternName = patternName;
this.paintProc = paintProc;
setBoundingBox(bBox);
setXStep(xStep);
setYStep(yStep);
setPaintType(paintType);
setTilingType(tilingType);
this.xUID = xUID;
}
/**
* Constructor for the creation of pattern with defined texture
*
* @param patternName the name of the pattern (for example: "Pattern1" ), if
* the name is null, the pattern should be stored in PSPatternStorage, where the pattern
* gets a name (a pattern without name cannot be use in PS file)
* @param texture a texture is used for filling a shape
* @param xStep the desired horizontal spacing between pattern cells
* @param yStep yStep the desired vertical spacing between pattern cells
* @param tilingType adjustments to the spacing of tiles relative to
* the device pixel grid (1,2 or 3)
* @param xUID xUID an extended unique ID (optional)
*/
public PSTilingPattern(String patternName, TexturePaint texture, double xStep, double yStep,
int tilingType, List xUID) {
this(patternName, null, new Rectangle(), 1, 1, 1, tilingType, xUID);
this.texture = texture;
Rectangle2D anchor = texture.getAnchorRect();
bBox = new Rectangle2D.Double(
anchor.getX(), anchor.getY(),
anchor.getX() + anchor.getWidth(), anchor.getY() + anchor.getHeight());
// xStep and yStep may be either positive or negative, but not zero => if it is zero,
// we set xStep and yStep in this way that the pattern will be without spaces
this.xStep = (xStep == 0) ? anchor.getWidth() : xStep;
this.yStep = (yStep == 0) ? anchor.getHeight() : yStep;
}
/**
* Gets the name of the pattern
*
* @return String representing the name of the pattern.
*/
public String getName() {
return (this.patternName);
}
/**
* Sets the name of the pattern.
* @param name the name of the pattern. Can be anything without spaces (for example "Pattern1").
*/
public void setName(String name) {
if (name == null) {
throw new NullPointerException("Parameter patternName must not be null");
}
if (name.length() == 0) {
throw new IllegalArgumentException("Parameter patternName must not be empty");
}
if (name.indexOf(" ") >= 0) {
throw new IllegalArgumentException(
"Pattern name must not contain any spaces");
}
this.patternName = name;
}
/**
* Returns the bounding box.
*
* @return a pattern cell's bounding box
*/
public Rectangle2D getBoundingBox() {
return (this.bBox);
}
/**
* Sets the bounding box.
*
* @param bBox a pattern cell's bounding box
*/
public void setBoundingBox(Rectangle2D bBox) {
if (bBox == null) {
throw new NullPointerException("Parameter bBox must not be null");
}
this.bBox = bBox;
}
/**
* Gets the postscript procedure PaintProc
*
* @return the postscript procedure PaintProc
*/
public StringBuffer getPaintProc() {
return (this.paintProc);
}
/**
* Sets the postscript procedure PaintProc
*
* @param paintProc the postscript procedure PaintProc
*/
public void setPaintProc(StringBuffer paintProc) {
this.paintProc = paintProc;
}
/**
* Gets the horizontal spacing between pattern cells
*
* @return the horizontal spacing between pattern cells
*/
public double getXStep() {
return (this.xStep);
}
/**
* Sets the horizontal spacing between pattern cells
*
* @param xStep the horizontal spacing between pattern cells
*/
public void setXStep(double xStep) {
if (xStep == 0) {
throw new IllegalArgumentException("Parameter xStep must not be 0");
}
this.xStep = xStep;
}
/**
* Gets the vertical spacing between pattern cells
*
* @return the vertical spacing between pattern cells
*/
public double getYStep() {
return (this.yStep);
}
/**
* Sets the vertical spacing between pattern cells
*
* @param yStep the vertical spacing between pattern cells
*/
public void setYStep(double yStep) {
if (yStep == 0) {
throw new IllegalArgumentException("Parameter yStep must not be 0");
}
this.yStep = yStep;
}
/**
* Gets the code that determines how the color of the pattern cell is to be
* specified: 1 for colored pattern, 2 for uncolored pattern
*
* @return the paint type
*/
public int getPaintType() {
return (this.paintType);
}
/**
* Sets the code that determines how the color of the pattern cell is to be
* specified: 1 for colored pattern, 2 for uncolored pattern
*
* @param paintType the paint type
*/
public void setPaintType(int paintType) {
if ((paintType != 1) && (paintType != 2)) {
throw new IllegalArgumentException("Parameter paintType must not be "
+ paintType + " (only 1 or 2)");
}
this.paintType = paintType;
}
/**
* Gets a code that controls adjustments to the spacing of tiles relative to
* the device pixel grid: 1 for constant spacing, 2 for no distortion
* 3 for constant spacing and faster tiling
*
* @return the tiling type
*/
public int getTilingType() {
return (this.tilingType);
}
/**
* Sets a code that controls adjustments to the spacing of tiles relative to
* the device pixel grid: 1 for constant spacing, 2 for no distortion
* 3 for constant spacing and faster tiling
*
* @param tilingType the tiling type
*/
public void setTilingType(int tilingType) {
if (!((tilingType <= 3) && (tilingType >= 1))) {
throw new IllegalArgumentException("Parameter tilingType must not be "
+ tilingType + " (only 1, 2 or 3)");
}
this.tilingType = tilingType;
}
/**
* Gets a texture which is used for filling shapes
*
* @return the texture
*/
public TexturePaint getTexturePaint() {
return (this.texture);
}
/**
* Sets a texture which is used for filling shapes
*
* @param texturePaint the texture
*/
public void setTexturePaint(TexturePaint texturePaint) {
this.texture = texturePaint;
}
/**
* Gets an extended unique ID that uniquely identifies the pattern
*
* @return xUID the unique ID
*/
public List getXUID() {
return (this.xUID);
}
/**
* Sets an extended unique ID that uniquely identifies the pattern
*
* @param xUID the unique ID
*/
public void setXUID(List xUID) {
this.xUID = xUID;
}
/**
* Generates postscript code for a pattern
*
* @return The string which contains postscript code of pattern definition
*/
public String toString(boolean acrobatDownsample) {
StringBuffer sb = new StringBuffer("<<\n");
sb.append("/PatternType " + this.patternType + "\n");
sb.append("/PaintType " + paintType + "\n");
sb.append("/TilingType " + tilingType + "\n");
sb.append("/XStep " + xStep + "\n");
sb.append("/YStep " + yStep + "\n");
sb.append("/BBox " + "[" + bBox.getX() + " " + bBox.getY() + " "
+ bBox.getWidth() + " " + bBox.getHeight() + "]" + "\n");
sb.append("/PaintProc\n" + "{\n");
// the PaintProc procedure is expected to consume its dictionary operand !
if ((paintProc == null) || (paintProc.indexOf("pop") != 0)) {
sb.append("pop\n");
}
if (texture != null) {
int width = texture.getImage().getWidth();
int height = texture.getImage().getHeight();
Rectangle2D anchor = texture.getAnchorRect();
if (anchor.getX() != 0 || anchor.getY() != 0) {
sb.append(anchor.getX() + " " + anchor.getY() + " translate\n");
}
double scaleX = anchor.getWidth() / width;
double scaleY = anchor.getHeight() / height;
if (scaleX != 1 || scaleY != 1) {
sb.append(scaleX + " " + scaleY + " scale\n");
}
// define color image: width height bits/comp matrix
// datasrc0 datasrcncomp-1 multi ncomp colorimage
// width height bits/comp matrix
int bits = 8;
if (acrobatDownsample) {
bits = 4;
}
sb.append(width).append(" ").append(height).append(" ").append(bits).append(" ").append("matrix\n");
int [] argb = new int[width * height]; // datasrc0 datasrcncomp-1
getAsRGB().getRGB(0, 0, width, height, argb, 0, width);
writeImage(sb, argb, width, bits);
sb.append(" false 3 colorimage"); // multi ncomp colorimage
} else {
sb.append(paintProc);
}
sb.append("\n} bind \n"); // the end of PaintProc
sb.append(">>\n");
// create pattern instance from prototype
sb.append("matrix\n");
sb.append("makepattern\n");
// save pattern to current dictionary
sb.append("/" + patternName + " exch def\n");
return sb.toString();
}
private void writeImage(StringBuffer sb, int[] argb, int width, int bits) {
int count = 0;
sb.append("{<");
for (int i = 0; i < argb.length; i++) {
if ((i % width == 0) || (count > 249)) {
sb.append('\n');
count = 0; // line should not be longer than 255 characters
}
if (bits == 4) {
Color c = new Color(argb[i]);
int v = c.getRed() / 16;
String s = Integer.toHexString(v);
sb.append(s);
v = c.getGreen() / 16;
s = Integer.toHexString(v);
sb.append(s);
v = c.getBlue() / 16;
s = Integer.toHexString(v);
sb.append(s);
count += 3;
} else {
// delete alpha canal and write to output
StringBuffer sRGB = new StringBuffer(Integer.toHexString(argb[i] & 0x00ffffff));
if (sRGB.length() != 6) {
sRGB.insert(0, "000000"); // zero padding
sRGB = new StringBuffer(sRGB.substring(sRGB.length() - 6));
}
sb.append(sRGB);
count += 6;
}
}
sb.append("\n>}");
}
private BufferedImage getAsRGB() {
BufferedImage img = texture.getImage();
if (img.getType() != BufferedImage.TYPE_INT_RGB) {
BufferedImage buf = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D g = buf.createGraphics();
g.setComposite(AlphaComposite.SrcOver);
g.setBackground(Color.white);
g.fillRect(0, 0, img.getWidth(), img.getHeight());
g.drawImage(img, 0, 0, null);
g.dispose();
return buf;
}
return img;
}
/** {@inheritDoc} */
public int hashCode() {
return
0
^ patternType
^ ((xUID != null) ? xUID.hashCode() : 0)
^ ((paintProc != null) ? paintProc.hashCode() : 0)
^ ((bBox != null) ? bBox.hashCode() : 0)
^ Double.valueOf(xStep).hashCode()
^ Double.valueOf(yStep).hashCode()
^ paintType
^ tilingType
^ ((texture != null) ? texture.hashCode() : 0);
}
/**
* Compares two patterns data (except their names).
* {@inheritDoc}
*/
public boolean equals(Object pattern) {
if (pattern == null) {
return false;
}
if (!(pattern instanceof PSTilingPattern)) {
return false;
}
if (this == pattern) {
return true;
}
PSTilingPattern patternObj = (PSTilingPattern) pattern;
if (this.patternType != patternObj.patternType) {
return false;
}
TexturePaint patternTexture = patternObj.getTexturePaint();
if (((patternTexture == null) && (texture != null))
|| ((patternTexture != null) && (texture == null))) {
return false;
}
if ((patternTexture != null) && (texture != null)) {
// compare textures data
int width = texture.getImage().getWidth();
int height = texture.getImage().getHeight();
int widthPattern = patternTexture.getImage().getWidth();
int heightPattern = patternTexture.getImage().getHeight();
if (width != widthPattern) {
return false;
}
if (height != heightPattern) {
return false;
}
int [] rgbData = new int[width * height];
int [] rgbDataPattern = new int[widthPattern * heightPattern];
texture.getImage().getRGB(0, 0, width, height, rgbData, 0, width);
patternTexture.getImage().getRGB(0, 0, widthPattern, heightPattern,
rgbDataPattern, 0, widthPattern);
for (int i = 0; i < rgbData.length; i++) {
if (rgbData[i] != rgbDataPattern[i]) {
return false;
}
}
} else {
// compare PaintProc
if (!paintProc.toString().equals(patternObj.getPaintProc().toString())) {
return false;
}
}
// compare other parameters
if (xStep != patternObj.getXStep()) {
return false;
}
if (yStep != patternObj.getYStep()) {
return false;
}
if (paintType != patternObj.getPaintType()) {
return false;
}
if (tilingType != patternObj.getTilingType()) {
return false;
}
if (!bBox.equals(patternObj.getBoundingBox())) {
return false;
}
if ((xUID != null) && (patternObj.getXUID() != null)) {
if (!xUID.equals(patternObj.getXUID())) {
return false;
}
}
return true;
}
}