Merge pull request '【修改】文字考点段落,线性汉化' (#2) from hyc into master

Reviewed-on: #2
This commit is contained in:
hyc
2025-09-10 21:55:06 +08:00
6 changed files with 431 additions and 121 deletions

View File

@@ -2,6 +2,7 @@ package pc.exam.pp.module.judgement.utils.wps_word.docx4j.drawing;
import jakarta.xml.bind.*;
import lombok.val;
import org.apache.xmlbeans.XmlCursor;
import org.docx4j.XmlUtils;
import org.docx4j.dml.*;
import org.docx4j.dml.chartDrawing.CTPicture;
@@ -24,6 +25,7 @@ import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import java.io.ByteArrayInputStream;
import java.io.StringReader;
@@ -1328,10 +1330,10 @@ public static String getSolidFillColor(List<JudgementWordsVO> judgementWordsVOS,
return "";
}
// 2. 预设类型shadow preset
// 2. 预设类型
public static String getShadowPreset(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
CTReflectionEffect shadow = getReflectionEffect(anchor);
if (shadow == null) return "";
if (shadow == null) return "预设";
// 例如根据blurRad、dist、dir组合判断
BigInteger blurRad = BigInteger.valueOf(shadow.getBlurRad());

View File

@@ -8,6 +8,7 @@ import org.docx4j.XmlUtils;
import org.docx4j.dml.*;
import org.docx4j.dml.chartDrawing.CTPicture;
import org.docx4j.dml.picture.Pic;
import org.docx4j.dml.wordprocessingDrawing.Anchor;
import org.docx4j.dml.wordprocessingDrawing.CTWrapSquare;
import org.docx4j.dml.wordprocessingDrawing.Inline;
import org.docx4j.dml.wordprocessingDrawing.Inline;
@@ -753,8 +754,80 @@ public class DrawingInline {
// 获取“预设”
public static String getShadowPresetType(List<JudgementWordsVO> judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
CTOuterShadowEffect shadow = getOuterShadow(anchor);
CTInnerShadowEffect innerShadow= getInnerShadow(anchor);
CTPresetShadowEffect presetShadow = getPresetShadow(anchor);
if (shadow != null ) {
return "无预设";
Long blurRadObj = shadow.getBlurRad();
Long distObj = shadow.getDist();
Long dirObj = (long) shadow.getDir();
long blurRad = blurRadObj != null ? blurRadObj : 0L;
long dist = distObj != null ? distObj : 0L;
long dir = dirObj != null ? dirObj : 0L;
// 转换为角度
double angle = dir / 60000.0;
// 1⃣ 偏移阴影(外部)
if (angle == 45 ) return "偏移右下";
if (angle == 90 ) return "偏移下";
if (angle == 135) return "偏移左下";
if (angle == 0&&dist!=0 ) return "偏移右";
if (angle == 180 ) return "偏移左";
if (angle ==315 ) return "偏移右上";
if (angle == 270 ) return "偏移上";
if (angle == 225) return "偏移左上";
if (dist==0) return "";
}
if (innerShadow!=null){
Long blurRadObj = innerShadow.getBlurRad();
Long distObj = innerShadow.getDist();
Long dirObj = (long) innerShadow.getDir();
long blurRad = blurRadObj != null ? blurRadObj : 0L;
long dist = distObj != null ? distObj : 0L;
long dir = dirObj != null ? dirObj : 0L;
// 转换为角度
double angle = dir / 60000.0;
// 2⃣ 内部阴影dist 较小blurRad 中等)
if (angle == 45 ) return "内部右下";
if (angle == 90 ) return "内部下";
if (angle == 135) return "内部左下";
if (angle == 00&&dist!=0 ) return "内部右";
if (angle == 180 ) return "内部左";
if (angle ==315 ) return "内部右上";
if (angle == 270 ) return "内部上";
if (angle == 225) return "内部左上";
if (dist==0) return "";
}
if (presetShadow!=null){
Long blurRadObj = shadow.getBlurRad();
Long distObj = shadow.getDist();
Long dirObj = (long) shadow.getDir();
long blurRad = blurRadObj != null ? blurRadObj : 0L;
long dist = distObj != null ? distObj : 0L;
long dir = dirObj != null ? dirObj : 0L;
// 转换为角度
double angle = dir / 60000.0;
// 3⃣ 透视阴影dist 和 blurRad 都较大)
if (angle == 225) return "透视左上";
if (angle ==315 ) return "透视右上";
if (angle == 90 ) return "透视下";
if (angle == 135) return "透视左下";
if (angle == 45 ) return "透视右下";
if (dist==0) return "";
}
return "";
}
@@ -1324,7 +1397,34 @@ public static String getSolidFillColor(List<JudgementWordsVO> judgementWordsVOS,
}
private static CTInnerShadowEffect getInnerShadow(Inline anchor) {
if (anchor == null || anchor.getDocPr() == null) return null;
if (anchor.getGraphic() != null &&
anchor.getGraphic().getGraphicData() != null &&
anchor.getGraphic().getGraphicData().getPic() != null) {
CTShapeProperties spPr = anchor.getGraphic().getGraphicData().getPic().getSpPr();
if (spPr != null && spPr.getEffectLst() != null && spPr.getEffectLst().getInnerShdw() != null) {
return spPr.getEffectLst().getInnerShdw();
}
}
return null;
}
private static CTPresetShadowEffect getPresetShadow(Inline anchor) {
if (anchor == null || anchor.getGraphic() == null) return null;
Object graphicData = anchor.getGraphic().getGraphicData().getAny().get(0);
if (graphicData instanceof JAXBElement) {
graphicData = ((JAXBElement<?>) graphicData).getValue();
}
if (graphicData instanceof CTPresetShadowEffect) {
return (CTPresetShadowEffect) graphicData;
}
return null;
}
private static String getShapeName(String shapeType) {

View File

@@ -2,6 +2,7 @@ package pc.exam.pp.module.judgement.utils.wps_word.docx4j.paragraph;
import jakarta.xml.bind.JAXBElement;
import org.docx4j.XmlUtils;
import org.docx4j.model.structure.SectionWrapper;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.NumberingDefinitionsPart;
import org.docx4j.openpackaging.parts.WordprocessingML.StyleDefinitionsPart;
@@ -10,6 +11,7 @@ import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTextAlignment;
import pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils;
import java.math.BigInteger;
import java.util.List;
/**
* @author REN
@@ -790,59 +792,90 @@ public class Paragraphs {
// 分栏 - 分隔线
public static String getParagraphColIsSep(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) {
PPr pPr = paragraph.getPPr();
SectPr sectPr = null;
sectPr = pPr.getSectPr();
// 优先取段落自身的节属性
if (pPr != null && pPr.getSectPr() != null) {
sectPr = pPr.getSectPr();
}
if (paragraph == null) return "";
// 或最后一个段落中会有一个节结束的SectPr
if (sectPr == null && paragraph.getContent().size() > 0) {
Object lastObj = paragraph.getContent().get(paragraph.getContent().size() - 1);
if (lastObj instanceof JAXBElement && ((JAXBElement<?>) lastObj).getValue() instanceof SectPr) {
sectPr = (SectPr) ((JAXBElement<?>) lastObj).getValue();
List<Object> allParagraphs = wordMLPackage.getMainDocumentPart().getContent();
if (allParagraphs == null || allParagraphs.isEmpty()) return "";
SectPr currentSectPr = null;
for (Object obj : allParagraphs) {
P p = null;
if (obj instanceof P) {
p = (P) obj;
} else if (obj instanceof JAXBElement && ((JAXBElement<?>) obj).getValue() instanceof P) {
p = (P) ((JAXBElement<?>) obj).getValue();
}
if (p == null) continue;
// 每遇到一个段落的 SectPr 就更新当前节
PPr pPr = p.getPPr();
if (pPr != null && pPr.getSectPr() != null) {
currentSectPr = pPr.getSectPr();
}
// 如果当前段落就是目标段落,返回该节分栏信息
if (p == paragraph) {
if (currentSectPr != null && currentSectPr.getCols() != null) {
CTColumns cols = currentSectPr.getCols();
Boolean sep = cols.isSep();
return sep != null && sep ? "" : "";
} else {
return "";
}
}
}
if (sectPr != null && sectPr.getCols() != null) {
CTColumns cols = sectPr.getCols();
return cols.isSep() ? "" : "";
}
return "";
}
// 分栏 - 各栏间距
public static String getParagraphColSpace(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) {
PPr pPr = paragraph.getPPr();
SectPr sectPr = null;
sectPr = pPr.getSectPr();
// 优先取段落自身的节属性
if (pPr != null && pPr.getSectPr() != null) {
sectPr = pPr.getSectPr();
}
if (paragraph == null) return "";
// 或最后一个段落中会有一个节结束的SectPr
if (sectPr == null && paragraph.getContent().size() > 0) {
Object lastObj = paragraph.getContent().get(paragraph.getContent().size() - 1);
if (lastObj instanceof JAXBElement && ((JAXBElement<?>) lastObj).getValue() instanceof SectPr) {
sectPr = (SectPr) ((JAXBElement<?>) lastObj).getValue();
List<Object> allParagraphs = wordMLPackage.getMainDocumentPart().getContent();
if (allParagraphs == null || allParagraphs.isEmpty()) return "";
SectPr currentSectPr = null;
for (Object obj : allParagraphs) {
P p = null;
if (obj instanceof P) {
p = (P) obj;
} else if (obj instanceof JAXBElement && ((JAXBElement<?>) obj).getValue() instanceof P) {
p = (P) ((JAXBElement<?>) obj).getValue();
}
}
if (sectPr != null && sectPr.getCols() != null) {
CTColumns cols = sectPr.getCols();
if (cols.getCol() != null && !cols.getCol().isEmpty()) {
int index = 1;
String value = "";
for (CTColumn col : cols.getCol()) {
value += "" + index + " 栏间距 (twips): " + col.getSpace() + " ";
index++;
if (p == null) continue;
// 遇到段落中有SectPr则更新当前节
PPr pPr = p.getPPr();
if (pPr != null && pPr.getSectPr() != null) {
currentSectPr = pPr.getSectPr();
}
// 如果是目标段落,返回分栏间距
if (p == paragraph) {
if (currentSectPr != null && currentSectPr.getCols() != null) {
CTColumns cols = currentSectPr.getCols();
if (cols.getCol() != null && !cols.getCol().isEmpty()) {
StringBuilder value = new StringBuilder();
int index = 1;
for (CTColumn col : cols.getCol()) {
value.append("").append(index).append(" 栏间距 (twips): ").append(col.getSpace()).append(" ");
index++;
}
return value.toString();
} else if (cols.getSpace() != null) {
// 如果没有单独列配置,使用总栏间距
return "栏间距 (twips): " + cols.getSpace();
}
}
return value;
return "";
}
}
return "";
}
// 分栏 - 各栏宽度

View File

@@ -29,6 +29,7 @@ import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils;
import javax.xml.namespace.QName;
import javax.xml.xpath.XPath;
@@ -1729,7 +1730,7 @@ public class SectionPage {
// 以 Top 为例获取颜色
CTBorder top = borders.getTop();
if (top != null && top.getColor() != null) {
return top.getColor();
return ColorUtils.getChineseColorName(top.getColor());
}
}
return "";

View File

@@ -123,7 +123,28 @@ public class TableIng {
tblCursor.selectPath(path);
if (tblCursor.toNextSelection()) {
String val = tblCursor.getAttributeText(new QName(W_NAMESPACE, "val"));
return val != null ? val : null;
if (val == null) return null;
// 汉化
switch (val) {
case "single":
return "单实线";
case "dashed":
return "虚线";
case "dotted":
return "点线";
case "double":
return "双实线";
case "thick":
return "粗线";
case "thinThickSmallGap":
return "细粗小间隔线";
case "thickThinSmallGap":
return "粗细小间隔线";
case "nil":
return "无边框";
default:
return val; // 保留原值
}
}
return null;
} finally {

View File

@@ -13,6 +13,7 @@ import org.docx4j.dml.*;
import org.docx4j.dml.CTColor;
import org.docx4j.dml.picture.Pic;
import org.docx4j.dml.wordprocessingDrawing.Anchor;
import org.docx4j.dml.wordprocessingDrawing.Inline;
import org.docx4j.jaxb.Context;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.vml.CTTextbox;
@@ -1360,22 +1361,75 @@ public class TextInfo {
//填充方式
public static String getTianChongValue(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
if (anchor == null || anchor.getGraphic() == null) return "无填充";
try {
// 1. 获取 anchor 的 graphicData
Object graphicData = anchor.getGraphic().getGraphicData().getAny().get(0);
if (graphicData instanceof JAXBElement) {
JAXBElement<?> jaxbElement = (JAXBElement<?>) graphicData;
Object value = jaxbElement.getValue();
if (value instanceof CTWordprocessingShape) {
CTWordprocessingShape textInfo = (CTWordprocessingShape) value;
if (textInfo.getSpPr() != null) {
if (textInfo.getSpPr().getSolidFill() != null) {
return "纯色填充";
} else if (textInfo.getSpPr().getBlipFill() != null) {
return "图片填充";
} else if (textInfo.getSpPr().getGradFill() != null) {
return "渐变填充";
} else {
return "无填充";
if (!(graphicData instanceof JAXBElement)) return "无填充";
JAXBElement<?> jaxbElement = (JAXBElement<?>) graphicData;
Object value = jaxbElement.getValue();
// 2. 确认是文本框形状
if (!(value instanceof CTWordprocessingShape)) return "无填充";
CTWordprocessingShape textShape = (CTWordprocessingShape) value;
// 3. 解析 <w:txbxContent> 中的段落和文字
if (textShape.getTxbx() != null && textShape.getTxbx().getTxbxContent() != null) {
List<Object> blockLevelElts = textShape.getTxbx().getTxbxContent().getEGBlockLevelElts();
for (Object block : blockLevelElts) {
if (block instanceof P) {
P paragraph = (P) block;
for (Object runObj : paragraph.getContent()) {
if (runObj instanceof R) {
R run = (R) runObj;
// 4. 检查 run 的 rPr 中的颜色
if (run.getRPr() != null) {
RPr rPr = run.getRPr();
// 4.1 直接定义了颜色 -> 纯色填充
if (rPr.getColor() != null && rPr.getColor().getVal() != null) {
return "纯色填充";
}
// 4.2 检查 Drawing 中的 <a:solidFill> 或 <a:gradFill>
for (Object drawingObj : run.getContent()) {
if (drawingObj instanceof Drawing) {
Drawing drawing = (Drawing) drawingObj;
for (Object inlineObj : drawing.getAnchorOrInline()) {
if (inlineObj instanceof Inline) {
Inline inline = (Inline) inlineObj;
// 解析图形属性
if (inline.getGraphic() != null &&
inline.getGraphic().getGraphicData() != null) {
List<Object> anyList = inline.getGraphic().getGraphicData().getAny();
for (Object any : anyList) {
if (any instanceof JAXBElement) {
JAXBElement<?> innerElement = (JAXBElement<?>) any;
Object innerValue = innerElement.getValue();
// 解析 a:solidFill 或 a:gradFill
if (innerValue instanceof CTShapeProperties) {
CTShapeProperties shapeProps = (CTShapeProperties) innerValue;
if (shapeProps.getSolidFill() != null) {
return "纯色填充";
} else if (shapeProps.getGradFill() != null) {
return "渐变填充";
}
}
}
}
}
}
}
}
}
}
}
}
}
}
@@ -1383,78 +1437,177 @@ public class TextInfo {
} catch (Exception e) {
e.printStackTrace();
}
// 默认无填充
return "无填充";
}
// 文本框 - 文字 - 纯色填充 → 颜色
public static String getTextBoxTextSolidFillColor(List<JudgementWordsVO> judgementWordsVOS,
Anchor anchor,
int betoLong,
WordprocessingMLPackage wordMLPackage) {
try {
// 1. 判空检查
if (anchor == null || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null) {
return null;
}
List<Object> anyList = anchor.getGraphic().getGraphicData().getAny();
if (anyList == null || anyList.isEmpty()) {
return null;
}
// 2. 遍历 GraphicData 下的内容
for (Object obj : anyList) {
Object value = (obj instanceof JAXBElement) ? ((JAXBElement<?>) obj).getValue() : obj;
// 只处理 CTWordprocessingShape
if (!value.getClass().getName().endsWith("CTWordprocessingShape")) {
continue;
}
// 3. 通过反射获取 getTxbx() 方法
Method getTxbxMethod;
try {
getTxbxMethod = value.getClass().getMethod("getTxbx");
} catch (NoSuchMethodException e) {
System.out.println("当前 CTWordprocessingShape 没有 getTxbx() 方法");
continue;
}
Object txbxObj = getTxbxMethod.invoke(value);
if (txbxObj == null) {
continue;
}
// 4. 解析 wps:txbxContent
Method getTxbxContentMethod = txbxObj.getClass().getMethod("getTxbxContent");
Object txbxContent = getTxbxContentMethod.invoke(txbxObj);
if (txbxContent == null) {
continue;
}
// 5. 获取段落 <w:p>
if (!(txbxContent instanceof ContentAccessor)) {
continue;
}
ContentAccessor contentAccessor = (ContentAccessor) txbxContent;
List<Object> paragraphs = contentAccessor.getContent();
if (paragraphs == null || paragraphs.isEmpty()) {
continue;
}
for (Object pObj : paragraphs) {
Object paragraph = (pObj instanceof JAXBElement) ? ((JAXBElement<?>) pObj).getValue() : pObj;
if (!(paragraph instanceof P)) {
continue;
}
// 6. 获取段落中的 run
P p = (P) paragraph;
List<Object> runs = p.getContent();
for (Object rObj : runs) {
Object runVal = (rObj instanceof JAXBElement) ? ((JAXBElement<?>) rObj).getValue() : rObj;
if (!(runVal instanceof R)) {
continue;
}
R run = (R) runVal;
RPr runPr = run.getRPr();
if (runPr != null && runPr.getColor() != null) {
String colorVal = runPr.getColor().getVal(); // 十六进制字符串,例如 "4874CB"
if (colorVal != null && colorVal.length() == 6) {
try {
int r = Integer.parseInt(colorVal.substring(0, 2), 16);
int g = Integer.parseInt(colorVal.substring(2, 4), 16);
int b = Integer.parseInt(colorVal.substring(4, 6), 16);
String rgb = "RGB(" + r + "," + g + "," + b + ")";
System.out.println("文本框文字颜色 = " + rgb);
return rgb;
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("未找到文本框文字颜色");
return null;
}
// 文本框-文字-纯色填充 → 透明度
public static String getTextBoxSolidFillTransparency(
List<JudgementWordsVO> judgementWordsVOS,
org.docx4j.dml.wordprocessingDrawing.Anchor anchor,
int betoLong,
WordprocessingMLPackage wordMLPackage) {
if (anchor == null) return "无纯色填充";
try {
// 1. 包装成 JAXBElement 打印调试 XML
JAXBElement<Anchor> jaxbElement = new JAXBElement<>(
new javax.xml.namespace.QName(
"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
"anchor"),
Anchor.class,
anchor
);
String xml = XmlUtils.marshaltoString(jaxbElement, true, true);
// System.out.println("Anchor XML:\n" + xml);
//纯色填充→透明度
public static String getTextBoxSolidFillTransparency(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
if (anchor == null || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null) {
System.out.println("Anchor 或 Graphic 为空");
return "无纯色填充";
}
List<Object> anyList = anchor.getGraphic().getGraphicData().getAny();
if (anyList == null || anyList.isEmpty()) {
System.out.println("GraphicData 中没有任何对象");
return "无纯色填充";
}
for (Object obj : anyList) {
Object value = obj instanceof JAXBElement ? ((JAXBElement<?>) obj).getValue() : obj;
// 文本框一般是 CTWordprocessingShape 类型
if (value.getClass().getName().endsWith("CTWordprocessingShape")) {
// 2. 直接解析 XML 字符串查找 w14:textFill/solidFill/schemeClr/alpha
String alpha = "无纯色填充";
// 正则匹配 w14:solidFill 下的 w14:alpha
java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(
"<w14:textFill>\\s*<w14:solidFill>.*?<w14:alpha\\s+w14:val=\"(\\d+)\"\\s*/>.*?</w14:solidFill>",
java.util.regex.Pattern.DOTALL
);
java.util.regex.Matcher matcher = pattern.matcher(xml);
if (matcher.find()) {
String alphaVal = matcher.group(1);
// Word 中 alpha 取值范围 0~100000对应 0~100%
try {
// 反射调用 getSpPr()
Method getSpPrMethod = value.getClass().getMethod("getSpPr");
Object spPrObj = getSpPrMethod.invoke(value);
if (spPrObj == null) {
System.out.println("文本框 spPr 为 null");
return "无纯色填充";
}
// 反射调用 getSolidFill()
Method getSolidFillMethod = spPrObj.getClass().getMethod("getSolidFill");
Object solidFillObj = getSolidFillMethod.invoke(spPrObj);
if (solidFillObj == null) {
System.out.println("spPr 中无纯色填充");
return "无纯色填充";
}
// 获取 srgbClr 或 schemeClr
Method getSrgbClrMethod = solidFillObj.getClass().getMethod("getSrgbClr");
Object srgbClrObj = getSrgbClrMethod.invoke(solidFillObj);
Method getSchemeClrMethod = solidFillObj.getClass().getMethod("getSchemeClr");
Object schemeClrObj = getSchemeClrMethod.invoke(solidFillObj);
// 透明度提取逻辑
String transparency = "0%"; // 默认完全不透明
if (srgbClrObj != null) {
transparency = extractAlphaTransparency(srgbClrObj);
return transparency;
} else if (schemeClrObj != null) {
transparency = extractAlphaTransparency(schemeClrObj);
return transparency;
} else {
System.out.println("无srgbClr和schemeClr颜色");
return transparency;
}
} catch (Exception e) {
e.printStackTrace();
}
} else {
System.out.println("未识别的类型: " + value.getClass().getName());
int alphaInt = Integer.parseInt(alphaVal);
int percent = Math.round(alphaInt / 1000.0f); // 四舍五入取整百分比
alpha = percent + "%";
} catch (NumberFormatException ignored) {}
}
return alpha;
} catch (Exception e) {
e.printStackTrace();
}
return null;
return "无纯色填充";
}
// 获取透明度方法0-100%
private static int getAlphaFromColor(Object colorObj) {
try {
Method getAlpha = colorObj.getClass().getMethod("getAlpha");
Object alpha = getAlpha.invoke(colorObj);
if (alpha != null) {
// alpha 为 CTPositiveFixedPercentage 或类似类型
Method getVal = alpha.getClass().getMethod("getVal");
Long val = (Long) getVal.invoke(alpha); // 0-100000
return 100 - (int)(val / 1000); // 转换为百分比
}
} catch (Exception e) {
// 如果没有 alpha默认不透明
return 0;
}
return 0;
}