Java实现word文档替换,以及在指定插入图片
在开发过程中 我们可能会遇到这样的需求,给你一个word文档,可能是合同等其他,需要你把文档内容补充完整 ,把字段赋值进去或者在指定位置插入一张图片
如果有需要完成品的工具类直接拉倒最后,开箱即用
这里采用的是apache poi 的包 开源免费放心食用
<dependency>
<groupId>org.apache.poigroupId>
<artifactId>poiartifactId>
<version>4.1.0version>
dependency>
<dependency>
<groupId>org.apache.poigroupId>
<artifactId>poi-ooxmlartifactId>
<version>4.1.0version>
dependency>
<dependency>
<groupId>org.apache.poigroupId>
<artifactId>poi-ooxml-schemasartifactId>
<version>4.1.0version>
dependency>
<dependency>
<groupId>org.apache.poigroupId>
<artifactId>poi-scratchpadartifactId>
<version>4.1.0version>
dependency>
<dependency>
<groupId>org.apache.poigroupId>
<artifactId>ooxml-schemasartifactId>
<version>1.4version>
dependency>
下面开始实操给大家解决
前后效果图对比
替换前:
附上代码实例
public static void main(String[] args) {
//这个map用于存储要替换的字段
Map<String, Object> params = new HashMap<>();
params.put("name", "PG_L");
//模板的路径
String wordUrl = "D:\\TuTu\\测试.docx";
//要存储的文件的路径
String newWordUrl = "D:\\TuTu\\测试2.docx";
InputStream is = null;
try {
//读取文件
is = new FileInputStream(wordUrl);
XWPFDocument doc = new XWPFDocument(is);
//替换段落里面的变量
POIUtil.replaceInPara(doc, params);
//输出到测试2文件中
OutputStream os = new FileOutputStream(newWordUrl);
doc.write(os);
is.close();
os.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
// POIUtil工具类
/**
* 替换段落里面的变量
* @param doc 要替换的文档
* @param params 参数
*/
public static void replaceInPara(XWPFDocument doc, Map<String, Object> params) {
for(XWPFParagraph para : doc.getParagraphs()) {
replaceInPara(para, params);
}
}
/**
* 替换段落里面的变量
*
* @param paragraph 要替换的段落
* @param params 参数
*/
private static void replaceInPara(XWPFParagraph paragraph, Map<String, Object> params) {
List<XWPFRun> runs = paragraph.getRuns();
for (int i = 0; i < runs.size(); i++) {
//获取字符
String text0 = runs.get(i).getText(runs.get(i).getTextPosition());
if (text0 != null && text0.contains("$")) {
//包含占位符的字符缓存
StringBuilder cache = new StringBuilder(text0);
int endIndex = 0;
boolean contains = text0.contains("}");
//同一个run中是否包含占位符
if (!contains) {
int j = i + 1;
for (; j < runs.size(); j++) {
String text1 = runs.get(j).getText(runs.get(j).getTextPosition());
if (text1 == null) {
continue;
}
cache.append(text1);
if (text1.contains("}")) {
endIndex = j;
break;
}
}
}
if (contains || endIndex != 0) {
//处理替换
String s = cache.toString();
for (Map.Entry<String, Object> entry : params.entrySet()) {
String k = entry.getKey();
String v = entry.getValue().toString();
if (s.contains("${" + k +"}")) {
String replace = s.replace("${" + k +"}", v);
runs.get(i).setText(replace, 0);
for (int j = endIndex; j > i; j--) {
runs.get(j).setText("", 0);
}
break;
}
}
}
}
}
}
有小伙伴可能要问,屁股刘(PG_L),那我想给替换的字段指定样式怎么办?当…当然可以了,但没必要,我的建议是在创建模板的时候就预定义好字段样式属性。如果实在有这方面的诉求,我后期可以单独开一篇文章来讲。
插入图片我的建议是用书签来做
打开你的word文档,选择插入,选择书签,并命名
替换后的图片:
代码实例:
public static void main(String[] args) {
Map<String, Object> params = new HashMap<>();
params.put("name", "PG_L");
String wordUrl = "D:\\TuTu\\测试.docx";
String newWordUrl = "D:\\TuTu\\测试2.docx";
InputStream is = null;
try {
InputStream picStream = new FileInputStream("D:\\TuTu\\tutu.jpg");
params.put("mypic", picStream);
is = new FileInputStream(wordUrl);
XWPFDocument doc = new XWPFDocument(is);
//替换段落里面的变量
POIUtil.replaceInPara(doc, params);
OutputStream os = new FileOutputStream(newWordUrl);
doc.write(os);
is.close();
os.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//POIUtil
/**
* 替换段落里面的变量
* @param doc 要替换的文档
* @param params 参数
*/
public static void replaceInPara(XWPFDocument doc, Map<String, Object> params) {
for(XWPFParagraph para : doc.getParagraphs()) {
replaceInBook(para, params);
}
}
/**
* 替换段落里面的书签
*
* @param paragraph 要替换的段落
* @param params 参数
*/
private static void replaceInBook(XWPFParagraph paragraph, Map<String, Object> params) {
CTP ctp = paragraph.getCTP();
for (int dwI = 0; dwI < ctp.sizeOfBookmarkStartArray(); dwI++) {
try {
CTBookmark bookmark = ctp.getBookmarkStartArray(dwI);
if(!params.containsKey(bookmark.getName())) continue;
InputStream ins = (InputStream) params.get(bookmark.getName());
XWPFRun run = paragraph.createRun();
//bus.png为鼠标在word里选择图片时,图片显示的名字,100,100则为像素单元,根据实际需要的大小进行调整即可。
run.addPicture(ins,XWPFDocument.PICTURE_TYPE_PNG,"tutu.png,", Units.toEMU(100), Units.toEMU(100));
} catch (InvalidFormatException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
前面讲了怎么替换字段 但是那是段落里的字段,表格里的其实也类似 无非多了一段步骤:找到表格、找到行、找到单元格、再找到段落(替换表格里的图片也类似操作,不再赘述)下面看案例:
替换前:
替换后:
public static void main(String[] args) {
Map<String, Object> params = new HashMap<>();
params.put("name", "PG_L");
params.put("henshuai","比坤坤都要帅");
String wordUrl = "D:\\TuTu\\测试.docx";
String newWordUrl = "D:\\TuTu\\测试2.docx";
InputStream is = null;
try {
InputStream picStream = new FileInputStream("D:\\TuTu\\tutu.jpg");
params.put("mypic", picStream);
is = new FileInputStream(wordUrl);
XWPFDocument doc = new XWPFDocument(is);
//替换段落里面的变量
POIUtil.replaceInTable(doc, params);
OutputStream os = new FileOutputStream(newWordUrl);
doc.write(os);
is.close();
os.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//POIUtil
/**
* 替换表格里面的变量
* @param doc 要替换的文档
* @param params 参数
*/
private static void replaceInTable(XWPFDocument doc, Map<String, Object> params) {
for(XWPFTable table : doc.getTables()){
replaceInTable(table, params);
}
}
/**
* 替换表格里面的变量
* @param table 要替换的表格
* @param params 参数
*/
private static void replaceInTable(XWPFTable table, Map<String, Object> params) {
for(XWPFTableRow row : table.getRows()){
for(XWPFTableCell cell : row.getTableCells()){
for(XWPFTable secTable : cell.getTables()){
replaceInTable(secTable,params);
}
for (XWPFParagraph para : cell.getParagraphs()){
replaceInPara(para, params);
replaceInBook(para, params);
}
}
}
}
/**
* 替换段落里面的变量 这个和之前的一样 用就完事了
*
* @param paragraph 要替换的段落
* @param params 参数
*/
private static void replaceInPara(XWPFParagraph paragraph, Map<String, Object> params) {
List<XWPFRun> runs = paragraph.getRuns();
for (int i = 0; i < runs.size(); i++) {
//获取字符
String text0 = runs.get(i).getText(runs.get(i).getTextPosition());
if (text0 != null && text0.contains("$")) {
//包含占位符的字符缓存
StringBuilder cache = new StringBuilder(text0);
int endIndex = 0;
boolean contains = text0.contains("}");
//同一个run中是否包含占位符
if (!contains) {
int j = i + 1;
for (; j < runs.size(); j++) {
String text1 = runs.get(j).getText(runs.get(j).getTextPosition());
if (text1 == null) {
continue;
}
cache.append(text1);
if (text1.contains("}")) {
endIndex = j;
break;
}
}
}
if (contains || endIndex != 0) {
//处理替换
String s = cache.toString();
for (Map.Entry<String, Object> entry : params.entrySet()) {
String k = entry.getKey();
String v = entry.getValue().toString();
if (s.contains("${" + k +"}")) {
String replace = s.replace("${" + k +"}", v);
runs.get(i).setText(replace, 0);
for (int j = endIndex; j > i; j--) {
runs.get(j).setText("", 0);
}
break;
}
}
}
}
}
}
正常插入的图片会影响插入位置行的样式 ,但是如果设置了图片悬浮,就可以完美解决,不过需要自定义好图片位置,设置偏移量
在我屁股刘(PG_L)经过n轮尝试后 发现 重写apache poi下的包实现起来最最简单拉
先在自己的项目里 建一个包
org.apache.poi.xwpf.usermodel
然后把源码的XWPFRun拷贝到包里,然加上我的方法搞定
这里代码就直接展示具体插入图片的方法
//原方法
run.addPicture(ins,XWPFDocument.PICTURE_TYPE_PNG,"bus.png,", Units.toEMU(100), Units.toEMU(100));
//新方法
/**
*
* 复制原本的方法,加入了是否悬浮图片;在加入图片的偏移位置参数
* x和y的取值:1厘米=360045
* @param pictureData
* @param pictureType
* @param filename
* @param width
* @param height
* @param isXuanfu
* @param x 水平偏移位置
* @param y 垂直偏移位置
* @return
* @throws InvalidFormatException
* @throws IOException
*/
public XWPFPicture addPictureV2(InputStream pictureData, int pictureType, String filename, int width, int height,boolean isXuanfu,int x,int y)
throws InvalidFormatException, IOException {
String relationId;
XWPFPictureData picData;
// Work out what to add the picture to, then add both the
// picture and the relationship for it
// TODO Should we have an interface for this sort of thing?
if (parent.getPart() instanceof XWPFHeaderFooter) {
XWPFHeaderFooter headerFooter = (XWPFHeaderFooter) parent.getPart();
relationId = headerFooter.addPictureData(pictureData, pictureType);
picData = (XWPFPictureData) headerFooter.getRelationById(relationId);
} else {
@SuppressWarnings("resource")
XWPFDocument doc = parent.getDocument();
relationId = doc.addPictureData(pictureData, pictureType);
picData = (XWPFPictureData) doc.getRelationById(relationId);
}
// Create the drawing entry for it
try {
CTDrawing drawing = run.addNewDrawing();
// CTInline inline = drawing.addNewInline();
//*************修改 zhengmeng
CTAnchor inline = drawing.addNewAnchor();
// Do the fiddly namespace bits on the inline
// (We need full control of what goes where and as what)
String xml =
" + CTGraphicalObject.type.getName().getNamespaceURI() + "\">" +
" + CTPicture.type.getName().getNamespaceURI() + "\">" +
" + CTPicture.type.getName().getNamespaceURI() + "\" />" +
" " +
" ";
InputSource is = new InputSource(new StringReader(xml));
org.w3c.dom.Document doc = DocumentHelper.readDocument(is);
inline.set(XmlToken.Factory.parse(doc.getDocumentElement(), DEFAULT_XML_OPTIONS));
// Setup the inline
inline.setDistT(0);
inline.setDistR(0);
inline.setDistB(0);
inline.setDistL(0);
//*********************修改zhengmeng**********************************
inline.setSimplePos2(false);
inline.setRelativeHeight(251658240);//查了文档 ,也试过,没找到作用位置,先放着
inline.setBehindDoc(isXuanfu);//重点,浮于文字上方
inline.setLocked(false);
inline.setLayoutInCell(true);
inline.setAllowOverlap(true);
CTPoint2D simplePost = inline.addNewSimplePos();
simplePost.setX(0);
simplePost.setY(0);
CTPosH ph = inline.addNewPositionH();
ph.setRelativeFrom(STRelFromH.PAGE);//以整个页面为准的定位
ph.setPosOffset(x);//水平位置,针对RelativeFrom的一个偏移 1厘米=360045 ,1.1*360045
CTPosV pv = inline.addNewPositionV();
pv.setRelativeFrom(STRelFromV.PAGE);
pv.setPosOffset(y);//竖直位置,针对RelativeFrom的一个偏移 1.3*360045
/**
* 这个是此元素指定的其他程度应添加到每个边缘 (顶部、 底部、 左、 右)
* 以补偿应用于 DrawingML 对象的任何图形效果的图像
* 但目前好像用不到
*/
CTEffectExtent efextent = inline.addNewEffectExtent();
efextent.setL(19050);
efextent.setT(0);
efextent.setR(0);
efextent.setB(0);
CTWrapNone wn = inline.addNewWrapNone();
CTNonVisualGraphicFrameProperties fp = inline.addNewCNvGraphicFramePr();
//*******************************************************
CTNonVisualDrawingProps docPr = inline.addNewDocPr();
long id = getParent().getDocument().getDrawingIdManager().reserveNew();
docPr.setId(id);
/* This name is not visible in Word 2010 anywhere. */
docPr.setName("Drawing " + id);
docPr.setDescr(filename);
CTPositiveSize2D extent = inline.addNewExtent();
extent.setCx(width);
extent.setCy(height);
// Grab the picture object
CTGraphicalObject graphic = inline.getGraphic();
CTGraphicalObjectData graphicData = graphic.getGraphicData();
CTPicture pic = getCTPictures(graphicData).get(0);
// Set it up
CTPictureNonVisual nvPicPr = pic.addNewNvPicPr();
CTNonVisualDrawingProps cNvPr = nvPicPr.addNewCNvPr();
/* use "0" for the id. See ECM-576, 20.2.2.3 */
cNvPr.setId(0L);
/* This name is not visible in Word 2010 anywhere */
cNvPr.setName("Picture " + id);
cNvPr.setDescr(filename);
CTNonVisualPictureProperties cNvPicPr = nvPicPr.addNewCNvPicPr();
cNvPicPr.addNewPicLocks().setNoChangeAspect(true);
CTBlipFillProperties blipFill = pic.addNewBlipFill();
CTBlip blip = blipFill.addNewBlip();
blip.setEmbed(parent.getPart().getRelationId(picData));
/******** 增加 zhengmeng***********/
blip.setCstate(STBlipCompression.PRINT);//压缩状态
blipFill.addNewStretch().addNewFillRect();
CTShapeProperties spPr = pic.addNewSpPr();
CTTransform2D xfrm = spPr.addNewXfrm();
CTPoint2D off = xfrm.addNewOff();
off.setX(0);
off.setY(0);
CTPositiveSize2D ext = xfrm.addNewExt();
ext.setCx(width);
ext.setCy(height);
CTPresetGeometry2D prstGeom = spPr.addNewPrstGeom();
prstGeom.setPrst(STShapeType.RECT);
prstGeom.addNewAvLst();
// Finish up
XWPFPicture xwpfPicture = new XWPFPicture(pic, this);
pictures.add(xwpfPicture);
return xwpfPicture;
} catch (XmlException | SAXException e) {
throw new IllegalStateException(e);
}
}
这样基本的word替换就实现完了 接下来附上工具类
public class POIUtil {
/**
* 用一个docx文档作为模板,然后替换其中的内容,再写入目标文档中。
* @throws Exception
*/
public static ByteArrayOutputStream templateWrite(String filePath/*, String outFilePath*/, Map<String, Object> params) throws Exception {
InputStream is = new FileInputStream(filePath);
XWPFDocument doc = new XWPFDocument(is);
//替换段落里面的变量
replaceInPara(doc, params);
//替换表格里面的变量
replaceInTable(doc, params);
//OutputStream os = new FileOutputStream(outFilePath);
ByteArrayOutputStream os = new ByteArrayOutputStream();//二进制OutputStream
doc.write(os);
close(os);
close(is);
return os;
}
/**
* 替换段落里面的书签
*
* @param paragraph 要替换的段落
* @param params 参数
*/
private static void replaceInBook(XWPFParagraph paragraph, Map<String, Object> params) {
if(params == null){
return;
}
CTP ctp = paragraph.getCTP();
for (int dwI = 0; dwI < ctp.sizeOfBookmarkStartArray(); dwI++) {
try {
CTBookmark bookmark = ctp.getBookmarkStartArray(dwI);
if(!params.containsKey(bookmark.getName())) continue;
PicIuputStreamModel model = (PicIuputStreamModel) params.get(bookmark.getName());
InputStream picIs = model.getIs();
if(picIs == null) continue;
XWPFRun run = paragraph.createRun();
//bus.png为鼠标在word里选择图片时,图片显示的名字,400,400则为像素单元,根据实际需要的大小进行调整即可。
run.addPictureV2(picIs,XWPFDocument.PICTURE_TYPE_PNG,"bus.png,", Units.toEMU(model.getWidth()), Units.toEMU(model.getHeight()),true,(int)(model.getLeftOffset()*360045),(int)(model.getTopOffset()*360045));
} catch (InvalidFormatException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 替换段落里面的变量
* @param doc 要替换的文档
* @param params 参数
*/
private static void replaceInPara(XWPFDocument doc, Map<String, Object> params) {
for(XWPFParagraph para : doc.getParagraphs()) {
replaceInPara(para, params);
replaceInBook(para, params);
}
}
/**
* 替换段落里面的变量
*
* @param paragraph 要替换的段落
* @param params 参数
*/
private static void replaceInPara(XWPFParagraph paragraph, Map<String, Object> params) {
if(params == null){
return;
}
List<XWPFRun> runs = paragraph.getRuns();
for (int i = 0; i < runs.size(); i++) {
//获取字符
String text0 = runs.get(i).getText(runs.get(i).getTextPosition());
if (text0 != null && text0.contains("$")) {
//包含占位符的字符缓存
StringBuilder cache = new StringBuilder(text0);
int endIndex = 0;
boolean contains = text0.contains("}");
//同一个run中是否包含占位符
if (!contains) {
int j = i + 1;
for (; j < runs.size(); j++) {
String text1 = runs.get(j).getText(runs.get(j).getTextPosition());
if (text1 == null) {
continue;
}
cache.append(text1);
if (text1.contains("}")) {
endIndex = j;
break;
}
}
}
if (contains || endIndex != 0) {
//处理替换
String s = cache.toString();
//System.out.println("替换字符:"+s);
List<String> keys = get$Value(s);
for (String key : keys) {
String value = "";
if(params.containsKey(key)){
value = params.get(key).toString();
}
String replace = s.replace("${" + key +"}", value);
runs.get(i).setText(replace, 0);
for (int j = endIndex; j > i; j--) {
runs.get(j).setText("", 0);
}
}
// for (Map.Entry entry : params.entrySet()) {
// String k = entry.getKey();
// String v = entry.getValue().toString();
// if (s.contains("${" + k +"}")) {
// String replace = s.replace("${" + k +"}", v);
// runs.get(i).setText(replace, 0);
// for (int j = endIndex; j > i; j--) {
// runs.get(j).setText("", 0);
// }
//
//
// break;
// }
// }
}
}
}
}
/**
* 替换表格里面的变量
* @param doc 要替换的文档
* @param params 参数
*/
private static void replaceInTable(XWPFDocument doc, Map<String, Object> params) {
for(XWPFTable table : doc.getTables()){
replaceInTable(table, params);
}
}
/**
* 替换表格里面的变量
* @param table 要替换的表格
* @param params 参数
*/
private static void replaceInTable(XWPFTable table, Map<String, Object> params) {
for(XWPFTableRow row : table.getRows()){
for(XWPFTableCell cell : row.getTableCells()){
for(XWPFTable secTable : cell.getTables()){
replaceInTable(secTable,params);
}
for (XWPFParagraph para : cell.getParagraphs()){
replaceInPara(para, params);
replaceInBook(para, params);
}
}
}
}
public static List<String> get$Value(String str) {
List<String> variables = new ArrayList<>();
Pattern pattern = Pattern.compile("\\$\\{([^}]+)}");
Matcher matcher = pattern.matcher(str);
while (matcher.find()) {
variables.add(matcher.group(1));
}
return variables;
}
/**
* 关闭输入流
* @param is
*/
private static void close(InputStream is) {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 关闭输出流
* @param os
*/
private static void close(OutputStream os) {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public class XWPFRun implements ISDTContents, IRunElement, CharacterRun {
private CTR run;
private String pictureText;
private IRunBody parent;
private List<XWPFPicture> pictures;
/**
* @param r the CTR bean which holds the run attributes
* @param p the parent paragraph
*/
public XWPFRun(CTR r, IRunBody p) {
this.run = r;
this.parent = p;
/*
* reserve already occupied drawing ids, so reserving new ids later will
* not corrupt the document
*/
for (CTDrawing ctDrawing : r.getDrawingArray()) {
for (CTAnchor anchor : ctDrawing.getAnchorArray()) {
if (anchor.getDocPr() != null) {
getDocument().getDrawingIdManager().reserve(anchor.getDocPr().getId());
}
}
for (CTInline inline : ctDrawing.getInlineArray()) {
if (inline.getDocPr() != null) {
getDocument().getDrawingIdManager().reserve(inline.getDocPr().getId());
}
}
}
// Look for any text in any of our pictures or drawings
StringBuilder text = new StringBuilder();
List<XmlObject> pictTextObjs = new ArrayList<>();
pictTextObjs.addAll(Arrays.asList(r.getPictArray()));
pictTextObjs.addAll(Arrays.asList(r.getDrawingArray()));
for (XmlObject o : pictTextObjs) {
XmlObject[] ts = o.selectPath("declare namespace w='http://schemas.openxmlformats.org/wordprocessingml/2006/main' .//w:t");
for (XmlObject t : ts) {
NodeList kids = t.getDomNode().getChildNodes();
for (int n = 0; n < kids.getLength(); n++) {
if (kids.item(n) instanceof Text) {
if (text.length() > 0) {
text.append("\n");
}
text.append(kids.item(n).getNodeValue());
}
}
}
}
pictureText = text.toString();
// Do we have any embedded pictures?
// (They're a different CTPicture, under the drawingml namespace)
pictures = new ArrayList<>();
for (XmlObject o : pictTextObjs) {
for (CTPicture pict : getCTPictures(o)) {
XWPFPicture picture = new XWPFPicture(pict, this);
pictures.add(picture);
}
}
}
/**
* @deprecated Use {@link XWPFRun#XWPFRun(CTR, IRunBody)}
*/
@Deprecated
public XWPFRun(CTR r, XWPFParagraph p) {
this(r, (IRunBody) p);
}
/**
* Add the xml:spaces="preserve" attribute if the string has leading or trailing white spaces
*
* @param xs the string to check
*/
static void preserveSpaces(XmlString xs) {
String text = xs.getStringValue();
if (text != null && (text.startsWith(" ") || text.endsWith(" "))) {
XmlCursor c = xs.newCursor();
c.toNextToken();
c.insertAttributeWithValue(new QName("http://www.w3.org/XML/1998/namespace", "space"), "preserve");
c.dispose();
}
}
private List<CTPicture> getCTPictures(XmlObject o) {
List<CTPicture> pics = new ArrayList<>();
XmlObject[] picts = o.selectPath("declare namespace pic='" + CTPicture.type.getName().getNamespaceURI() + "' .//pic:pic");
for (XmlObject pict : picts) {
if (pict instanceof XmlAnyTypeImpl) {
// Pesky XmlBeans bug - see Bugzilla #49934
try {
pict = CTPicture.Factory.parse(pict.toString(), DEFAULT_XML_OPTIONS);
} catch (XmlException e) {
throw new POIXMLException(e);
}
}
if (pict instanceof CTPicture) {
pics.add((CTPicture) pict);
}
}
return pics;
}
/**
* Get the currently used CTR object
*
* @return ctr object
*/
@Internal
public CTR getCTR() {
return run;
}
/**
* Get the currently referenced paragraph/SDT object
*
* @return current parent
*/
public IRunBody getParent() {
return parent;
}
/**
* Get the currently referenced paragraph, or null if a SDT object
*
* @deprecated use {@link XWPFRun#getParent()} instead
*/
@Deprecated
public XWPFParagraph getParagraph() {
if (parent instanceof XWPFParagraph) {
return (XWPFParagraph) parent;
}
return null;
}
/**
* @return The {@link XWPFDocument} instance, this run belongs to, or
* null
if parent structure (paragraph > document) is not properly set.
*/
public XWPFDocument getDocument() {
if (parent != null) {
return parent.getDocument();
}
return null;
}
/**
* For isBold, isItalic etc
*/
private static boolean isCTOnOff(CTOnOff onoff) {
if (!onoff.isSetVal()) {
return true;
}
final STOnOff.Enum val = onoff.getVal();
return (
(STOnOff.TRUE == val) ||
(STOnOff.X_1 == val) ||
(STOnOff.ON == val)
);
}
/**
* Get the language tag associated with this run, if any.
*
* @return the language tag associated with this run, if any
*/
public String getLang() {
CTRPr pr = getRunProperties(false);
Object lang = pr == null || !pr.isSetLang() ? null : pr.getLang().getVal();
return (String) lang;
}
/**
* Set the language tag associated with this run.
*
* @param lang the language tag associated with this run
* @since 4.1.0
*/
public void setLang(String lang) {
CTRPr pr = getRunProperties(true);
CTLanguage ctLang = pr.isSetLang() ? pr.getLang() : pr.addNewLang();
ctLang.setVal(lang);
}
/**
* Whether the bold property shall be applied to all non-complex script
* characters in the contents of this run when displayed in a document
*
* @return true
if the bold property is applied
*/
@Override
public boolean isBold() {
CTRPr pr = getRunProperties(false);
return pr != null && pr.isSetB() && isCTOnOff(pr.getB());
}
/**
* Whether the bold property shall be applied to all non-complex script
* characters in the contents of this run when displayed in a document.
*
* This formatting property is a toggle property, which specifies that its
* behavior differs between its use within a style definition and its use as
* direct formatting. When used as part of a style definition, setting this
* property shall toggle the current state of that property as specified up
* to this point in the hierarchy (i.e. applied to not applied, and vice
* versa). Setting it to false
(or an equivalent) shall
* result in the current setting remaining unchanged. However, when used as
* direct formatting, setting this property to true or false shall set the
* absolute state of the resulting property.
*
*
* If this element is not present, the default value is to leave the
* formatting applied at previous level in the style hierarchy. If this
* element is never applied in the style hierarchy, then bold shall not be
* applied to non-complex script characters.
*
*
* @param value true
if the bold property is applied to
* this run
*/
@Override
public void setBold(boolean value) {
CTRPr pr = getRunProperties(true);
CTOnOff bold = pr.isSetB() ? pr.getB() : pr.addNewB();
bold.setVal(value ? STOnOff.TRUE : STOnOff.FALSE);
}
/**
* Get text color. The returned value is a string in the hex form "RRGGBB".
*/
public String getColor() {
String color = null;
if (run.isSetRPr()) {
CTRPr pr = getRunProperties(false);
if (pr != null && pr.isSetColor()) {
CTColor clr = pr.getColor();
color = clr.xgetVal().getStringValue();
}
}
return color;
}
/**
* Set text color.
*
* @param rgbStr - the desired color, in the hex form "RRGGBB".
*/
public void setColor(String rgbStr) {
CTRPr pr = getRunProperties(true);
CTColor color = pr.isSetColor() ? pr.getColor() : pr.addNewColor();
color.setVal(rgbStr);
}
/**
* Return the string content of this text run
*
* @return the text of this text run or null
if not set
*/
public String getText(int pos) {
return run.sizeOfTArray() == 0 ? null : run.getTArray(pos)
.getStringValue();
}
/**
* Returns text embedded in pictures
*/
public String getPictureText() {
return pictureText;
}
/**
* Sets the text of this text run
*
* @param value the literal text which shall be displayed in the document
*/
public void setText(String value) {
setText(value, run.sizeOfTArray());
}
/**
* Sets the text of this text run in the
*
* @param value the literal text which shall be displayed in the document
* @param pos - position in the text array (NB: 0 based)
*/
public void setText(String value, int pos) {
if (pos > run.sizeOfTArray()) {
throw new ArrayIndexOutOfBoundsException("Value too large for the parameter position in XWPFRun.setText(String value,int pos)");
}
CTText t = (pos < run.sizeOfTArray() && pos >= 0) ? run.getTArray(pos) : run.addNewT();
t.setStringValue(value);
preserveSpaces(t);
}
/**
* Whether the italic property should be applied to all non-complex script
* characters in the contents of this run when displayed in a document.
*
* @return true
if the italic property is applied
*/
@Override
public boolean isItalic() {
CTRPr pr = getRunProperties(false);
return pr != null && pr.isSetI() && isCTOnOff(pr.getI());
}
/**
* Whether the bold property shall be applied to all non-complex script
* characters in the contents of this run when displayed in a document
*
*
* This formatting property is a toggle property, which specifies that its
* behavior differs between its use within a style definition and its use as
* direct formatting. When used as part of a style definition, setting this
* property shall toggle the current state of that property as specified up
* to this point in the hierarchy (i.e. applied to not applied, and vice
* versa). Setting it to false
(or an equivalent) shall
* result in the current setting remaining unchanged. However, when used as
* direct formatting, setting this property to true or false shall set the
* absolute state of the resulting property.
*
*
* If this element is not present, the default value is to leave the
* formatting applied at previous level in the style hierarchy. If this
* element is never applied in the style hierarchy, then bold shall not be
* applied to non-complex script characters.
*
*
* @param value true
if the italic property is applied to
* this run
*/
@Override
public void setItalic(boolean value) {
CTRPr pr = getRunProperties(true);
CTOnOff italic = pr.isSetI() ? pr.getI() : pr.addNewI();
italic.setVal(value ? STOnOff.TRUE : STOnOff.FALSE);
}
/**
* Get the underline setting for the run.
*
* @return the Underline pattern applied to this run
* @see (@link UnderlinePatterns}
*/
public UnderlinePatterns getUnderline() {
UnderlinePatterns value = UnderlinePatterns.NONE;
CTUnderline underline = getCTUnderline(false);
if (underline != null) {
STUnderline.Enum baseValue = underline.getVal();
if (baseValue != null) {
value = UnderlinePatterns.valueOf(baseValue.intValue());
}
}
return value;
}
/**
* Specifies that the contents of this run should be displayed along with an
* underline appearing directly below the character height.
*
* If this element is not present, the default value is to leave the
* formatting applied at previous level in the style hierarchy. If this
* element is never applied in the style hierarchy, then an underline shall
* not be applied to the contents of this run.
*
*
* @param value -
* underline type
* @see {@link UnderlinePatterns} : all possible patterns that could be applied
*/
public void setUnderline(UnderlinePatterns value) {
CTUnderline underline = getCTUnderline(true);
underline.setVal(STUnderline.Enum.forInt(value.getValue()));
}
/**
* Get the CTUnderline for the run.
* @param create Create a new underline if necessary
* @return The underline, or null create is false and there is no underline.
*/
private CTUnderline getCTUnderline(boolean create) {
CTRPr pr = getRunProperties(true);
CTUnderline underline = pr.getU();
if (create && underline == null) {
underline = pr.addNewU();
}
return underline;
}
/**
* Set the underline color for the run's underline, if any.
*
* @param color An RGB color value (e.g, "a0C6F3") or "auto".
* @since 4.0.0
*/
public void setUnderlineColor(String color) {
CTUnderline underline = getCTUnderline(true);
SimpleValue svColor = null;
if (color.equals("auto")) {
STHexColorAuto hexColor = STHexColorAuto.Factory.newInstance();
hexColor.set(STHexColorAuto.Enum.forString(color));
svColor = (SimpleValue) hexColor;
} else {
STHexColorRGB rgbColor = STHexColorRGB.Factory.newInstance();
rgbColor.setStringValue(color);
svColor = (SimpleValue) rgbColor;
}
underline.setColor(svColor);
}
/**
* Set the underline theme color for the run's underline, if any.
*
* @param themeColor A theme color name (see {@link STThemeColor.Enum}).
* @since 4.0.0
*/
public void setUnderlineThemeColor(String themeColor) {
CTUnderline underline = getCTUnderline(true);
STThemeColor.Enum val = STThemeColor.Enum.forString(themeColor);
if (val != null) {
underline.setThemeColor(val);
}
}
/**
* Get the underline theme color for the run's underline, if any.
*
* @return The {@link STThemeColor.Enum}.
* @since 4.0.0
*/
public STThemeColor.Enum getUnderlineThemeColor() {
CTUnderline underline = getCTUnderline(false);
STThemeColor.Enum color = STThemeColor.NONE;
if (underline != null) {
color = underline.getThemeColor();
}
return color;
}
/**
* Get the underline color for the run's underline, if any.
*
* @return The RGB color value as as a string of hexadecimal digits (e.g., "A0B2F1") or "auto".
* @since 4.0.0
*/
public String getUnderlineColor() {
CTUnderline underline = getCTUnderline(true);
String colorName = "auto";
Object rawValue = underline.getColor();
if (rawValue != null) {
if (rawValue instanceof String) {
colorName = (String)rawValue;
} else {
byte[] rgbColor = (byte[])rawValue;
colorName = HexDump.toHex(rgbColor[0]) + HexDump.toHex(rgbColor[1]) + HexDump.toHex(rgbColor[2]);
}
}
return colorName;
}
/**
* Specifies that the contents of this run shall be displayed with a single
* horizontal line through the center of the line.
*
* @return true
if the strike property is applied
*/
@Override
public boolean isStrikeThrough() {
CTRPr pr = getRunProperties(false);
return pr != null && pr.isSetStrike() && isCTOnOff(pr.getStrike());
}
/**
* Specifies that the contents of this run shall be displayed with a single
* horizontal line through the center of the line.
*
* This formatting property is a toggle property, which specifies that its
* behaviour differs between its use within a style definition and its use as
* direct formatting. When used as part of a style definition, setting this
* property shall toggle the current state of that property as specified up
* to this point in the hierarchy (i.e. applied to not applied, and vice
* versa). Setting it to false (or an equivalent) shall result in the
* current setting remaining unchanged. However, when used as direct
* formatting, setting this property to true or false shall set the absolute
* state of the resulting property.
*
*
* If this element is not present, the default value is to leave the
* formatting applied at previous level in the style hierarchy. If this
* element is never applied in the style hierarchy, then strikethrough shall
* not be applied to the contents of this run.
*
*
* @param value true
if the strike property is applied to
* this run
*/
@Override
public void setStrikeThrough(boolean value) {
CTRPr pr = getRunProperties(true);
CTOnOff strike = pr.isSetStrike() ? pr.getStrike() : pr.addNewStrike();
strike.setVal(value ? STOnOff.TRUE : STOnOff.FALSE);
}
@Deprecated
public boolean isStrike() {
return isStrikeThrough();
}
@Deprecated
public void setStrike(boolean value) {
setStrikeThrough(value);
}
/**
* Specifies that the contents of this run shall be displayed with a double
* horizontal line through the center of the line.
*
* @return true
if the double strike property is applied
*/
@Override
public boolean isDoubleStrikeThrough() {
CTRPr pr = getRunProperties(false);
return pr != null && pr.isSetDstrike() && isCTOnOff(pr.getDstrike());
}
/**
* Specifies that the contents of this run shall be displayed with a
* double horizontal line through the center of the line.
*
* @see #setStrikeThrough(boolean) for the rules about this
*/
@Override
public void setDoubleStrikethrough(boolean value) {
CTRPr pr = getRunProperties(true);
CTOnOff dstrike = pr.isSetDstrike() ? pr.getDstrike() : pr.addNewDstrike();
dstrike.setVal(value ? STOnOff.TRUE : STOnOff.FALSE);
}
@Override
public boolean isSmallCaps() {
CTRPr pr = getRunProperties(false);
return pr != null && pr.isSetSmallCaps() && isCTOnOff(pr.getSmallCaps());
}
@Override
public void setSmallCaps(boolean value) {
CTRPr pr = getRunProperties(true);
CTOnOff caps = pr.isSetSmallCaps() ? pr.getSmallCaps() : pr.addNewSmallCaps();
caps.setVal(value ? STOnOff.TRUE : STOnOff.FALSE);
}
@Override
public boolean isCapitalized() {
CTRPr pr = getRunProperties(false);
return pr != null && pr.isSetCaps() && isCTOnOff(pr.getCaps());
}
@Override
public void setCapitalized(boolean value) {
CTRPr pr = getRunProperties(true);
CTOnOff caps = pr.isSetCaps() ? pr.getCaps() : pr.addNewCaps();
caps.setVal(value ? STOnOff.TRUE : STOnOff.FALSE);
}
@Override
public boolean isShadowed() {
CTRPr pr = getRunProperties(false);
return pr != null && pr.isSetShadow() && isCTOnOff(pr.getShadow());
}
@Override
public void setShadow(boolean value) {
CTRPr pr = getRunProperties(true);
CTOnOff shadow = pr.isSetShadow() ? pr.getShadow() : pr.addNewShadow();
shadow.setVal(value ? STOnOff.TRUE : STOnOff.FALSE);
}
@Override
public boolean isImprinted() {
CTRPr pr = getRunProperties(false);
return pr != null && pr.isSetImprint() && isCTOnOff(pr.getImprint());
}
@Override
public void setImprinted(boolean value) {
CTRPr pr = getRunProperties(true);
CTOnOff imprinted = pr.isSetImprint() ? pr.getImprint() : pr.addNewImprint();
imprinted.setVal(value ? STOnOff.TRUE : STOnOff.FALSE);
}
@Override
public boolean isEmbossed() {
CTRPr pr = getRunProperties(false);
return pr != null && pr.isSetEmboss() && isCTOnOff(pr.getEmboss());
}
@Override
public void setEmbossed(boolean value) {
CTRPr pr = getRunProperties(true);
CTOnOff emboss = pr.isSetEmboss() ? pr.getEmboss() : pr.addNewEmboss();
emboss.setVal(value ? STOnOff.TRUE : STOnOff.FALSE);
}
/**
* Specifies the alignment which shall be applied to the contents of this
* run in relation to the default appearance of the run's text.
* This allows the text to be repositioned as subscript or superscript without
* altering the font size of the run properties.
*
* @return VerticalAlign
* @see {@link VerticalAlign} all possible value that could be applyed to this run
* @deprecated use {@link XWPFRun.getVerticalAlignment}
*/
@Removal(version = "4.2")
public VerticalAlign getSubscript() {
CTRPr pr = getRunProperties(false);
return (pr != null && pr.isSetVertAlign()) ? VerticalAlign.valueOf(pr.getVertAlign().getVal().intValue()) : VerticalAlign.BASELINE;
}
/**
* Specifies the alignment which shall be applied to the contents of this
* run in relation to the default appearance of the run's text. This allows
* the text to be repositioned as subscript or superscript without altering
* the font size of the run properties.
*
* If this element is not present, the default value is to leave the
* formatting applied at previous level in the style hierarchy. If this
* element is never applied in the style hierarchy, then the text shall not
* be subscript or superscript relative to the default baseline location for
* the contents of this run.
*
*
* @param valign Type of vertical align to apply
* @see VerticalAlign
*/
public void setSubscript(VerticalAlign valign) {
CTRPr pr = getRunProperties(true);
CTVerticalAlignRun ctValign = pr.isSetVertAlign() ? pr.getVertAlign() : pr.addNewVertAlign();
ctValign.setVal(STVerticalAlignRun.Enum.forInt(valign.getValue()));
}
@Override
public int getKerning() {
CTRPr pr = getRunProperties(false);
if (pr == null || !pr.isSetKern()) {
return 0;
}
return pr.getKern().getVal().intValue();
}
@Override
public void setKerning(int kern) {
CTRPr pr = getRunProperties(true);
CTHpsMeasure kernmes = pr.isSetKern() ? pr.getKern() : pr.addNewKern();
kernmes.setVal(BigInteger.valueOf(kern));
}
@Override
public boolean isHighlighted() {
CTRPr pr = getRunProperties(false);
if (pr == null || !pr.isSetHighlight()) {
return false;
}
STHighlightColor.Enum val = pr.getHighlight().getVal();
if (val == null || val == STHighlightColor.NONE) {
return false;
}
return true;
}
// TODO Provide a wrapper round STHighlightColor, then expose getter/setter
// for the highlight colour. Ideally also then add to CharacterRun interface
@Override
public int getCharacterSpacing() {
CTRPr pr = getRunProperties(false);
if (pr == null || !pr.isSetSpacing()) {
return 0;
}
return pr.getSpacing().getVal().intValue();
}
@Override
public void setCharacterSpacing(int twips) {
CTRPr pr = getRunProperties(true);
CTSignedTwipsMeasure spc = pr.isSetSpacing() ? pr.getSpacing() : pr.addNewSpacing();
spc.setVal(BigInteger.valueOf(twips));
}
/**
* Gets the fonts which shall be used to display the text contents of
* this run. Specifies a font which shall be used to format all characters
* in the ASCII range (0 - 127) within the parent run
*
* @return a string representing the font family
*/
public String getFontFamily() {
return getFontFamily(null);
}
/**
* Specifies the fonts which shall be used to display the text contents of
* this run. Specifies a font which shall be used to format all characters
* in the ASCII range (0 - 127) within the parent run.
*
* Also sets the other font ranges, if they haven't been set before
*
* @param fontFamily The font family to apply
* @see FontCharRange
*/
public void setFontFamily(String fontFamily) {
setFontFamily(fontFamily, null);
}
/**
* Alias for {@link #getFontFamily()}
*/
@Override
public String getFontName() {
return getFontFamily();
}
/**
* Gets the font family for the specified font char range.
* If fcr is null, the font char range "ascii" is used
*
* @param fcr the font char range, defaults to "ansi"
* @return a string representing the font famil
*/
public String getFontFamily(FontCharRange fcr) {
CTRPr pr = getRunProperties(false);
if (pr == null || !pr.isSetRFonts()) {
return null;
}
CTFonts fonts = pr.getRFonts();
switch (fcr == null ? FontCharRange.ascii : fcr) {
default:
case ascii:
return fonts.getAscii();
case cs:
return fonts.getCs();
case eastAsia:
return fonts.getEastAsia();
case hAnsi:
return fonts.getHAnsi();
}
}
/**
* Specifies the fonts which shall be used to display the text contents of
* this run. The default handling for fcr == null is to overwrite the
* ascii font char range with the given font family and also set all not
* specified font ranges
*
* @param fontFamily The font family to apply
* @param fcr FontCharRange or null for default handling
*/
public void setFontFamily(String fontFamily, FontCharRange fcr) {
CTRPr pr = getRunProperties(true);
CTFonts fonts = pr.isSetRFonts() ? pr.getRFonts() : pr.addNewRFonts();
if (fcr == null) {
fonts.setAscii(fontFamily);
if (!fonts.isSetHAnsi()) {
fonts.setHAnsi(fontFamily);
}
if (!fonts.isSetCs()) {
fonts.setCs(fontFamily);
}
if (!fonts.isSetEastAsia()) {
fonts.setEastAsia(fontFamily);
}
} else {
switch (fcr) {
case ascii:
fonts.setAscii(fontFamily);
break;
case cs:
fonts.setCs(fontFamily);
break;
case eastAsia:
fonts.setEastAsia(fontFamily);
break;
case hAnsi:
fonts.setHAnsi(fontFamily);
break;
}
}
}
/**
* Specifies the font size which shall be applied to all non complex script
* characters in the contents of this run when displayed.
*
* @return value representing the font size
*/
@Override
public int getFontSize() {
CTRPr pr = getRunProperties(false);
return (pr != null && pr.isSetSz()) ? pr.getSz().getVal().divide(new BigInteger("2")).intValue() : -1;
}
/**
* Specifies the font size which shall be applied to all non complex script
* characters in the contents of this run when displayed.
*
* If this element is not present, the default value is to leave the value
* applied at previous level in the style hierarchy. If this element is
* never applied in the style hierarchy, then any appropriate font size may
* be used for non complex script characters.
*
*
* @param size The font size as number of point measurements.
*/
@Override
public void setFontSize(int size) {
BigInteger bint = new BigInteger(Integer.toString(size));
CTRPr pr = getRunProperties(true);
CTHpsMeasure ctSize = pr.isSetSz() ? pr.getSz() : pr.addNewSz();
ctSize.setVal(bint.multiply(new BigInteger("2")));
}
/**
* This element specifies the amount by which text shall be raised or
* lowered for this run in relation to the default baseline of the
* surrounding non-positioned text. This allows the text to be repositioned
* without altering the font size of the contents.
*
* @return a big integer representing the amount of text shall be "moved"
*/
public int getTextPosition() {
CTRPr pr = getRunProperties(false);
return (pr != null && pr.isSetPosition()) ? pr.getPosition().getVal().intValue()
: -1;
}
/**
* This element specifies the amount by which text shall be raised or
* lowered for this run in relation to the default baseline of the
* surrounding non-positioned text. This allows the text to be repositioned
* without altering the font size of the contents.
*
* If the val attribute is positive, then the parent run shall be raised
* above the baseline of the surrounding text by the specified number of
* half-points. If the val attribute is negative, then the parent run shall
* be lowered below the baseline of the surrounding text by the specified
* number of half-points.
*
*
* If this element is not present, the default value is to leave the
* formatting applied at previous level in the style hierarchy. If this
* element is never applied in the style hierarchy, then the text shall not
* be raised or lowered relative to the default baseline location for the
* contents of this run.
*
*
* @param val Positive values will raise the baseline of the text, negative
* values will lower it.
*/
public void setTextPosition(int val) {
BigInteger bint = new BigInteger(Integer.toString(val));
CTRPr pr = getRunProperties(true);
CTSignedHpsMeasure position = pr.isSetPosition() ? pr.getPosition() : pr.addNewPosition();
position.setVal(bint);
}
/**
*
*/
public void removeBreak() {
// TODO
}
/**
* Specifies that a break shall be placed at the current location in the run
* content.
* A break is a special character which is used to override the
* normal line breaking that would be performed based on the normal layout
* of the document's contents.
*
* @see #addCarriageReturn()
*/
public void addBreak() {
run.addNewBr();
}
/**
* Specifies that a break shall be placed at the current location in the run
* content.
* A break is a special character which is used to override the
* normal line breaking that would be performed based on the normal layout
* of the document's contents.
*
* The behavior of this break character (the
* location where text shall be restarted after this break) shall be
* determined by its type values.
*
*
* @see BreakType
*/
public void addBreak(BreakType type) {
CTBr br = run.addNewBr();
br.setType(STBrType.Enum.forInt(type.getValue()));
}
/**
* Specifies that a break shall be placed at the current location in the run
* content. A break is a special character which is used to override the
* normal line breaking that would be performed based on the normal layout
* of the document's contents.
*
* The behavior of this break character (the
* location where text shall be restarted after this break) shall be
* determined by its type (in this case is BreakType.TEXT_WRAPPING as default) and clear attribute values.
*
*
* @see BreakClear
*/
public void addBreak(BreakClear clear) {
CTBr br = run.addNewBr();
br.setType(STBrType.Enum.forInt(BreakType.TEXT_WRAPPING.getValue()));
br.setClear(STBrClear.Enum.forInt(clear.getValue()));
}
/**
* Specifies that a tab shall be placed at the current location in
* the run content.
*/
public void addTab() {
run.addNewTab();
}
public void removeTab() {
//TODO
}
/**
* Specifies that a carriage return shall be placed at the
* current location in the run content.
* A carriage return is used to end the current line of text in
* Wordprocess.
* The behavior of a carriage return in run content shall be
* identical to a break character with null type and clear attributes, which
* shall end the current line and find the next available line on which to
* continue.
* The carriage return character forced the following text to be
* restarted on the next available line in the document.
*/
public void addCarriageReturn() {
run.addNewCr();
}
public void removeCarriageReturn() {
//TODO
}
/**
*
* Adds a picture to the run. This method handles
* attaching the picture data to the overall file.
* 复制原本的方法,加入了是否悬浮图片;在加入图片的偏移位置参数
* x和y的取值:1厘米=360045
* @param pictureData
* @param pictureType
* @param filename
* @param width
* @param height
* @param isXuanfu
* @param x 水平偏移位置
* @param y 垂直偏移位置
* @return
* @throws InvalidFormatException
* @throws IOException
*/
public XWPFPicture addPictureV2(InputStream pictureData, int pictureType, String filename, int width, int height,boolean isXuanfu,int x,int y)
throws InvalidFormatException, IOException {
String relationId;
XWPFPictureData picData;
// Work out what to add the picture to, then add both the
// picture and the relationship for it
// TODO Should we have an interface for this sort of thing?
if (parent.getPart() instanceof XWPFHeaderFooter) {
XWPFHeaderFooter headerFooter = (XWPFHeaderFooter) parent.getPart();
relationId = headerFooter.addPictureData(pictureData, pictureType);
picData = (XWPFPictureData) headerFooter.getRelationById(relationId);
} else {
@SuppressWarnings("resource")
XWPFDocument doc = parent.getDocument();
relationId = doc.addPictureData(pictureData, pictureType);
picData = (XWPFPictureData) doc.getRelationById(relationId);
}
// Create the drawing entry for it
try {
CTDrawing drawing = run.addNewDrawing();
// CTInline inline = drawing.addNewInline();
//*************修改 zhengmeng
CTAnchor inline = drawing.addNewAnchor();
// Do the fiddly namespace bits on the inline
// (We need full control of what goes where and as what)
String xml =
" + CTGraphicalObject.type.getName().getNamespaceURI() + "\">" +
" + CTPicture.type.getName().getNamespaceURI() + "\">" +
" + CTPicture.type.getName().getNamespaceURI() + "\" />" +
" " +
" ";
InputSource is = new InputSource(new StringReader(xml));
org.w3c.dom.Document doc = DocumentHelper.readDocument(is);
inline.set(XmlToken.Factory.parse(doc.getDocumentElement(), DEFAULT_XML_OPTIONS));
// Setup the inline
inline.setDistT(0);
inline.setDistR(0);
inline.setDistB(0);
inline.setDistL(0);
//*********************修改zhengmeng**********************************
inline.setSimplePos2(false);
inline.setRelativeHeight(251658240);//查了文档 ,也试过,没找到作用位置,先放着
inline.setBehindDoc(isXuanfu);//重点,浮于文字上方
inline.setLocked(false);
inline.setLayoutInCell(true);
inline.setAllowOverlap(true);
CTPoint2D simplePost = inline.addNewSimplePos();
simplePost.setX(0);
simplePost.setY(0);
CTPosH ph = inline.addNewPositionH();
ph.setRelativeFrom(STRelFromH.PAGE);//以整个页面为准的定位
ph.setPosOffset(x);//水平位置,针对RelativeFrom的一个偏移 1厘米=360045 ,1.1*360045
CTPosV pv = inline.addNewPositionV();
pv.setRelativeFrom(STRelFromV.PAGE);
pv.setPosOffset(y);//竖直位置,针对RelativeFrom的一个偏移 1.3*360045
/**
* 这个是此元素指定的其他程度应添加到每个边缘 (顶部、 底部、 左、 右)
* 以补偿应用于 DrawingML 对象的任何图形效果的图像
* 但目前好像用不到
*/
CTEffectExtent efextent = inline.addNewEffectExtent();
efextent.setL(19050);
efextent.setT(0);
efextent.setR(0);
efextent.setB(0);
CTWrapNone wn = inline.addNewWrapNone();
CTNonVisualGraphicFrameProperties fp = inline.addNewCNvGraphicFramePr();
//*******************************************************
CTNonVisualDrawingProps docPr = inline.addNewDocPr();
long id = getParent().getDocument().getDrawingIdManager().reserveNew();
docPr.setId(id);
/* This name is not visible in Word 2010 anywhere. */
docPr.setName("Drawing " + id);
docPr.setDescr(filename);
CTPositiveSize2D extent = inline.addNewExtent();
extent.setCx(width);
extent.setCy(height);
// Grab the picture object
CTGraphicalObject graphic = inline.getGraphic();
CTGraphicalObjectData graphicData = graphic.getGraphicData();
CTPicture pic = getCTPictures(graphicData).get(0);
// Set it up
CTPictureNonVisual nvPicPr = pic.addNewNvPicPr();
CTNonVisualDrawingProps cNvPr = nvPicPr.addNewCNvPr();
/* use "0" for the id. See ECM-576, 20.2.2.3 */
cNvPr.setId(0L);
/* This name is not visible in Word 2010 anywhere */
cNvPr.setName("Picture " + id);
cNvPr.setDescr(filename);
CTNonVisualPictureProperties cNvPicPr = nvPicPr.addNewCNvPicPr();
cNvPicPr.addNewPicLocks().setNoChangeAspect(true);
CTBlipFillProperties blipFill = pic.addNewBlipFill();
CTBlip blip = blipFill.addNewBlip();
blip.setEmbed(parent.getPart().getRelationId(picData));
/******** 增加 zhengmeng***********/
blip.setCstate(STBlipCompression.PRINT);//压缩状态
blipFill.addNewStretch().addNewFillRect();
CTShapeProperties spPr = pic.addNewSpPr();
CTTransform2D xfrm = spPr.addNewXfrm();
CTPoint2D off = xfrm.addNewOff();
off.setX(0);
off.setY(0);
CTPositiveSize2D ext = xfrm.addNewExt();
ext.setCx(width);
ext.setCy(height);
CTPresetGeometry2D prstGeom = spPr.addNewPrstGeom();
prstGeom.setPrst(STShapeType.RECT);
prstGeom.addNewAvLst();
// Finish up
XWPFPicture xwpfPicture = new XWPFPicture(pic, this);
pictures.add(xwpfPicture);
return xwpfPicture;
} catch (XmlException | SAXException e) {
throw new IllegalStateException(e);
}
}
public XWPFPicture addPicture(InputStream pictureData, int pictureType, String filename, int width, int height) throws InvalidFormatException, IOException {
String relationId;
XWPFPictureData picData;
if (this.parent.getPart() instanceof XWPFHeaderFooter) {
XWPFHeaderFooter headerFooter = (XWPFHeaderFooter)this.parent.getPart();
relationId = headerFooter.addPictureData(pictureData, pictureType);
picData = (XWPFPictureData)headerFooter.getRelationById(relationId);
} else {
XWPFDocument doc = this.parent.getDocument();
relationId = doc.addPictureData(pictureData, pictureType);
picData = (XWPFPictureData)doc.getRelationById(relationId);
}
try {
CTDrawing drawing = this.run.addNewDrawing();
CTInline inline = drawing.addNewInline();
String xml = " + CTGraphicalObject.type.getName().getNamespaceURI() + "\"> + CTPicture.type.getName().getNamespaceURI() + "\"> + CTPicture.type.getName().getNamespaceURI() + "\" /> ";
InputSource is = new InputSource(new StringReader(xml));
Document doc = DocumentHelper.readDocument(is);
inline.set(org.apache.xmlbeans.XmlToken.Factory.parse(doc.getDocumentElement(), POIXMLTypeLoader.DEFAULT_XML_OPTIONS));
inline.setDistT(0L);
inline.setDistR(0L);
inline.setDistB(0L);
inline.setDistL(0L);
CTNonVisualDrawingProps docPr = inline.addNewDocPr();
long id = this.getParent().getDocument().getDrawingIdManager().reserveNew();
docPr.setId(id);
docPr.setName("Drawing " + id);
docPr.setDescr(filename);
CTPositiveSize2D extent = inline.addNewExtent();
extent.setCx((long)width);
extent.setCy((long)height);
CTGraphicalObject graphic = inline.getGraphic();
CTGraphicalObjectData graphicData = graphic.getGraphicData();
CTPicture pic = (CTPicture)this.getCTPictures(graphicData).get(0);
CTPictureNonVisual nvPicPr = pic.addNewNvPicPr();
CTNonVisualDrawingProps cNvPr = nvPicPr.addNewCNvPr();
cNvPr.setId(0L);
cNvPr.setName("Picture " + id);
cNvPr.setDescr(filename);
CTNonVisualPictureProperties cNvPicPr = nvPicPr.addNewCNvPicPr();
cNvPicPr.addNewPicLocks().setNoChangeAspect(true);
CTBlipFillProperties blipFill = pic.addNewBlipFill();
CTBlip blip = blipFill.addNewBlip();
blip.setEmbed(this.parent.getPart().getRelationId(picData));
blipFill.addNewStretch().addNewFillRect();
CTShapeProperties spPr = pic.addNewSpPr();
CTTransform2D xfrm = spPr.addNewXfrm();
CTPoint2D off = xfrm.addNewOff();
off.setX(0L);
off.setY(0L);
CTPositiveSize2D ext = xfrm.addNewExt();
ext.setCx((long)width);
ext.setCy((long)height);
CTPresetGeometry2D prstGeom = spPr.addNewPrstGeom();
prstGeom.setPrst(STShapeType.RECT);
prstGeom.addNewAvLst();
XWPFPicture xwpfPicture = new XWPFPicture(pic, this);
this.pictures.add(xwpfPicture);
return xwpfPicture;
} catch (SAXException | XmlException var31) {
throw new IllegalStateException(var31);
}
}
/**
* this method add chart template into document
*
* @param chartRelId relation id of chart in document relation file
* @throws InvalidFormatException
* @throws IOException
* @since POI 4.0.0
*/
@Internal
public CTInline addChart(String chartRelId)
throws InvalidFormatException, IOException {
try {
CTInline inline = run.addNewDrawing().addNewInline();
//xml part of chart in document
String xml =
" + CTGraphicalObject.type.getName().getNamespaceURI() + "\">" +
" + CTChart.type.getName().getNamespaceURI() + "\">" +
" + CTChart.type.getName().getNamespaceURI() + "\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" r:id=\"" + chartRelId + "\" />" +
" " +
" ";
InputSource is = new InputSource(new StringReader(xml));
org.w3c.dom.Document doc = DocumentHelper.readDocument(is);
inline.set(XmlToken.Factory.parse(doc.getDocumentElement(), DEFAULT_XML_OPTIONS));
// Setup the inline with 0 margin
inline.setDistT(0);
inline.setDistR(0);
inline.setDistB(0);
inline.setDistL(0);
CTNonVisualDrawingProps docPr = inline.addNewDocPr();
long id = getParent().getDocument().getDrawingIdManager().reserveNew();
docPr.setId(id);
//This name is not visible in Word anywhere.
docPr.setName("chart " + id);
return inline;
} catch (XmlException | SAXException e) {
throw new IllegalStateException(e);
}
}
/**
* Returns the embedded pictures of the run. These
* are pictures which reference an external,
* embedded picture image such as a .png or .jpg
*/
public List<XWPFPicture> getEmbeddedPictures() {
return pictures;
}
/**
* Set the style ID for the run.
*
* @param styleId ID (not name) of the style to set for the run, e.g. "BoldItalic" (not "Bold Italic").
*/
public void setStyle(String styleId) {
CTRPr pr = getCTR().getRPr();
if (null == pr) {
pr = getCTR().addNewRPr();
}
CTString style = pr.getRStyle() != null ? pr.getRStyle() : pr.addNewRStyle();
style.setVal(styleId);
}
/**
* Returns the string version of the text and the phonetic string
*/
@Override
public String toString() {
String phonetic = getPhonetic();
if (phonetic.length() > 0) {
return text() + " (" + phonetic + ")";
} else {
return text();
}
}
/**
* Returns the string version of the text, with tabs and
* carriage returns in place of their xml equivalents.
*/
@Override
public String text() {
StringBuilder text = new StringBuilder(64);
// Grab the text and tabs of the text run
// Do so in a way that preserves the ordering
XmlCursor c = run.newCursor();
c.selectPath("./*");
while (c.toNextSelection()) {
XmlObject o = c.getObject();
if (o instanceof CTRuby) {
handleRuby(o, text, false);
continue;
}
_getText(o, text);
}
c.dispose();
return text.toString();
}
/**
* @return the phonetic (ruby) string associated with this run or an empty String if none exists
*/
public String getPhonetic() {
StringBuilder text = new StringBuilder(64);
// Grab the text and tabs of the text run
// Do so in a way that preserves the ordering
XmlCursor c = run.newCursor();
c.selectPath("./*");
while (c.toNextSelection()) {
XmlObject o = c.getObject();
if (o instanceof CTRuby) {
handleRuby(o, text, true);
}
}
// Any picture text?
if (pictureText != null && pictureText.length() > 0) {
text.append("\n").append(pictureText).append("\n");
}
c.dispose();
return text.toString();
}
/**
* @param rubyObj rubyobject
* @param text buffer to which to append the content
* @param extractPhonetic extract the phonetic (rt) component or the base component
*/
private void handleRuby(XmlObject rubyObj, StringBuilder text, boolean extractPhonetic) {
XmlCursor c = rubyObj.newCursor();
//according to the spec, a ruby object
//has the phonetic (rt) first, then the actual text (base)
//second.
c.selectPath(".//*");
boolean inRT = false;
boolean inBase = false;
while (c.toNextSelection()) {
XmlObject o = c.getObject();
if (o instanceof CTRubyContent) {
String tagName = o.getDomNode().getNodeName();
if ("w:rt".equals(tagName)) {
inRT = true;
} else if ("w:rubyBase".equals(tagName)) {
inRT = false;
inBase = true;
}
} else {
if (extractPhonetic && inRT) {
_getText(o, text);
} else if (!extractPhonetic && inBase) {
_getText(o, text);
}
}
}
c.dispose();
}
private void _getText(XmlObject o, StringBuilder text) {
if (o instanceof CTText) {
String tagName = o.getDomNode().getNodeName();
// Field Codes (w:instrText, defined in spec sec. 17.16.23)
// come up as instances of CTText, but we don't want them
// in the normal text output
if (!"w:instrText".equals(tagName)) {
text.append(((CTText) o).getStringValue());
}
}
// Complex type evaluation (currently only for extraction of check boxes)
if (o instanceof CTFldChar) {
CTFldChar ctfldChar = ((CTFldChar) o);
if (ctfldChar.getFldCharType() == STFldCharType.BEGIN) {
if (ctfldChar.getFfData() != null) {
for (CTFFCheckBox checkBox : ctfldChar.getFfData().getCheckBoxList()) {
if (checkBox.getDefault() != null && checkBox.getDefault().getVal() == STOnOff.X_1) {
text.append("|X|");
} else {
text.append("|_|");
}
}
}
}
}
if (o instanceof CTPTab) {
text.append('\t');
}
if (o instanceof CTBr) {
text.append('\n');
}
if (o instanceof CTEmpty) {
// Some inline text elements get returned not as
// themselves, but as CTEmpty, owing to some odd
// definitions around line 5642 of the XSDs
// This bit works around it, and replicates the above
// rules for that case
String tagName = o.getDomNode().getNodeName();
if ("w:tab".equals(tagName) || "tab".equals(tagName)) {
text.append('\t');
}
if ("w:br".equals(tagName) || "br".equals(tagName)) {
text.append('\n');
}
if ("w:cr".equals(tagName) || "cr".equals(tagName)) {
text.append('\n');
}
}
if (o instanceof CTFtnEdnRef) {
CTFtnEdnRef ftn = (CTFtnEdnRef) o;
String footnoteRef = ftn.getDomNode().getLocalName().equals("footnoteReference") ?
"[footnoteRef:" + ftn.getId().intValue() + "]" : "[endnoteRef:" + ftn.getId().intValue() + "]";
text.append(footnoteRef);
}
}
/**
* @see [MS-OI29500] Run Fonts
*/
public static enum FontCharRange {
ascii /* char 0-127 */,
cs /* complex symbol */,
eastAsia /* east asia */,
hAnsi /* high ansi */
}
/**
* Set the text expand/collapse scale value.
*
* @param percentage The percentage to expand or compress the text
* @since 4.0.0
*/
public void setTextScale(int percentage) {
CTRPr pr = getRunProperties(true);
CTTextScale scale = pr.isSetW() ? pr.getW() : pr.addNewW();
scale.setVal(percentage);
}
/**
* Gets the current text scale value.
*
* @return Value is an integer percentage
* @since 4.0.0
*/
public int getTextScale() {
CTRPr pr = getRunProperties(true);
CTTextScale scale = pr.isSetW() ? pr.getW() : pr.addNewW();
int value = scale.getVal();
if (value == 0) {
value = 100; // 100% scaling, that is, no change. See 17.3.2.43 w (Expanded/Compressed Text)
}
return value;
}
/**
* Set the highlight color for the run. Silently does nothing of colorName is not a recognized value.
*
* @param colorName The name of the color as defined in the ST_HighlightColor simple type ({@link STHightlightColor})
* @since 4.0.0
*/
public void setTextHighlightColor(String colorName) {
CTRPr pr = getRunProperties(true);
CTHighlight highlight = pr.isSetHighlight() ? pr.getHighlight() : pr.addNewHighlight();
STHighlightColor color = highlight.xgetVal();
if (color == null) {
color = STHighlightColor.Factory.newInstance();
}
STHighlightColor.Enum val = STHighlightColor.Enum.forString(colorName);
if (val != null) {
color.setStringValue(val.toString());
highlight.xsetVal(color);
}
}
/**
* Gets the highlight color for the run
*
* @return {@link STHighlightColor} for the run.
* @since 4.0.0
*/
public STHighlightColor.Enum getTextHightlightColor() {
CTRPr pr = getRunProperties(true);
CTHighlight highlight = pr.isSetHighlight() ? pr.getHighlight() : pr.addNewHighlight();
STHighlightColor color = highlight.xgetVal();
if (color == null) {
color = STHighlightColor.Factory.newInstance();
color.set(STHighlightColor.NONE);
}
return (STHighlightColor.Enum)(color.enumValue());
}
/**
* Get the vanish (hidden text) value
*
* @return True if the run is hidden text.
* @since 4.0.0
*/
public boolean isVanish() {
CTRPr pr = getRunProperties(true);
return pr != null && pr.isSetVanish() && isCTOnOff(pr.getVanish());
}
/**
* The vanish (hidden text) property for the run.
*
* @param value Set to true to make the run hidden text.
* @since 4.0.0
*/
public void setVanish(boolean value) {
CTRPr pr = getRunProperties(true);
CTOnOff vanish = pr.isSetVanish() ? pr.getVanish() : pr.addNewVanish();
vanish.setVal(value ? STOnOff.TRUE : STOnOff.FALSE);
}
/**
* Get the vertical alignment value
*
* @return {@link STVerticalAlignRun.Enum} value (see 22.9.2.17 ST_VerticalAlignRun (Vertical Positioning Location))
* @since 4.0.0
*/
public STVerticalAlignRun.Enum getVerticalAlignment() {
CTRPr pr = getRunProperties(true);
CTVerticalAlignRun vertAlign = pr.isSetVertAlign() ? pr.getVertAlign() : pr.addNewVertAlign();
STVerticalAlignRun.Enum val = vertAlign.getVal();
if (val == null) {
val = STVerticalAlignRun.BASELINE;
}
return val;
}
/**
* Set the vertical alignment of the run.
*
* @param verticalAlignment Vertical alignment value, one of "baseline", "superscript", or "subscript".
* @since 4.0.0
*/
public void setVerticalAlignment(String verticalAlignment) {
CTRPr pr = getRunProperties(true);
CTVerticalAlignRun vertAlign = pr.isSetVertAlign() ? pr.getVertAlign() : pr.addNewVertAlign();
STVerticalAlignRun align = vertAlign.xgetVal();
if (align == null) {
align = STVerticalAlignRun.Factory.newInstance();
}
STVerticalAlignRun.Enum val = STVerticalAlignRun.Enum.forString(verticalAlignment);
if (val != null) {
align.setStringValue(val.toString());
vertAlign.xsetVal(align);
}
}
/**
* Get the emphasis mark value for the run.
*
* @return {@link STEm.Enum} emphasis mark type enumeration. See 17.18.24 ST_Em (Emphasis Mark Type).
* @since 4.0.0
*/
public STEm.Enum getEmphasisMark() {
CTRPr pr = getRunProperties(true);
CTEm emphasis = pr.isSetEm() ? pr.getEm() : pr.addNewEm();
STEm.Enum val = emphasis.getVal();
if (val == null) {
val = STEm.NONE;
}
return val;
}
/**
* Set the emphasis mark for the run. The emphasis mark goes above or below the run
* text.
*
* @param markType Emphasis mark type name, e.g., "dot" or "none". See 17.18.24 ST_Em (Emphasis Mark Type)
* @since 4.0.0
*/
public void setEmphasisMark(String markType) {
CTRPr pr = getRunProperties(true);
CTEm emphasisMark = pr.isSetEm() ? pr.getEm() : pr.addNewEm();
STEm mark = emphasisMark.xgetVal();
if (mark == null) {
mark = STEm.Factory.newInstance();
}
STEm.Enum val = STEm.Enum.forString(markType);
if (val != null) {
mark.setStringValue(val.toString());
emphasisMark.xsetVal(mark);
}
}
/**
* Get the run properties for the run.
*
* @param create If true, create the properties, if false, do not.
* @return The run properties or null if there are no properties and create is false.
*/
protected CTRPr getRunProperties(boolean create) {
CTRPr pr = run.isSetRPr() ? run.getRPr() : null;
if (create && pr == null) {
pr = run.addNewRPr();
}
return pr;
}
}
在实际开发中也遇到很多问题比如自定义的 不包含在一个 X W P F R u n 中,当然书签可以解决,但是为了展示字段直观,还是采用了字段替换,逐字段读取找到所有的 X W P F R u n 再替换,还有一个操作也能实现,但没必要,因为我提供的工具类完美解决了,感兴趣的可以按照如下操作试一下 1. 将 d o c x 后缀改成 z i p 2. 找到 w o r d / d o c u e n t . x m l 并打开 3. 修改或删除里面的 < w : t > {}不包含在一个XWPFRun中, 当然书签可以解决,但是为了展示字段直观,还是采用了字段替换, 逐字段读取找到所有的XWPFRun再替换,还有一个操作也能实现,但没必要,因为我提供的工具类完美解决了, 感兴趣的可以按照如下操作试一下 1.将docx后缀改成zip 2.找到 word/docuent.xml并打开 3.修改或删除里面的
docx文件的本质就是zip
点击此处有详细教程
点击此处有详细教程
2023/12/11 21:19:00补充
表格中还存在表格的获取方式
通过XWPFTableCell中的getTables()方法获取表格
前文已经同步修改
求个一键三连哦!!!!!!