simplify image processing interface for WordConverters, add PictureManager interface
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1151760 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
0f1c11c113
commit
5895434e97
@ -95,6 +95,8 @@ public abstract class AbstractWordConverter
|
||||
|
||||
private final Set<Bookmark> bookmarkStack = new LinkedHashSet<Bookmark>();
|
||||
|
||||
private PicturesManager fileManager;
|
||||
|
||||
private FontReplacer fontReplacer = new DefaultFontReplacer();
|
||||
|
||||
protected Triplet getCharacterRunTriplet( CharacterRun characterRun )
|
||||
@ -109,6 +111,11 @@ public abstract class AbstractWordConverter
|
||||
|
||||
public abstract Document getDocument();
|
||||
|
||||
public PicturesManager getFileManager()
|
||||
{
|
||||
return fileManager;
|
||||
}
|
||||
|
||||
public FontReplacer getFontReplacer()
|
||||
{
|
||||
return fontReplacer;
|
||||
@ -509,6 +516,25 @@ public abstract class AbstractWordConverter
|
||||
return;
|
||||
}
|
||||
|
||||
protected Field processDeadField( HWPFDocumentCore wordDocument,
|
||||
Range charactersRange, int currentTableLevel, int startOffset,
|
||||
Element currentBlock )
|
||||
{
|
||||
if ( !( wordDocument instanceof HWPFDocument ) )
|
||||
return null;
|
||||
|
||||
HWPFDocument hwpfDocument = (HWPFDocument) wordDocument;
|
||||
Field field = hwpfDocument.getFields().getFieldByStartOffset(
|
||||
FieldsDocumentPart.MAIN, startOffset );
|
||||
if ( field == null )
|
||||
return null;
|
||||
|
||||
processField( hwpfDocument, charactersRange, currentTableLevel, field,
|
||||
currentBlock );
|
||||
|
||||
return field;
|
||||
}
|
||||
|
||||
public void processDocument( HWPFDocumentCore wordDocument )
|
||||
{
|
||||
final SummaryInformation summaryInformation = wordDocument
|
||||
@ -595,25 +621,6 @@ public abstract class AbstractWordConverter
|
||||
field.secondSubrange( parentRange ), currentBlock );
|
||||
}
|
||||
|
||||
protected Field processDeadField( HWPFDocumentCore wordDocument,
|
||||
Range charactersRange, int currentTableLevel, int startOffset,
|
||||
Element currentBlock )
|
||||
{
|
||||
if ( !( wordDocument instanceof HWPFDocument ) )
|
||||
return null;
|
||||
|
||||
HWPFDocument hwpfDocument = (HWPFDocument) wordDocument;
|
||||
Field field = hwpfDocument.getFields().getFieldByStartOffset(
|
||||
FieldsDocumentPart.MAIN, startOffset );
|
||||
if ( field == null )
|
||||
return null;
|
||||
|
||||
processField( hwpfDocument, charactersRange, currentTableLevel, field,
|
||||
currentBlock );
|
||||
|
||||
return field;
|
||||
}
|
||||
|
||||
protected abstract void processFootnoteAutonumbered( HWPFDocument doc,
|
||||
int noteIndex, Element block, Range footnoteTextRange );
|
||||
|
||||
@ -767,6 +774,11 @@ public abstract class AbstractWordConverter
|
||||
protected abstract void processTable( HWPFDocumentCore wordDocument,
|
||||
Element flow, Table table );
|
||||
|
||||
public void setFileManager( PicturesManager fileManager )
|
||||
{
|
||||
this.fileManager = fileManager;
|
||||
}
|
||||
|
||||
public void setFontReplacer( FontReplacer fontReplacer )
|
||||
{
|
||||
this.fontReplacer = fontReplacer;
|
||||
|
@ -0,0 +1,45 @@
|
||||
/* ====================================================================
|
||||
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.hwpf.converter;
|
||||
|
||||
import org.apache.poi.hwpf.usermodel.Picture;
|
||||
import org.apache.poi.hwpf.usermodel.PictureType;
|
||||
|
||||
/**
|
||||
* User-implemented pictures manager to store images on-disk
|
||||
*
|
||||
* @author Sergey Vladimirov (vlsergey {at} gmail {dot} com)
|
||||
*/
|
||||
public interface PicturesManager
|
||||
{
|
||||
/**
|
||||
* Stores image (probably on disk). Please, note that different output
|
||||
* format support different file types, so image conversion may be required.
|
||||
* For example, HTML browsers usually supports {@link PictureType#GIF},
|
||||
* {@link PictureType#JPEG}, {@link PictureType#PNG},
|
||||
* {@link PictureType#TIFF}, but rarely {@link PictureType#EMF} or
|
||||
* {@link PictureType#WMF}. FO (Apache FOP) supports at least PNG and SVG
|
||||
* types.
|
||||
*
|
||||
* @param picture
|
||||
* Word picture
|
||||
* @return path to file that can be used as reference in HTML (img's src) of
|
||||
* XLS FO (fo:external-graphic's src) or <tt>null</tt> if image were
|
||||
* not saved and should not be referenced from result HTML / FO.
|
||||
*/
|
||||
String savePicture( Picture picture );
|
||||
}
|
@ -359,12 +359,32 @@ public class WordToFoConverter extends AbstractWordConverter
|
||||
* HWPF object, contained picture data and properties
|
||||
*/
|
||||
protected void processImage( Element currentBlock, boolean inlined,
|
||||
Picture picture )
|
||||
{
|
||||
Picture picture ) {
|
||||
PicturesManager fileManager = getFileManager();
|
||||
if ( fileManager != null )
|
||||
{
|
||||
String url = fileManager.savePicture( picture );
|
||||
|
||||
if ( WordToFoUtils.isNotEmpty( url ) )
|
||||
{
|
||||
processImage( currentBlock, inlined, picture, url );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// no default implementation -- skip
|
||||
currentBlock.appendChild( foDocumentFacade.getDocument().createComment(
|
||||
"Image link to '" + picture.suggestFullFileName()
|
||||
+ "' can be here" ) );
|
||||
currentBlock.appendChild( foDocumentFacade.document
|
||||
.createComment( "Image link to '"
|
||||
+ picture.suggestFullFileName() + "' can be here" ) );
|
||||
}
|
||||
|
||||
protected void processImage( Element currentBlock, boolean inlined,
|
||||
Picture picture, String url )
|
||||
{
|
||||
final Element externalGraphic = foDocumentFacade
|
||||
.createExternalGraphic( url );
|
||||
WordToFoUtils.setPictureProperties( picture, externalGraphic );
|
||||
currentBlock.appendChild( externalGraphic );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -234,30 +234,30 @@ public class WordToFoUtils extends AbstractWordUtils
|
||||
public static void setPictureProperties( Picture picture,
|
||||
Element graphicElement )
|
||||
{
|
||||
final int aspectRatioX = picture.getAspectRatioX();
|
||||
final int aspectRatioY = picture.getAspectRatioY();
|
||||
final int horizontalScale = picture.getHorizontalScalingFactor();
|
||||
final int verticalScale = picture.getVerticalScalingFactor();
|
||||
|
||||
if ( aspectRatioX > 0 )
|
||||
if ( horizontalScale > 0 )
|
||||
{
|
||||
graphicElement
|
||||
.setAttribute( "content-width", ( ( picture.getDxaGoal()
|
||||
* aspectRatioX / 100 ) / TWIPS_PER_PT )
|
||||
* horizontalScale / 1000 ) / TWIPS_PER_PT )
|
||||
+ "pt" );
|
||||
}
|
||||
else
|
||||
graphicElement.setAttribute( "content-width",
|
||||
( picture.getDxaGoal() / TWIPS_PER_PT ) + "pt" );
|
||||
|
||||
if ( aspectRatioY > 0 )
|
||||
if ( verticalScale > 0 )
|
||||
graphicElement
|
||||
.setAttribute( "content-height", ( ( picture.getDyaGoal()
|
||||
* aspectRatioY / 100 ) / TWIPS_PER_PT )
|
||||
* verticalScale / 1000 ) / TWIPS_PER_PT )
|
||||
+ "pt" );
|
||||
else
|
||||
graphicElement.setAttribute( "content-height",
|
||||
( picture.getDyaGoal() / TWIPS_PER_PT ) + "pt" );
|
||||
|
||||
if ( aspectRatioX <= 0 || aspectRatioY <= 0 )
|
||||
if ( horizontalScale <= 0 || verticalScale <= 0 )
|
||||
{
|
||||
graphicElement.setAttribute( "scaling", "uniform" );
|
||||
}
|
||||
|
@ -315,6 +315,18 @@ public class WordToHtmlConverter extends AbstractWordConverter
|
||||
protected void processImage( Element currentBlock, boolean inlined,
|
||||
Picture picture )
|
||||
{
|
||||
PicturesManager fileManager = getFileManager();
|
||||
if ( fileManager != null )
|
||||
{
|
||||
String url = fileManager.savePicture( picture );
|
||||
|
||||
if ( WordToHtmlUtils.isNotEmpty( url ) )
|
||||
{
|
||||
processImage( currentBlock, inlined, picture, url );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// no default implementation -- skip
|
||||
currentBlock.appendChild( htmlDocumentFacade.document
|
||||
.createComment( "Image link to '"
|
||||
|
@ -44,17 +44,27 @@ public final class Picture extends PictureDescriptor
|
||||
static final int PICF_SHAPE_OFFSET = 0xE;
|
||||
static final int UNKNOWN_HEADER_SIZE = 0x49;
|
||||
|
||||
public static final byte[] GIF = new byte[]{'G', 'I', 'F'};
|
||||
public static final byte[] PNG = new byte[]{ (byte)0x89, 0x50, 0x4E, 0x47,0x0D,0x0A,0x1A,0x0A};
|
||||
public static final byte[] JPG = new byte[]{(byte)0xFF, (byte)0xD8};
|
||||
public static final byte[] BMP = new byte[]{'B', 'M'};
|
||||
public static final byte[] TIFF = new byte[]{0x49, 0x49, 0x2A, 0x00};
|
||||
public static final byte[] TIFF1 = new byte[]{0x4D, 0x4D, 0x00, 0x2A};
|
||||
@Deprecated
|
||||
public static final byte[] GIF = PictureType.GIF.getSignatures()[0];
|
||||
@Deprecated
|
||||
public static final byte[] PNG = PictureType.PNG.getSignatures()[0];
|
||||
@Deprecated
|
||||
public static final byte[] JPG = PictureType.JPEG.getSignatures()[0];
|
||||
@Deprecated
|
||||
public static final byte[] BMP = PictureType.BMP.getSignatures()[0];
|
||||
@Deprecated
|
||||
public static final byte[] TIFF = PictureType.TIFF.getSignatures()[0];
|
||||
@Deprecated
|
||||
public static final byte[] TIFF1 = PictureType.TIFF.getSignatures()[1];
|
||||
|
||||
public static final byte[] EMF = { 0x01, 0x00, 0x00, 0x00 };
|
||||
public static final byte[] WMF1 = { (byte)0xD7, (byte)0xCD, (byte)0xC6, (byte)0x9A, 0x00, 0x00 };
|
||||
public static final byte[] WMF2 = { 0x01, 0x00, 0x09, 0x00, 0x00, 0x03 }; // Windows 3.x
|
||||
// TODO: DIB, PICT
|
||||
@Deprecated
|
||||
public static final byte[] EMF = PictureType.EMF.getSignatures()[0];
|
||||
@Deprecated
|
||||
public static final byte[] WMF1 = PictureType.WMF.getSignatures()[0];
|
||||
// Windows 3.x
|
||||
@Deprecated
|
||||
public static final byte[] WMF2 = PictureType.WMF.getSignatures()[1];
|
||||
// TODO: DIB, PICT
|
||||
|
||||
public static final byte[] IHDR = new byte[]{'I', 'H', 'D', 'R'};
|
||||
|
||||
@ -103,16 +113,23 @@ public final class Picture extends PictureDescriptor
|
||||
this.size = _dataStream.length;
|
||||
}
|
||||
|
||||
private void fillWidthHeight()
|
||||
{
|
||||
String ext = suggestFileExtension();
|
||||
// trying to extract width and height from pictures content:
|
||||
if ("jpg".equalsIgnoreCase(ext)) {
|
||||
fillJPGWidthHeight();
|
||||
} else if ("png".equalsIgnoreCase(ext)) {
|
||||
fillPNGWidthHeight();
|
||||
private void fillWidthHeight()
|
||||
{
|
||||
PictureType pictureType = suggestPictureType();
|
||||
// trying to extract width and height from pictures content:
|
||||
switch ( pictureType )
|
||||
{
|
||||
case JPEG:
|
||||
fillJPGWidthHeight();
|
||||
break;
|
||||
case PNG:
|
||||
fillPNGWidthHeight();
|
||||
break;
|
||||
default:
|
||||
// unsupported;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to suggest a filename: hex representation of picture structure offset in "Data" stream plus extension that
|
||||
@ -265,76 +282,39 @@ public final class Picture extends PictureDescriptor
|
||||
}
|
||||
|
||||
/**
|
||||
* tries to suggest extension for picture's file by matching signatures of popular image formats to first bytes
|
||||
* of picture's contents
|
||||
* @return suggested file extension
|
||||
*/
|
||||
public String suggestFileExtension()
|
||||
{
|
||||
String extension = suggestFileExtension(_dataStream, pictureBytesStartOffset);
|
||||
if ("".equals(extension)) {
|
||||
// May be compressed. Get the uncompressed content and inspect that.
|
||||
extension = suggestFileExtension(getContent(), 0);
|
||||
* tries to suggest extension for picture's file by matching signatures of
|
||||
* popular image formats to first bytes of picture's contents
|
||||
*
|
||||
* @return suggested file extension
|
||||
*/
|
||||
public String suggestFileExtension()
|
||||
{
|
||||
return suggestPictureType().getExtension();
|
||||
}
|
||||
return extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mime type for the image
|
||||
*/
|
||||
public String getMimeType() {
|
||||
String extension = suggestFileExtension();
|
||||
if("jpg".equals(extension)) {
|
||||
return "image/jpeg";
|
||||
}
|
||||
if("png".equals(extension)) {
|
||||
return "image/png";
|
||||
}
|
||||
if("gif".equals(extension)) {
|
||||
return "image/gif";
|
||||
}
|
||||
if("bmp".equals(extension)) {
|
||||
return "image/bmp";
|
||||
}
|
||||
if("tiff".equals(extension)) {
|
||||
return "image/tiff";
|
||||
}
|
||||
if("wmf".equals(extension)) {
|
||||
return "image/x-wmf";
|
||||
}
|
||||
if("emf".equals(extension)) {
|
||||
return "image/x-emf";
|
||||
}
|
||||
return "image/unknown";
|
||||
}
|
||||
/**
|
||||
* Returns the MIME type for the image
|
||||
*
|
||||
* @return MIME-type for known types of image or "image/unknown" if unknown
|
||||
*/
|
||||
public String getMimeType()
|
||||
{
|
||||
return suggestPictureType().getMime();
|
||||
}
|
||||
|
||||
|
||||
private String suggestFileExtension(byte[] _dataStream, int pictureBytesStartOffset)
|
||||
{
|
||||
if (matchSignature(_dataStream, JPG, pictureBytesStartOffset)) {
|
||||
return "jpg";
|
||||
} else if (matchSignature(_dataStream, PNG, pictureBytesStartOffset)) {
|
||||
return "png";
|
||||
} else if (matchSignature(_dataStream, GIF, pictureBytesStartOffset)) {
|
||||
return "gif";
|
||||
} else if (matchSignature(_dataStream, BMP, pictureBytesStartOffset)) {
|
||||
return "bmp";
|
||||
} else if (matchSignature(_dataStream, TIFF, pictureBytesStartOffset) ||
|
||||
matchSignature(_dataStream, TIFF1, pictureBytesStartOffset)) {
|
||||
return "tiff";
|
||||
} else {
|
||||
// Need to load the image content before we can try the following tests
|
||||
public PictureType suggestPictureType()
|
||||
{
|
||||
fillImageContent();
|
||||
|
||||
if (matchSignature(content, WMF1, 0) || matchSignature(content, WMF2, 0)) {
|
||||
return "wmf";
|
||||
} else if (matchSignature(content, EMF, 0)) {
|
||||
return "emf";
|
||||
}
|
||||
for ( PictureType pictureType : PictureType.values() )
|
||||
for ( byte[] signature : pictureType.getSignatures() )
|
||||
if ( matchSignature( _dataStream, signature,
|
||||
pictureBytesStartOffset ) )
|
||||
return pictureType;
|
||||
|
||||
// TODO: DIB, PICT
|
||||
return PictureType.UNKNOWN;
|
||||
}
|
||||
// TODO: DIB, PICT
|
||||
return "";
|
||||
}
|
||||
|
||||
private static boolean matchSignature(byte[] dataStream, byte[] signature, int pictureBytesOffset)
|
||||
{
|
||||
|
@ -0,0 +1,72 @@
|
||||
/* ====================================================================
|
||||
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.hwpf.usermodel;
|
||||
|
||||
/**
|
||||
* Picture types supported by MS Word format
|
||||
*
|
||||
* @author Sergey Vladimirov (vlsergey {at} gmail {dot} com)
|
||||
*/
|
||||
public enum PictureType {
|
||||
BMP( "image/bmp", "bmp", new byte[][] { { 'B', 'M' } } ),
|
||||
|
||||
EMF( "image/x-emf", "emf", new byte[][] { { 0x01, 0x00, 0x00, 0x00 } } ),
|
||||
|
||||
GIF( "image/gif", "gif", new byte[][] { { 'G', 'I', 'F' } } ),
|
||||
|
||||
JPEG( "image/jpeg", "jpg", new byte[][] { { (byte) 0xFF, (byte) 0xD8 } } ),
|
||||
|
||||
PNG( "image/png", "png", new byte[][] { { (byte) 0x89, 0x50, 0x4E, 0x47,
|
||||
0x0D, 0x0A, 0x1A, 0x0A } } ),
|
||||
|
||||
TIFF( "image/tiff", "tiff", new byte[][] { { 0x49, 0x49, 0x2A, 0x00 },
|
||||
{ 0x4D, 0x4D, 0x00, 0x2A } } ),
|
||||
|
||||
WMF( "image/x-wmf", "wmf", new byte[][] {
|
||||
{ (byte) 0xD7, (byte) 0xCD, (byte) 0xC6, (byte) 0x9A, 0x00, 0x00 },
|
||||
{ 0x01, 0x00, 0x09, 0x00, 0x00, 0x03 } } ),
|
||||
|
||||
UNKNOWN( "image/unknown", "", new byte[][] {} );
|
||||
|
||||
private String _extension;
|
||||
|
||||
private String _mime;
|
||||
|
||||
private byte[][] _signatures;
|
||||
|
||||
private PictureType( String mime, String extension, byte[][] signatures )
|
||||
{
|
||||
this._mime = mime;
|
||||
this._extension = extension;
|
||||
this._signatures = signatures;
|
||||
}
|
||||
|
||||
public String getExtension()
|
||||
{
|
||||
return _extension;
|
||||
}
|
||||
|
||||
public String getMime()
|
||||
{
|
||||
return _mime;
|
||||
}
|
||||
|
||||
public byte[][] getSignatures()
|
||||
{
|
||||
return _signatures;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user