git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1843481 13f79535-47bb-0310-9956-ffa450edef68tags/REL_4_0_1
* This class bundles the configuration options used for the existing | * This class bundles the configuration options used for the existing | ||||
* signature facets. | * signature facets. | ||||
* Apart of the thread local members (e.g. opc-package) most values will probably be constant, so | * Apart of the thread local members (e.g. opc-package) most values will probably be constant, so | ||||
* it might be configured centrally (e.g. by spring) | |||||
* it might be configured centrally (e.g. by spring) | |||||
*/ | */ | ||||
@SuppressWarnings({"unused","WeakerAccess"}) | @SuppressWarnings({"unused","WeakerAccess"}) | ||||
public class SignatureConfig { | public class SignatureConfig { | ||||
public interface SignatureConfigurable { | public interface SignatureConfigurable { | ||||
void setSignatureConfig(SignatureConfig signatureConfig); | |||||
void setSignatureConfig(SignatureConfig signatureConfig); | |||||
} | } | ||||
private ThreadLocal<OPCPackage> opcPackage = new ThreadLocal<>(); | private ThreadLocal<OPCPackage> opcPackage = new ThreadLocal<>(); | ||||
private ThreadLocal<XMLSignatureFactory> signatureFactory = new ThreadLocal<>(); | private ThreadLocal<XMLSignatureFactory> signatureFactory = new ThreadLocal<>(); | ||||
private ThreadLocal<KeyInfoFactory> keyInfoFactory = new ThreadLocal<>(); | private ThreadLocal<KeyInfoFactory> keyInfoFactory = new ThreadLocal<>(); | ||||
private ThreadLocal<Provider> provider = new ThreadLocal<>(); | private ThreadLocal<Provider> provider = new ThreadLocal<>(); | ||||
private List<SignatureFacet> signatureFacets = new ArrayList<>(); | private List<SignatureFacet> signatureFacets = new ArrayList<>(); | ||||
private HashAlgorithm digestAlgo = HashAlgorithm.sha256; | private HashAlgorithm digestAlgo = HashAlgorithm.sha256; | ||||
private Date executionTime = new Date(); | private Date executionTime = new Date(); | ||||
private SignaturePolicyService signaturePolicyService; | private SignaturePolicyService signaturePolicyService; | ||||
private URIDereferencer uriDereferencer; | private URIDereferencer uriDereferencer; | ||||
private String canonicalizationMethod = CanonicalizationMethod.INCLUSIVE; | private String canonicalizationMethod = CanonicalizationMethod.INCLUSIVE; | ||||
private boolean includeEntireCertificateChain = true; | private boolean includeEntireCertificateChain = true; | ||||
private boolean includeIssuerSerial; | private boolean includeIssuerSerial; | ||||
private boolean includeKeyValue; | private boolean includeKeyValue; | ||||
/** | /** | ||||
* the time-stamp service used for XAdES-T and XAdES-X. | * the time-stamp service used for XAdES-T and XAdES-X. | ||||
*/ | */ | ||||
private String tspRequestPolicy = "1.3.6.1.4.1.13762.3"; | private String tspRequestPolicy = "1.3.6.1.4.1.13762.3"; | ||||
private String userAgent = "POI XmlSign Service TSP Client"; | private String userAgent = "POI XmlSign Service TSP Client"; | ||||
private String proxyUrl; | private String proxyUrl; | ||||
/** | /** | ||||
* the optional revocation data service used for XAdES-C and XAdES-X-L. | * the optional revocation data service used for XAdES-C and XAdES-X-L. | ||||
* When <code>null</code> the signature will be limited to XAdES-T only. | * When <code>null</code> the signature will be limited to XAdES-T only. | ||||
* <code>null</code> value will trigger an automatically generated signature Id. | * <code>null</code> value will trigger an automatically generated signature Id. | ||||
*/ | */ | ||||
private String packageSignatureId = "idPackageSignature"; | private String packageSignatureId = "idPackageSignature"; | ||||
/** | /** | ||||
* Gives back the human-readable description of what the citizen will be | * Gives back the human-readable description of what the citizen will be | ||||
* signing. The default value is "Office OpenXML Document". | * signing. The default value is "Office OpenXML Document". | ||||
*/ | */ | ||||
private String signatureDescription = "Office OpenXML Document"; | private String signatureDescription = "Office OpenXML Document"; | ||||
/** | /** | ||||
* The process of signing includes the marshalling of xml structures. | * The process of signing includes the marshalling of xml structures. | ||||
* This also includes the canonicalization. Currently this leads to problems | |||||
* This also includes the canonicalization. Currently this leads to problems | |||||
* with certain namespaces, so this EventListener is used to interfere | * with certain namespaces, so this EventListener is used to interfere | ||||
* with the marshalling process. | * with the marshalling process. | ||||
*/ | */ | ||||
/** | /** | ||||
* Inits and checks the config object. | * Inits and checks the config object. | ||||
* If not set previously, complex configuration properties also get | |||||
* If not set previously, complex configuration properties also get | |||||
* created/initialized via this initialization call. | * created/initialized via this initialization call. | ||||
* | * | ||||
* @param onlyValidation if true, only a subset of the properties | * @param onlyValidation if true, only a subset of the properties | ||||
namespacePrefixes.put(OO_DIGSIG_NS, "mdssi"); | namespacePrefixes.put(OO_DIGSIG_NS, "mdssi"); | ||||
namespacePrefixes.put(XADES_132_NS, "xd"); | namespacePrefixes.put(XADES_132_NS, "xd"); | ||||
} | } | ||||
if (onlyValidation) { | if (onlyValidation) { | ||||
return; | return; | ||||
} | } | ||||
if (signatureMarshalListener == null) { | if (signatureMarshalListener == null) { | ||||
signatureMarshalListener = new SignatureMarshalListener(); | signatureMarshalListener = new SignatureMarshalListener(); | ||||
} | } | ||||
if (signatureMarshalListener instanceof SignatureConfigurable) { | if (signatureMarshalListener instanceof SignatureConfigurable) { | ||||
((SignatureConfigurable)signatureMarshalListener).setSignatureConfig(this); | ((SignatureConfigurable)signatureMarshalListener).setSignatureConfig(this); | ||||
} | } | ||||
if (tspService != null) { | if (tspService != null) { | ||||
tspService.setSignatureConfig(this); | tspService.setSignatureConfig(this); | ||||
} | } | ||||
if (signatureFacets.isEmpty()) { | if (signatureFacets.isEmpty()) { | ||||
addSignatureFacet(new OOXMLSignatureFacet()); | addSignatureFacet(new OOXMLSignatureFacet()); | ||||
addSignatureFacet(new KeyInfoSignatureFacet()); | addSignatureFacet(new KeyInfoSignatureFacet()); | ||||
sf.setSignatureConfig(this); | sf.setSignatureConfig(this); | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* @param signatureFacet the signature facet is appended to facet list | |||||
* @param signatureFacet the signature facet is appended to facet list | |||||
*/ | */ | ||||
public void addSignatureFacet(SignatureFacet signatureFacet) { | public void addSignatureFacet(SignatureFacet signatureFacet) { | ||||
signatureFacets.add(signatureFacet); | signatureFacets.add(signatureFacet); | ||||
} | } | ||||
/** | /** | ||||
* @return the list of facets, may be empty when the config object is not initialized | * @return the list of facets, may be empty when the config object is not initialized | ||||
*/ | */ | ||||
public void setDigestAlgo(HashAlgorithm digestAlgo) { | public void setDigestAlgo(HashAlgorithm digestAlgo) { | ||||
this.digestAlgo = digestAlgo; | this.digestAlgo = digestAlgo; | ||||
} | } | ||||
/** | /** | ||||
* @return the opc package to be used by this thread, stored as thread-local | * @return the opc package to be used by this thread, stored as thread-local | ||||
*/ | */ | ||||
public OPCPackage getOpcPackage() { | public OPCPackage getOpcPackage() { | ||||
return opcPackage.get(); | return opcPackage.get(); | ||||
} | } | ||||
/** | /** | ||||
* @param opcPackage the opc package to be handled by this thread, stored as thread-local | * @param opcPackage the opc package to be handled by this thread, stored as thread-local | ||||
*/ | */ | ||||
public void setSignatureDescription(String signatureDescription) { | public void setSignatureDescription(String signatureDescription) { | ||||
this.signatureDescription = signatureDescription; | this.signatureDescription = signatureDescription; | ||||
} | } | ||||
/** | /** | ||||
* @return the default canonicalization method, defaults to INCLUSIVE | * @return the default canonicalization method, defaults to INCLUSIVE | ||||
*/ | */ | ||||
public String getCanonicalizationMethod() { | public String getCanonicalizationMethod() { | ||||
return canonicalizationMethod; | return canonicalizationMethod; | ||||
} | } | ||||
/** | /** | ||||
* @param canonicalizationMethod the default canonicalization method | * @param canonicalizationMethod the default canonicalization method | ||||
*/ | */ | ||||
public void setTspUrl(String tspUrl) { | public void setTspUrl(String tspUrl) { | ||||
this.tspUrl = tspUrl; | this.tspUrl = tspUrl; | ||||
} | } | ||||
/** | /** | ||||
* @return if true, uses timestamp-request/response mimetype, | * @return if true, uses timestamp-request/response mimetype, | ||||
* if false, timestamp-query/reply mimetype | |||||
* if false, timestamp-query/reply mimetype | |||||
*/ | */ | ||||
public boolean isTspOldProtocol() { | public boolean isTspOldProtocol() { | ||||
return tspOldProtocol; | return tspOldProtocol; | ||||
} | } | ||||
/** | /** | ||||
* @param tspOldProtocol defines the timestamp-protocol mimetype | * @param tspOldProtocol defines the timestamp-protocol mimetype | ||||
* @see #isTspOldProtocol | * @see #isTspOldProtocol | ||||
public void setTspOldProtocol(boolean tspOldProtocol) { | public void setTspOldProtocol(boolean tspOldProtocol) { | ||||
this.tspOldProtocol = tspOldProtocol; | this.tspOldProtocol = tspOldProtocol; | ||||
} | } | ||||
/** | /** | ||||
* @return the hash algorithm to be used for the timestamp entry. | * @return the hash algorithm to be used for the timestamp entry. | ||||
* Defaults to the hash algorithm of the main entry | * Defaults to the hash algorithm of the main entry | ||||
public HashAlgorithm getTspDigestAlgo() { | public HashAlgorithm getTspDigestAlgo() { | ||||
return nvl(tspDigestAlgo,digestAlgo); | return nvl(tspDigestAlgo,digestAlgo); | ||||
} | } | ||||
/** | /** | ||||
* @param tspDigestAlgo the algorithm to be used for the timestamp entry. | * @param tspDigestAlgo the algorithm to be used for the timestamp entry. | ||||
* if <code>null</code>, the hash algorithm of the main entry | * if <code>null</code>, the hash algorithm of the main entry | ||||
public String getProxyUrl() { | public String getProxyUrl() { | ||||
return proxyUrl; | return proxyUrl; | ||||
} | } | ||||
/** | /** | ||||
* @param proxyUrl the proxy url to be used for all communications. | * @param proxyUrl the proxy url to be used for all communications. | ||||
* Currently this affects the timestamp service | * Currently this affects the timestamp service | ||||
public void setProxyUrl(String proxyUrl) { | public void setProxyUrl(String proxyUrl) { | ||||
this.proxyUrl = proxyUrl; | this.proxyUrl = proxyUrl; | ||||
} | } | ||||
/** | /** | ||||
* @return the timestamp service. Defaults to {@link TSPTimeStampService} | * @return the timestamp service. Defaults to {@link TSPTimeStampService} | ||||
*/ | */ | ||||
public TimeStampService getTspService() { | public TimeStampService getTspService() { | ||||
return tspService; | return tspService; | ||||
} | } | ||||
/** | /** | ||||
* @param tspService the timestamp service | * @param tspService the timestamp service | ||||
*/ | */ | ||||
public void setTspService(TimeStampService tspService) { | public void setTspService(TimeStampService tspService) { | ||||
this.tspService = tspService; | this.tspService = tspService; | ||||
} | } | ||||
/** | /** | ||||
* @return the user id for the timestamp service - currently only basic authorization is supported | * @return the user id for the timestamp service - currently only basic authorization is supported | ||||
*/ | */ | ||||
public String getTspUser() { | public String getTspUser() { | ||||
return tspUser; | return tspUser; | ||||
} | } | ||||
/** | /** | ||||
* @param tspUser the user id for the timestamp service - currently only basic authorization is supported | * @param tspUser the user id for the timestamp service - currently only basic authorization is supported | ||||
*/ | */ | ||||
public void setTspUser(String tspUser) { | public void setTspUser(String tspUser) { | ||||
this.tspUser = tspUser; | this.tspUser = tspUser; | ||||
} | } | ||||
/** | /** | ||||
* @return the password for the timestamp service | * @return the password for the timestamp service | ||||
*/ | */ | ||||
public String getTspPass() { | public String getTspPass() { | ||||
return tspPass; | return tspPass; | ||||
} | } | ||||
/** | /** | ||||
* @param tspPass the password for the timestamp service | * @param tspPass the password for the timestamp service | ||||
*/ | */ | ||||
public void setTspPass(String tspPass) { | public void setTspPass(String tspPass) { | ||||
this.tspPass = tspPass; | this.tspPass = tspPass; | ||||
} | } | ||||
/** | /** | ||||
* @return the validator for the timestamp service (certificate) | * @return the validator for the timestamp service (certificate) | ||||
*/ | */ | ||||
public TimeStampServiceValidator getTspValidator() { | public TimeStampServiceValidator getTspValidator() { | ||||
return tspValidator; | return tspValidator; | ||||
} | } | ||||
/** | /** | ||||
* @param tspValidator the validator for the timestamp service (certificate) | * @param tspValidator the validator for the timestamp service (certificate) | ||||
*/ | */ | ||||
public HashAlgorithm getXadesDigestAlgo() { | public HashAlgorithm getXadesDigestAlgo() { | ||||
return nvl(xadesDigestAlgo,digestAlgo); | return nvl(xadesDigestAlgo,digestAlgo); | ||||
} | } | ||||
/** | /** | ||||
* @param xadesDigestAlgo hash algorithm used for XAdES. | * @param xadesDigestAlgo hash algorithm used for XAdES. | ||||
* When <code>null</code>, defaults to {@link #getDigestAlgo()} | * When <code>null</code>, defaults to {@link #getDigestAlgo()} | ||||
public String getUserAgent() { | public String getUserAgent() { | ||||
return userAgent; | return userAgent; | ||||
} | } | ||||
/** | /** | ||||
* @param userAgent the user agent used for http communication (e.g. to the TSP) | * @param userAgent the user agent used for http communication (e.g. to the TSP) | ||||
*/ | */ | ||||
public String getTspRequestPolicy() { | public String getTspRequestPolicy() { | ||||
return tspRequestPolicy; | return tspRequestPolicy; | ||||
} | } | ||||
/** | /** | ||||
* @param tspRequestPolicy the asn.1 object id for the tsp request policy. | * @param tspRequestPolicy the asn.1 object id for the tsp request policy. | ||||
*/ | */ | ||||
/** | /** | ||||
* @return true, if the whole certificate chain is included in the signature. | * @return true, if the whole certificate chain is included in the signature. | ||||
* When false, only the signer cert will be included | |||||
* When false, only the signer cert will be included | |||||
*/ | */ | ||||
public boolean isIncludeEntireCertificateChain() { | public boolean isIncludeEntireCertificateChain() { | ||||
return includeEntireCertificateChain; | return includeEntireCertificateChain; | ||||
* Make sure the DN is encoded using the same order as present | * Make sure the DN is encoded using the same order as present | ||||
* within the certificate. This is an Office2010 work-around. | * within the certificate. This is an Office2010 work-around. | ||||
* Should be reverted back. | * Should be reverted back. | ||||
* | |||||
* | |||||
* XXX: not correct according to RFC 4514. | * XXX: not correct according to RFC 4514. | ||||
* | * | ||||
* @return when true, the issuer DN is used instead of the issuer X500 principal | * @return when true, the issuer DN is used instead of the issuer X500 principal | ||||
this.xadesIssuerNameNoReverseOrder = xadesIssuerNameNoReverseOrder; | this.xadesIssuerNameNoReverseOrder = xadesIssuerNameNoReverseOrder; | ||||
} | } | ||||
/** | /** | ||||
* @return the event listener which is active while xml structure for | * @return the event listener which is active while xml structure for | ||||
* the signature is created. | * the signature is created. | ||||
/** | /** | ||||
* Converts the digest algorithm - currently only sha* and ripemd160 is supported. | * Converts the digest algorithm - currently only sha* and ripemd160 is supported. | ||||
* MS Office only supports sha1, sha256, sha384, sha512. | |||||
* | |||||
* MS Office only supports sha1, sha256, sha384, sha512. | |||||
* | |||||
* @param digestAlgo the digest algorithm | * @param digestAlgo the digest algorithm | ||||
* @return the uri for the given digest | * @return the uri for the given digest | ||||
*/ | */ | ||||
* Converts the digest algorithm ur - currently only sha* and ripemd160 is supported. | * Converts the digest algorithm ur - currently only sha* and ripemd160 is supported. | ||||
* MS Office only supports sha1, sha256, sha384, sha512. | * MS Office only supports sha1, sha256, sha384, sha512. | ||||
* | * | ||||
* @param digestAlgo the digest algorithm uri | |||||
* @param digestMethodUri the digest algorithm uri | |||||
* @return the hash algorithm for the given digest | * @return the hash algorithm for the given digest | ||||
*/ | */ | ||||
private static HashAlgorithm getDigestMethodAlgo(String digestMethodUri) { | private static HashAlgorithm getDigestMethodAlgo(String digestMethodUri) { | ||||
public void setSignatureFactory(XMLSignatureFactory signatureFactory) { | public void setSignatureFactory(XMLSignatureFactory signatureFactory) { | ||||
this.signatureFactory.set(signatureFactory); | this.signatureFactory.set(signatureFactory); | ||||
} | } | ||||
/** | /** | ||||
* @return the xml signature factory (thread-local) | * @return the xml signature factory (thread-local) | ||||
*/ | */ | ||||
public void setKeyInfoFactory(KeyInfoFactory keyInfoFactory) { | public void setKeyInfoFactory(KeyInfoFactory keyInfoFactory) { | ||||
this.keyInfoFactory.set(keyInfoFactory); | this.keyInfoFactory.set(keyInfoFactory); | ||||
} | } | ||||
/** | /** | ||||
* @return the key factory (thread-local) | * @return the key factory (thread-local) | ||||
*/ | */ | ||||
* <li>the Santuario xmlsec provider</li> | * <li>the Santuario xmlsec provider</li> | ||||
* <li>the JDK xmlsec provider</li> | * <li>the JDK xmlsec provider</li> | ||||
* </ul> | * </ul> | ||||
* | |||||
* | |||||
* For signing the classes are linked against the Santuario xmlsec, so this might | * For signing the classes are linked against the Santuario xmlsec, so this might | ||||
* only work for validation (not tested). | * only work for validation (not tested). | ||||
* | |||||
* | |||||
* @return the xml dsig provider | * @return the xml dsig provider | ||||
*/ | */ | ||||
public Provider getProvider() { | public Provider getProvider() { | ||||
// Santuario xmlsec | // Santuario xmlsec | ||||
"org.apache.jcp.xml.dsig.internal.dom.XMLDSigRI", | "org.apache.jcp.xml.dsig.internal.dom.XMLDSigRI", | ||||
// JDK xmlsec | // JDK xmlsec | ||||
"org.jcp.xml.dsig.internal.dom.XMLDSigRI" | |||||
"org.jcp.xml.dsig.internal.dom.XMLDSigRI" | |||||
}; | }; | ||||
for (String pn : dsigProviderNames) { | for (String pn : dsigProviderNames) { | ||||
if (pn == null) { | if (pn == null) { | ||||
if (prov == null) { | if (prov == null) { | ||||
throw new RuntimeException("JRE doesn't support default xml signature provider - set jsr105Provider system property!"); | throw new RuntimeException("JRE doesn't support default xml signature provider - set jsr105Provider system property!"); | ||||
} | } | ||||
return prov; | return prov; | ||||
} | } | ||||
_parentPage = parentPage; | _parentPage = parentPage; | ||||
TextType text = shapeSheet.getText(); | TextType text = shapeSheet.getText(); | ||||
if (text != null) | |||||
if (text != null) { | |||||
_text = new XDGFText(text, this); | _text = new XDGFText(text, this); | ||||
} | |||||
if (shapeSheet.isSetShapes()) { | if (shapeSheet.isSetShapes()) { | ||||
_shapes = new ArrayList<>(); | _shapes = new ArrayList<>(); | ||||
for (ShapeSheetType shape : shapeSheet.getShapes().getShapeArray()) | |||||
for (ShapeSheetType shape : shapeSheet.getShapes().getShapeArray()) { | |||||
_shapes.add(new XDGFShape(this, shape, parentPage, document)); | _shapes.add(new XDGFShape(this, shape, parentPage, document)); | ||||
} | |||||
} | } | ||||
readProperties(); | readProperties(); | ||||
@Override | @Override | ||||
public String toString() { | public String toString() { | ||||
if (_parentPage instanceof XDGFMasterContents) | |||||
if (_parentPage instanceof XDGFMasterContents) { | |||||
return _parentPage + ": <Shape ID=\"" + getID() + "\">"; | return _parentPage + ": <Shape ID=\"" + getID() + "\">"; | ||||
else | |||||
} else { | |||||
return "<Shape ID=\"" + getID() + "\">"; | return "<Shape ID=\"" + getID() + "\">"; | ||||
} | |||||
} | } | ||||
protected void readProperties() { | protected void readProperties() { | ||||
if (obj.isSetMaster()) { | if (obj.isSetMaster()) { | ||||
_master = pageContents.getMasterById(obj.getMaster()); | _master = pageContents.getMasterById(obj.getMaster()); | ||||
if (_master == null) | |||||
if (_master == null) { | |||||
throw XDGFException.error("refers to non-existant master " | throw XDGFException.error("refers to non-existant master " | ||||
+ obj.getMaster(), this); | + obj.getMaster(), this); | ||||
} | |||||
/* | /* | ||||
* If a master has one top-level shape, a shape that inherits from | * If a master has one top-level shape, a shape that inherits from | ||||
} | } | ||||
} else if (obj.isSetMasterShape()) { | } else if (obj.isSetMasterShape()) { | ||||
_masterShape = master.getShapeById(obj.getMasterShape()); | |||||
if (_masterShape == null) | |||||
_masterShape = (master == null) ? null : master.getShapeById(obj.getMasterShape()); | |||||
if (_masterShape == null) { | |||||
throw XDGFException.error( | throw XDGFException.error( | ||||
"refers to non-existant master shape " | "refers to non-existant master shape " | ||||
+ obj.getMasterShape(), this); | + obj.getMasterShape(), this); | ||||
} | |||||
} | } | ||||
protected void setupSectionMasters() { | protected void setupSectionMasters() { | ||||
if (_masterShape == null) | |||||
if (_masterShape == null) { | |||||
return; | return; | ||||
} | |||||
try { | try { | ||||
for (Entry<String, XDGFSection> section : _sections.entrySet()) { | for (Entry<String, XDGFSection> section : _sections.entrySet()) { | ||||
XDGFSection master = _masterShape.getSection(section.getKey()); | XDGFSection master = _masterShape.getSection(section.getKey()); | ||||
if (master != null) | |||||
if (master != null) { | |||||
section.getValue().setupMaster(master); | section.getValue().setupMaster(master); | ||||
} | |||||
} | } | ||||
for (Entry<Long, GeometrySection> section : _geometry.entrySet()) { | for (Entry<Long, GeometrySection> section : _geometry.entrySet()) { | ||||
GeometrySection master = _masterShape.getGeometryByIdx(section | GeometrySection master = _masterShape.getGeometryByIdx(section | ||||
.getKey()); | .getKey()); | ||||
if (master != null) | |||||
if (master != null) { | |||||
section.getValue().setupMaster(master); | section.getValue().setupMaster(master); | ||||
} | |||||
} | } | ||||
} catch (POIXMLException e) { | } catch (POIXMLException e) { | ||||
throw XDGFException.wrap(this.toString(), e); | throw XDGFException.wrap(this.toString(), e); | ||||
public String getTextAsString() { | public String getTextAsString() { | ||||
XDGFText text = getText(); | XDGFText text = getText(); | ||||
if (text == null) | |||||
if (text == null) { | |||||
return ""; | return ""; | ||||
} | |||||
return text.getTextContent(); | return text.getTextContent(); | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Only available if this shape is a shape group, may be null | |||||
* Only available if this shape is a shape group, may be null | |||||
*/ | */ | ||||
// -> May be null | // -> May be null | ||||
public List<XDGFShape> getShapes() { | public List<XDGFShape> getShapes() { | ||||
// unique to this shape on the page? | // unique to this shape on the page? | ||||
public String getName() { | public String getName() { | ||||
String name = getXmlObject().getName(); | String name = getXmlObject().getName(); | ||||
if (name == null) | |||||
if (name == null) { | |||||
return ""; | return ""; | ||||
} | |||||
return name; | return name; | ||||
} | } | ||||
// unique to this shape on the page? | // unique to this shape on the page? | ||||
public String getShapeType() { | public String getShapeType() { | ||||
String type = getXmlObject().getType(); | String type = getXmlObject().getType(); | ||||
if (type == null) | |||||
if (type == null) { | |||||
return ""; | return ""; | ||||
} | |||||
return type; | return type; | ||||
} | } | ||||
// name of the symbol that this was derived from | // name of the symbol that this was derived from | ||||
public String getSymbolName() { | public String getSymbolName() { | ||||
if (_master == null) | |||||
if (_master == null) { | |||||
return ""; | return ""; | ||||
} | |||||
String name = _master.getName(); | String name = _master.getName(); | ||||
if (name == null) | |||||
if (name == null) { | |||||
return ""; | return ""; | ||||
} | |||||
return name; | return name; | ||||
} | } | ||||
XDGFShape top = null; | XDGFShape top = null; | ||||
if (_parent != null) { | if (_parent != null) { | ||||
top = _parent.getTopmostParentShape(); | top = _parent.getTopmostParentShape(); | ||||
if (top == null) | |||||
if (top == null) { | |||||
top = _parent; | top = _parent; | ||||
} | |||||
} | } | ||||
return top; | return top; | ||||
} | } | ||||
public XDGFText getText() { | public XDGFText getText() { | ||||
if (_text == null && _masterShape != null) | |||||
if (_text == null && _masterShape != null) { | |||||
return _masterShape.getText(); | return _masterShape.getText(); | ||||
} | |||||
return _text; | return _text; | ||||
} | } | ||||
public Double getPinX() { | public Double getPinX() { | ||||
if (_pinX == null && _masterShape != null) | |||||
if (_pinX == null && _masterShape != null) { | |||||
return _masterShape.getPinX(); | return _masterShape.getPinX(); | ||||
} | |||||
if (_pinX == null) | |||||
if (_pinX == null) { | |||||
throw XDGFException.error("PinX not set!", this); | throw XDGFException.error("PinX not set!", this); | ||||
} | |||||
return _pinX; | return _pinX; | ||||
} | } | ||||
public Double getPinY() { | public Double getPinY() { | ||||
if (_pinY == null && _masterShape != null) | |||||
if (_pinY == null && _masterShape != null) { | |||||
return _masterShape.getPinY(); | return _masterShape.getPinY(); | ||||
} | |||||
if (_pinY == null) | |||||
if (_pinY == null) { | |||||
throw XDGFException.error("PinY not specified!", this); | throw XDGFException.error("PinY not specified!", this); | ||||
} | |||||
return _pinY; | return _pinY; | ||||
} | } | ||||
public Double getWidth() { | public Double getWidth() { | ||||
if (_width == null && _masterShape != null) | |||||
if (_width == null && _masterShape != null) { | |||||
return _masterShape.getWidth(); | return _masterShape.getWidth(); | ||||
} | |||||
if (_width == null) | |||||
if (_width == null) { | |||||
throw XDGFException.error("Width not specified!", this); | throw XDGFException.error("Width not specified!", this); | ||||
} | |||||
return _width; | return _width; | ||||
} | } | ||||
public Double getHeight() { | public Double getHeight() { | ||||
if (_height == null && _masterShape != null) | |||||
if (_height == null && _masterShape != null) { | |||||
return _masterShape.getHeight(); | return _masterShape.getHeight(); | ||||
} | |||||
if (_height == null) | |||||
if (_height == null) { | |||||
throw XDGFException.error("Height not specified!", this); | throw XDGFException.error("Height not specified!", this); | ||||
} | |||||
return _height; | return _height; | ||||
} | } | ||||
public Double getLocPinX() { | public Double getLocPinX() { | ||||
if (_locPinX == null && _masterShape != null) | |||||
if (_locPinX == null && _masterShape != null) { | |||||
return _masterShape.getLocPinX(); | return _masterShape.getLocPinX(); | ||||
} | |||||
if (_locPinX == null) | |||||
if (_locPinX == null) { | |||||
throw XDGFException.error("LocPinX not specified!", this); | throw XDGFException.error("LocPinX not specified!", this); | ||||
} | |||||
return _locPinX; | return _locPinX; | ||||
} | } | ||||
public Double getLocPinY() { | public Double getLocPinY() { | ||||
if (_locPinY == null && _masterShape != null) | |||||
if (_locPinY == null && _masterShape != null) { | |||||
return _masterShape.getLocPinY(); | return _masterShape.getLocPinY(); | ||||
} | |||||
if (_locPinY == null) | |||||
if (_locPinY == null) { | |||||
throw XDGFException.error("LocPinY not specified!", this); | throw XDGFException.error("LocPinY not specified!", this); | ||||
} | |||||
return _locPinY; | return _locPinY; | ||||
} | } | ||||
public Double getBeginX() { | public Double getBeginX() { | ||||
if (_beginX == null && _masterShape != null) | |||||
if (_beginX == null && _masterShape != null) { | |||||
return _masterShape.getBeginX(); | return _masterShape.getBeginX(); | ||||
} | |||||
return _beginX; | return _beginX; | ||||
} | } | ||||
public Double getBeginY() { | public Double getBeginY() { | ||||
if (_beginY == null && _masterShape != null) | |||||
if (_beginY == null && _masterShape != null) { | |||||
return _masterShape.getBeginY(); | return _masterShape.getBeginY(); | ||||
} | |||||
return _beginY; | return _beginY; | ||||
} | } | ||||
public Double getEndX() { | public Double getEndX() { | ||||
if (_endX == null && _masterShape != null) | |||||
if (_endX == null && _masterShape != null) { | |||||
return _masterShape.getEndX(); | return _masterShape.getEndX(); | ||||
} | |||||
return _endX; | return _endX; | ||||
} | } | ||||
public Double getEndY() { | public Double getEndY() { | ||||
if (_endY == null && _masterShape != null) | |||||
if (_endY == null && _masterShape != null) { | |||||
return _masterShape.getEndY(); | return _masterShape.getEndY(); | ||||
} | |||||
return _endY; | return _endY; | ||||
} | } | ||||
public Double getAngle() { | public Double getAngle() { | ||||
if (_angle == null && _masterShape != null) | |||||
if (_angle == null && _masterShape != null) { | |||||
return _masterShape.getAngle(); | return _masterShape.getAngle(); | ||||
} | |||||
return _angle; | return _angle; | ||||
} | } | ||||
public Boolean getFlipX() { | public Boolean getFlipX() { | ||||
if (_flipX == null && _masterShape != null) | |||||
if (_flipX == null && _masterShape != null) { | |||||
return _masterShape.getFlipX(); | return _masterShape.getFlipX(); | ||||
} | |||||
return _flipX; | return _flipX; | ||||
} | } | ||||
public Boolean getFlipY() { | public Boolean getFlipY() { | ||||
if (_flipY == null && _masterShape != null) | |||||
if (_flipY == null && _masterShape != null) { | |||||
return _masterShape.getFlipY(); | return _masterShape.getFlipY(); | ||||
} | |||||
return _flipY; | return _flipY; | ||||
} | } | ||||
public Double getTxtPinX() { | public Double getTxtPinX() { | ||||
if (_txtPinX == null && _masterShape != null | if (_txtPinX == null && _masterShape != null | ||||
&& _masterShape._txtPinX != null) | |||||
&& _masterShape._txtPinX != null) { | |||||
return _masterShape._txtPinX; | return _masterShape._txtPinX; | ||||
} | |||||
if (_txtPinX == null) | |||||
if (_txtPinX == null) { | |||||
return getWidth() * 0.5; | return getWidth() * 0.5; | ||||
} | |||||
return _txtPinX; | return _txtPinX; | ||||
} | } | ||||
public Double getTxtPinY() { | public Double getTxtPinY() { | ||||
if (_txtLocPinY == null && _masterShape != null | if (_txtLocPinY == null && _masterShape != null | ||||
&& _masterShape._txtLocPinY != null) | |||||
&& _masterShape._txtLocPinY != null) { | |||||
return _masterShape._txtLocPinY; | return _masterShape._txtLocPinY; | ||||
} | |||||
if (_txtPinY == null) | |||||
if (_txtPinY == null) { | |||||
return getHeight() * 0.5; | return getHeight() * 0.5; | ||||
} | |||||
return _txtPinY; | return _txtPinY; | ||||
} | } | ||||
public Double getTxtLocPinX() { | public Double getTxtLocPinX() { | ||||
if (_txtLocPinX == null && _masterShape != null | if (_txtLocPinX == null && _masterShape != null | ||||
&& _masterShape._txtLocPinX != null) | |||||
&& _masterShape._txtLocPinX != null) { | |||||
return _masterShape._txtLocPinX; | return _masterShape._txtLocPinX; | ||||
} | |||||
if (_txtLocPinX == null) | |||||
if (_txtLocPinX == null) { | |||||
return getTxtWidth() * 0.5; | return getTxtWidth() * 0.5; | ||||
} | |||||
return _txtLocPinX; | return _txtLocPinX; | ||||
} | } | ||||
public Double getTxtLocPinY() { | public Double getTxtLocPinY() { | ||||
if (_txtLocPinY == null && _masterShape != null | if (_txtLocPinY == null && _masterShape != null | ||||
&& _masterShape._txtLocPinY != null) | |||||
&& _masterShape._txtLocPinY != null) { | |||||
return _masterShape._txtLocPinY; | return _masterShape._txtLocPinY; | ||||
} | |||||
if (_txtLocPinY == null) | |||||
if (_txtLocPinY == null) { | |||||
return getTxtHeight() * 0.5; | return getTxtHeight() * 0.5; | ||||
} | |||||
return _txtLocPinY; | return _txtLocPinY; | ||||
} | } | ||||
public Double getTxtAngle() { | public Double getTxtAngle() { | ||||
if (_txtAngle == null && _masterShape != null) | |||||
if (_txtAngle == null && _masterShape != null) { | |||||
return _masterShape.getTxtAngle(); | return _masterShape.getTxtAngle(); | ||||
} | |||||
return _txtAngle; | return _txtAngle; | ||||
} | } | ||||
public Double getTxtWidth() { | public Double getTxtWidth() { | ||||
if (_txtWidth == null && _masterShape != null | if (_txtWidth == null && _masterShape != null | ||||
&& _masterShape._txtWidth != null) | |||||
&& _masterShape._txtWidth != null) { | |||||
return _masterShape._txtWidth; | return _masterShape._txtWidth; | ||||
} | |||||
if (_txtWidth == null) | |||||
if (_txtWidth == null) { | |||||
return getWidth(); | return getWidth(); | ||||
} | |||||
return _txtWidth; | return _txtWidth; | ||||
} | } | ||||
public Double getTxtHeight() { | public Double getTxtHeight() { | ||||
if (_txtHeight == null && _masterShape != null | if (_txtHeight == null && _masterShape != null | ||||
&& _masterShape._txtHeight != null) | |||||
&& _masterShape._txtHeight != null) { | |||||
return _masterShape._txtHeight; | return _masterShape._txtHeight; | ||||
} | |||||
if (_txtHeight == null) | |||||
if (_txtHeight == null) { | |||||
return getHeight(); | return getHeight(); | ||||
} | |||||
return _txtHeight; | return _txtHeight; | ||||
} | } | ||||
public Integer getLineCap() { | public Integer getLineCap() { | ||||
Integer lineCap = super.getLineCap(); | Integer lineCap = super.getLineCap(); | ||||
if (lineCap != null) | |||||
if (lineCap != null) { | |||||
return lineCap; | return lineCap; | ||||
} | |||||
// get from master | // get from master | ||||
if (_masterShape != null) { | if (_masterShape != null) { | ||||
public Color getLineColor() { | public Color getLineColor() { | ||||
Color lineColor = super.getLineColor(); | Color lineColor = super.getLineColor(); | ||||
if (lineColor != null) | |||||
if (lineColor != null) { | |||||
return lineColor; | return lineColor; | ||||
} | |||||
// get from master | // get from master | ||||
if (_masterShape != null) { | if (_masterShape != null) { | ||||
public Integer getLinePattern() { | public Integer getLinePattern() { | ||||
Integer linePattern = super.getLinePattern(); | Integer linePattern = super.getLinePattern(); | ||||
if (linePattern != null) | |||||
if (linePattern != null) { | |||||
return linePattern; | return linePattern; | ||||
} | |||||
// get from master | // get from master | ||||
if (_masterShape != null) { | if (_masterShape != null) { | ||||
public Double getLineWeight() { | public Double getLineWeight() { | ||||
Double lineWeight = super.getLineWeight(); | Double lineWeight = super.getLineWeight(); | ||||
if (lineWeight != null) | |||||
if (lineWeight != null) { | |||||
return lineWeight; | return lineWeight; | ||||
} | |||||
// get from master | // get from master | ||||
if (_masterShape != null) { | if (_masterShape != null) { | ||||
public Color getFontColor() { | public Color getFontColor() { | ||||
Color fontColor = super.getFontColor(); | Color fontColor = super.getFontColor(); | ||||
if (fontColor != null) | |||||
if (fontColor != null) { | |||||
return fontColor; | return fontColor; | ||||
} | |||||
// get from master | // get from master | ||||
if (_masterShape != null) { | if (_masterShape != null) { | ||||
public Double getFontSize() { | public Double getFontSize() { | ||||
Double fontSize = super.getFontSize(); | Double fontSize = super.getFontSize(); | ||||
if (fontSize != null) | |||||
if (fontSize != null) { | |||||
return fontSize; | return fontSize; | ||||
} | |||||
// get from master | // get from master | ||||
if (_masterShape != null) { | if (_masterShape != null) { | ||||
public Rectangle2D.Double getBounds() { | public Rectangle2D.Double getBounds() { | ||||
return new Rectangle2D.Double(0, 0, getWidth(), getHeight()); | return new Rectangle2D.Double(0, 0, getWidth(), getHeight()); | ||||
} | } | ||||
/** | /** | ||||
* @return returns bounds as a path in local coordinates, which is | * @return returns bounds as a path in local coordinates, which is | ||||
* userful if you need to transform to global coordinates | * userful if you need to transform to global coordinates | ||||
* | |||||
* | |||||
* Warning: Don't use this for 1d objects, and will fail for | * Warning: Don't use this for 1d objects, and will fail for | ||||
* infinite line objects | * infinite line objects | ||||
*/ | */ | ||||
*/ | */ | ||||
public Path2D.Double getPath() { | public Path2D.Double getPath() { | ||||
for (GeometrySection geoSection : getGeometrySections()) { | for (GeometrySection geoSection : getGeometrySections()) { | ||||
if (geoSection.getNoShow()) | |||||
if (geoSection.getNoShow()) { | |||||
continue; | continue; | ||||
} | |||||
return geoSection.getPath(this); | return geoSection.getPath(this); | ||||
} | } | ||||
*/ | */ | ||||
public boolean hasGeometry() { | public boolean hasGeometry() { | ||||
for (GeometrySection geoSection : getGeometrySections()) { | for (GeometrySection geoSection : getGeometrySections()) { | ||||
if (!geoSection.getNoShow()) | |||||
if (!geoSection.getNoShow()) { | |||||
return true; | return true; | ||||
} | |||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
tr.concatenate(getParentTransform()); | tr.concatenate(getParentTransform()); | ||||
try { | try { | ||||
if (visitor.accept(this)) | |||||
if (visitor.accept(this)) { | |||||
visitor.visit(this, tr, level); | visitor.visit(this, tr, level); | ||||
} | |||||
if (_shapes != null) { | if (_shapes != null) { | ||||
for (XDGFShape shape : _shapes) { | for (XDGFShape shape : _shapes) { | ||||
public void visitShapes(ShapeVisitor visitor, int level) { | public void visitShapes(ShapeVisitor visitor, int level) { | ||||
try { | try { | ||||
if (visitor.accept(this)) | |||||
if (visitor.accept(this)) { | |||||
visitor.visit(this, null, level); | visitor.visit(this, null, level); | ||||
} | |||||
if (_shapes != null) { | if (_shapes != null) { | ||||
for (XDGFShape shape : _shapes) { | for (XDGFShape shape : _shapes) { |
// is a mixed type) | // is a mixed type) | ||||
return ((TextTypeImpl) _text).getStringValue(); | return ((TextTypeImpl) _text).getStringValue(); | ||||
} | } | ||||
/** | /** | ||||
* These are in the shape coordinate system | * These are in the shape coordinate system | ||||
* | * | ||||
public Path2D.Double getBoundsAsPath() { | public Path2D.Double getBoundsAsPath() { | ||||
Rectangle2D.Double rect = getTextBounds(); | Rectangle2D.Double rect = getTextBounds(); | ||||
Double w = rect.getWidth(); | |||||
Double h = rect.getHeight(); | |||||
double w = rect.getWidth(); | |||||
double h = rect.getHeight(); | |||||
Path2D.Double bounds = new Path2D.Double(); | Path2D.Double bounds = new Path2D.Double(); | ||||
bounds.moveTo(0, 0); | bounds.moveTo(0, 0); | ||||
return bounds; | return bounds; | ||||
} | } | ||||
/** | /** | ||||
* @return Center of text in local coordinates | * @return Center of text in local coordinates | ||||
*/ | */ | ||||
public void draw(Graphics2D graphics) { | public void draw(Graphics2D graphics) { | ||||
String textContent = getTextContent(); | String textContent = getTextContent(); | ||||
if (textContent.length() == 0) | |||||
if (textContent.length() == 0) { | |||||
return; | return; | ||||
} | |||||
Rectangle2D.Double bounds = getTextBounds(); | Rectangle2D.Double bounds = getTextBounds(); | ||||
} | } | ||||
Double txtAngle = _parent.getTxtAngle(); | Double txtAngle = _parent.getTxtAngle(); | ||||
if (txtAngle != null && Math.abs(txtAngle) > 0.01) | |||||
if (txtAngle != null && Math.abs(txtAngle) > 0.01) { | |||||
graphics.rotate(txtAngle); | graphics.rotate(txtAngle); | ||||
} | |||||
float nextY = 0; | float nextY = 0; | ||||
for (String line : lines) { | for (String line : lines) { | ||||
if (line.length() == 0) | |||||
if (line.length() == 0) { | |||||
continue; | continue; | ||||
} | |||||
TextLayout layout = new TextLayout(line, font, frc); | TextLayout layout = new TextLayout(line, font, frc); | ||||
if (layout.isLeftToRight()) | |||||
if (layout.isLeftToRight()) { | |||||
layout.draw(graphics, 0, nextY); | layout.draw(graphics, 0, nextY); | ||||
else | |||||
} else { | |||||
layout.draw(graphics, | layout.draw(graphics, | ||||
(float) (bounds.width - layout.getAdvance()), nextY); | (float) (bounds.width - layout.getAdvance()), nextY); | ||||
} | |||||
nextY += layout.getAscent() + layout.getDescent() | nextY += layout.getAscent() + layout.getDescent() | ||||
+ layout.getLeading(); | + layout.getLeading(); |
import java.io.FileInputStream; | import java.io.FileInputStream; | ||||
import java.io.FileNotFoundException; | import java.io.FileNotFoundException; | ||||
import java.io.FileOutputStream; | import java.io.FileOutputStream; | ||||
import java.io.IOException; | |||||
import java.io.OutputStream; | import java.io.OutputStream; | ||||
import java.io.PrintStream; | import java.io.PrintStream; | ||||
import java.io.UnsupportedEncodingException; | import java.io.UnsupportedEncodingException; | ||||
public class HierarchyPrinter { | public class HierarchyPrinter { | ||||
public static void printHierarchy(XDGFPage page, File outDir) | public static void printHierarchy(XDGFPage page, File outDir) | ||||
throws FileNotFoundException, UnsupportedEncodingException { | |||||
throws FileNotFoundException, UnsupportedEncodingException, IOException { | |||||
File pageFile = new File(outDir, "page" + page.getPageNumber() + "-" | File pageFile = new File(outDir, "page" + page.getPageNumber() + "-" | ||||
+ Util.sanitizeFilename(page.getName()) + ".txt"); | + Util.sanitizeFilename(page.getName()) + ".txt"); | ||||
OutputStream os = new FileOutputStream(pageFile); | |||||
PrintStream pos = new PrintStream(os, false, "utf-8"); | |||||
printHierarchy(page, pos); | |||||
pos.close(); | |||||
try ( | |||||
OutputStream os = new FileOutputStream(pageFile); | |||||
PrintStream pos = new PrintStream(os, false, "utf-8") | |||||
) { | |||||
printHierarchy(page, pos); | |||||
} | |||||
} | } | ||||
public static void printHierarchy(XDGFPage page, final PrintStream os) { | public static void printHierarchy(XDGFPage page, final PrintStream os) { | ||||
} | } | ||||
public static void printHierarchy(XmlVisioDocument document, | public static void printHierarchy(XmlVisioDocument document, | ||||
String outDirname) throws FileNotFoundException, UnsupportedEncodingException { | |||||
String outDirname) throws FileNotFoundException, UnsupportedEncodingException, IOException { | |||||
File outDir = new File(outDirname); | File outDir = new File(outDirname); | ||||
String inFilename = args[0]; | String inFilename = args[0]; | ||||
String outDir = args[1]; | String outDir = args[1]; | ||||
XmlVisioDocument doc = new XmlVisioDocument(new FileInputStream( | |||||
inFilename)); | |||||
printHierarchy(doc, outDir); | |||||
try (FileInputStream is = new FileInputStream(inFilename)) { | |||||
XmlVisioDocument doc = new XmlVisioDocument(is); | |||||
printHierarchy(doc, outDir); | |||||
} | |||||
} | } | ||||
} | } |
import java.awt.Graphics2D; | import java.awt.Graphics2D; | ||||
import java.awt.RenderingHints; | import java.awt.RenderingHints; | ||||
import java.awt.image.BufferedImage; | import java.awt.image.BufferedImage; | ||||
import java.io.*; | |||||
import java.io.File; | |||||
import java.io.FileInputStream; | |||||
import java.io.FileOutputStream; | |||||
import java.io.IOException; | |||||
import javax.imageio.ImageIO; | import javax.imageio.ImageIO; | ||||
/** | /** | ||||
* Converts a Visio diagram to a PNG file. | * Converts a Visio diagram to a PNG file. | ||||
* | |||||
* | |||||
* As more elements and styles are added/supported the output will get | * As more elements and styles are added/supported the output will get | ||||
* better, but it's very rough right now. | * better, but it's very rough right now. | ||||
*/ | */ | ||||
graphics.dispose(); | graphics.dispose(); | ||||
OutputStream out = new FileOutputStream(outFile); | |||||
try { | |||||
try (FileOutputStream out = new FileOutputStream(outFile)) { | |||||
ImageIO.write(img, "png", out); | ImageIO.write(img, "png", out); | ||||
} finally { | |||||
out.close(); | |||||
} | } | ||||
} | } | ||||
renderer = new ShapeDebuggerRenderer(); | renderer = new ShapeDebuggerRenderer(); | ||||
} | } | ||||
XmlVisioDocument doc = new XmlVisioDocument(new FileInputStream( | |||||
inFilename)); | |||||
renderToPng(doc, pngDir, 2000 / 11.0, renderer); | |||||
try (FileInputStream is = new FileInputStream(inFilename)) { | |||||
XmlVisioDocument doc = new XmlVisioDocument(is); | |||||
renderToPng(doc, pngDir, 2000 / 11.0, renderer); | |||||
} | |||||
} | } | ||||
} | } |
import org.apache.poi.ooxml.POIXMLDocumentPart; | import org.apache.poi.ooxml.POIXMLDocumentPart; | ||||
import org.apache.poi.ooxml.POIXMLException; | import org.apache.poi.ooxml.POIXMLException; | ||||
import org.apache.poi.ooxml.extractor.POIXMLPropertiesTextExtractor; | import org.apache.poi.ooxml.extractor.POIXMLPropertiesTextExtractor; | ||||
import org.apache.poi.ooxml.util.PackageHelper; | |||||
import org.apache.poi.openxml4j.exceptions.OpenXML4JException; | import org.apache.poi.openxml4j.exceptions.OpenXML4JException; | ||||
import org.apache.poi.openxml4j.opc.OPCPackage; | import org.apache.poi.openxml4j.opc.OPCPackage; | ||||
import org.apache.poi.openxml4j.opc.PackagePart; | import org.apache.poi.openxml4j.opc.PackagePart; | ||||
import org.apache.poi.util.LittleEndianConsts; | import org.apache.poi.util.LittleEndianConsts; | ||||
import org.apache.poi.util.POILogFactory; | import org.apache.poi.util.POILogFactory; | ||||
import org.apache.poi.util.POILogger; | import org.apache.poi.util.POILogger; | ||||
import org.apache.poi.ooxml.util.PackageHelper; | |||||
import org.apache.poi.util.Units; | import org.apache.poi.util.Units; | ||||
import org.apache.xmlbeans.XmlException; | import org.apache.xmlbeans.XmlException; | ||||
import org.apache.xmlbeans.XmlObject; | import org.apache.xmlbeans.XmlObject; | ||||
CTNotesMasterIdListEntry notesMasterId = notesMasterIdList.addNewNotesMasterId(); | CTNotesMasterIdListEntry notesMasterId = notesMasterIdList.addNewNotesMasterId(); | ||||
notesMasterId.setId(rp.getRelationship().getId()); | notesMasterId.setId(rp.getRelationship().getId()); | ||||
Integer themeIndex = 1; | |||||
int themeIndex = 1; | |||||
// TODO: check if that list can be replaced by idx = Math.max(idx,themeIdx) | // TODO: check if that list can be replaced by idx = Math.max(idx,themeIdx) | ||||
List<Integer> themeIndexList = new ArrayList<>(); | List<Integer> themeIndexList = new ArrayList<>(); | ||||
for (POIXMLDocumentPart p : getRelations()) { | for (POIXMLDocumentPart p : getRelations()) { | ||||
// TODO: implement! | // TODO: implement! | ||||
throw new UnsupportedOperationException(); | throw new UnsupportedOperationException(); | ||||
} | } | ||||
@Override | @Override | ||||
public POIXMLPropertiesTextExtractor getMetadataTextExtractor() { | public POIXMLPropertiesTextExtractor getMetadataTextExtractor() { | ||||
return new POIXMLPropertiesTextExtractor(this); | return new POIXMLPropertiesTextExtractor(this); |
@Internal | @Internal | ||||
public class XSLFColor { | public class XSLFColor { | ||||
private final static POILogger LOGGER = POILogFactory.getLogger(XSLFColor.class); | private final static POILogger LOGGER = POILogFactory.getLogger(XSLFColor.class); | ||||
private XmlObject _xmlObject; | private XmlObject _xmlObject; | ||||
private Color _color; | private Color _color; | ||||
private CTSchemeColor _phClr; | private CTSchemeColor _phClr; | ||||
} | } | ||||
}; | }; | ||||
} | } | ||||
private Color toColor(XmlObject obj, XSLFTheme theme) { | private Color toColor(XmlObject obj, XSLFTheme theme) { | ||||
Color color = null; | Color color = null; | ||||
for (XmlObject ch : obj.selectPath("*")) { | for (XmlObject ch : obj.selectPath("*")) { | ||||
if (fill.isSetScrgbClr()) { | if (fill.isSetScrgbClr()) { | ||||
fill.unsetScrgbClr(); | fill.unsetScrgbClr(); | ||||
} | } | ||||
if (fill.isSetHslClr()) { | if (fill.isSetHslClr()) { | ||||
fill.unsetHslClr(); | fill.unsetHslClr(); | ||||
} | } | ||||
if (fill.isSetPrstClr()) { | if (fill.isSetPrstClr()) { | ||||
fill.unsetPrstClr(); | fill.unsetPrstClr(); | ||||
} | } | ||||
if (fill.isSetSchemeClr()) { | if (fill.isSetSchemeClr()) { | ||||
fill.unsetSchemeClr(); | fill.unsetSchemeClr(); | ||||
} | } | ||||
if (fill.isSetSysClr()) { | if (fill.isSetSysClr()) { | ||||
fill.unsetSysClr(); | fill.unsetSysClr(); | ||||
} | } | ||||
float[] rgbaf = color.getRGBComponents(null); | float[] rgbaf = color.getRGBComponents(null); | ||||
boolean addAlpha = (rgbaf.length == 4 && rgbaf[3] < 1f); | boolean addAlpha = (rgbaf.length == 4 && rgbaf[3] < 1f); | ||||
CTPositiveFixedPercentage alphaPct; | CTPositiveFixedPercentage alphaPct; | ||||
// see office open xml part 4 - 5.1.2.2.30 and 5.1.2.2.32 | // see office open xml part 4 - 5.1.2.2.30 and 5.1.2.2.32 | ||||
if (isInt(rgbaf[0]) && isInt(rgbaf[1]) && isInt(rgbaf[2])) { | if (isInt(rgbaf[0]) && isInt(rgbaf[1]) && isInt(rgbaf[2])) { | ||||
// sRGB has a gamma of 2.2 | // sRGB has a gamma of 2.2 | ||||
CTSRgbColor rgb = fill.addNewSrgbClr(); | CTSRgbColor rgb = fill.addNewSrgbClr(); | ||||
byte rgbBytes[] = { (byte)color.getRed(), (byte)color.getGreen(), (byte)color.getBlue() }; | byte rgbBytes[] = { (byte)color.getRed(), (byte)color.getGreen(), (byte)color.getBlue() }; | ||||
rgb.setVal(rgbBytes); | rgb.setVal(rgbBytes); | ||||
alphaPct = (addAlpha) ? rgb.addNewAlpha() : null; | alphaPct = (addAlpha) ? rgb.addNewAlpha() : null; | ||||
alphaPct.setVal((int)(100000 * rgbaf[3])); | alphaPct.setVal((int)(100000 * rgbaf[3])); | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* @return true, if this is an integer color value | * @return true, if this is an integer color value | ||||
*/ | */ | ||||
private static boolean isInt(float f) { | private static boolean isInt(float f) { | ||||
return Math.abs((f*255f) - Math.rint(f*255f)) < 0.00001f; | |||||
return Math.abs((f*255d) - Math.rint(f*255d)) < 0.00001; | |||||
} | } | ||||
private int getRawValue(String elem) { | private int getRawValue(String elem) { | ||||
String query = "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' $this//a:" + elem; | String query = "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' $this//a:" + elem; | ||||
} | } | ||||
} | } | ||||
return -1; | |||||
return -1; | |||||
} | } | ||||
/** | /** | ||||
* Read a perecentage value from the supplied xml bean. | * Read a perecentage value from the supplied xml bean. | ||||
* Example: | * Example: | ||||
* or -1 if the value is not set | * or -1 if the value is not set | ||||
*/ | */ | ||||
int getAlpha(){ | int getAlpha(){ | ||||
return getPercentageValue("alpha"); | |||||
return getPercentageValue("alpha"); | |||||
} | } | ||||
/** | /** | ||||
/** | /** | ||||
* specifies the input color with the specific red component, but with the blue and green color | * specifies the input color with the specific red component, but with the blue and green color | ||||
* components unchanged | * components unchanged | ||||
* | |||||
* | |||||
* @return the value of the red component specified as a | * @return the value of the red component specified as a | ||||
* percentage with 0% indicating minimal blue and 100% indicating maximum | * percentage with 0% indicating minimal blue and 100% indicating maximum | ||||
* or -1 if the value is not set | * or -1 if the value is not set | ||||
/** | /** | ||||
* specifies a darker version of its input color. | * specifies a darker version of its input color. | ||||
* A 10% shade is 10% of the input color combined with 90% black. | * A 10% shade is 10% of the input color combined with 90% black. | ||||
* | |||||
* | |||||
* @return the value of the shade specified as a | * @return the value of the shade specified as a | ||||
* percentage with 0% indicating minimal shade and 100% indicating maximum | * percentage with 0% indicating minimal shade and 100% indicating maximum | ||||
* or -1 if the value is not set | * or -1 if the value is not set |
* | * | ||||
* @param part the package part holding the notes data, | * @param part the package part holding the notes data, | ||||
* the content type must be <code>application/vnd.openxmlformats-officedocument.notes+xml</code> | * the content type must be <code>application/vnd.openxmlformats-officedocument.notes+xml</code> | ||||
* @param rel the package relationship holding this notes, | |||||
* the relationship type must be http://schemas.openxmlformats.org/officeDocument/2006/relationships/notes | |||||
* | |||||
* | |||||
* @since POI 3.14-Beta1 | * @since POI 3.14-Beta1 | ||||
*/ | */ | ||||
XSLFNotes(PackagePart part) throws IOException, XmlException { | XSLFNotes(PackagePart part) throws IOException, XmlException { | ||||
@Override | @Override | ||||
protected String getRootElementName(){ | protected String getRootElementName(){ | ||||
return "notes"; | |||||
return "notes"; | |||||
} | } | ||||
@Override | @Override |
@Beta | @Beta | ||||
public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { | public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { | ||||
static final String PML_NS = "http://schemas.openxmlformats.org/presentationml/2006/main"; | static final String PML_NS = "http://schemas.openxmlformats.org/presentationml/2006/main"; | ||||
private final XmlObject _shape; | private final XmlObject _shape; | ||||
private final XSLFSheet _sheet; | private final XSLFSheet _sheet; | ||||
private XSLFShapeContainer _parent; | private XSLFShapeContainer _parent; | ||||
_shape = shape; | _shape = shape; | ||||
_sheet = sheet; | _sheet = sheet; | ||||
} | } | ||||
/** | /** | ||||
* @return the xml bean holding this shape's data | * @return the xml bean holding this shape's data | ||||
*/ | */ | ||||
// the (not existing) xmlbeans hierarchy and subclasses shouldn't narrow it's return value | // the (not existing) xmlbeans hierarchy and subclasses shouldn't narrow it's return value | ||||
return _shape; | return _shape; | ||||
} | } | ||||
@Override | |||||
public XSLFSheet getSheet() { | public XSLFSheet getSheet() { | ||||
return _sheet; | return _sheet; | ||||
} | } | ||||
@Override | @Override | ||||
public String getShapeName(){ | public String getShapeName(){ | ||||
return getCNvPr().getName(); | return getCNvPr().getName(); | ||||
PlaceableShape<?,?> ps = (PlaceableShape<?,?>)this; | PlaceableShape<?,?> ps = (PlaceableShape<?,?>)this; | ||||
ps.setAnchor(sh.getAnchor()); | ps.setAnchor(sh.getAnchor()); | ||||
} | } | ||||
} | } | ||||
public void setParent(XSLFShapeContainer parent) { | public void setParent(XSLFShapeContainer parent) { | ||||
this._parent = parent; | this._parent = parent; | ||||
} | } | ||||
@Override | |||||
public XSLFShapeContainer getParent() { | public XSLFShapeContainer getParent() { | ||||
return this._parent; | return this._parent; | ||||
} | } | ||||
protected PaintStyle getFillPaint() { | protected PaintStyle getFillPaint() { | ||||
final XSLFTheme theme = getSheet().getTheme(); | final XSLFTheme theme = getSheet().getTheme(); | ||||
final boolean hasPlaceholder = getPlaceholder() != null; | final boolean hasPlaceholder = getPlaceholder() != null; | ||||
PropertyFetcher<PaintStyle> fetcher = new PropertyFetcher<PaintStyle>() { | PropertyFetcher<PaintStyle> fetcher = new PropertyFetcher<PaintStyle>() { | ||||
@Override | |||||
public boolean fetch(XSLFShape shape) { | public boolean fetch(XSLFShape shape) { | ||||
XSLFFillProperties fp = XSLFPropertiesDelegate.getFillDelegate(shape.getShapeProperties()); | XSLFFillProperties fp = XSLFPropertiesDelegate.getFillDelegate(shape.getShapeProperties()); | ||||
if (fp == null) { | if (fp == null) { | ||||
setValue(null); | setValue(null); | ||||
return true; | return true; | ||||
} | } | ||||
PackagePart pp = shape.getSheet().getPackagePart(); | PackagePart pp = shape.getSheet().getPackagePart(); | ||||
PaintStyle paint = selectPaint(fp, null, pp, theme, hasPlaceholder); | PaintStyle paint = selectPaint(fp, null, pp, theme, hasPlaceholder); | ||||
if (paint != null) { | if (paint != null) { | ||||
setValue(paint); | setValue(paint); | ||||
return true; | return true; | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
}; | }; | ||||
protected CTBackgroundProperties getBgPr() { | protected CTBackgroundProperties getBgPr() { | ||||
return getChild(CTBackgroundProperties.class, PML_NS, "bgPr"); | return getChild(CTBackgroundProperties.class, PML_NS, "bgPr"); | ||||
} | } | ||||
@SuppressWarnings("unused") | @SuppressWarnings("unused") | ||||
protected CTStyleMatrixReference getBgRef() { | protected CTStyleMatrixReference getBgRef() { | ||||
return getChild(CTStyleMatrixReference.class, PML_NS, "bgRef"); | return getChild(CTStyleMatrixReference.class, PML_NS, "bgRef"); | ||||
} | } | ||||
protected CTGroupShapeProperties getGrpSpPr() { | protected CTGroupShapeProperties getGrpSpPr() { | ||||
return getChild(CTGroupShapeProperties.class, PML_NS, "grpSpPr"); | return getChild(CTGroupShapeProperties.class, PML_NS, "grpSpPr"); | ||||
} | } | ||||
protected CTNonVisualDrawingProps getCNvPr() { | protected CTNonVisualDrawingProps getCNvPr() { | ||||
if (_nvPr == null) { | if (_nvPr == null) { | ||||
String xquery = "declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' .//*/p:cNvPr"; | String xquery = "declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' .//*/p:cNvPr"; | ||||
public Placeholder getPlaceholder() { | public Placeholder getPlaceholder() { | ||||
return getPlaceholderDetails().getPlaceholder(); | return getPlaceholderDetails().getPlaceholder(); | ||||
} | } | ||||
/** | /** | ||||
* @see PlaceholderDetails#setPlaceholder(Placeholder) | * @see PlaceholderDetails#setPlaceholder(Placeholder) | ||||
*/ | */ | ||||
@SuppressWarnings({"unchecked", "WeakerAccess"}) | @SuppressWarnings({"unchecked", "WeakerAccess"}) | ||||
protected <T extends XmlObject> T selectProperty(Class<T> resultClass, String xquery) { | protected <T extends XmlObject> T selectProperty(Class<T> resultClass, String xquery) { | ||||
XmlObject[] rs = getXmlObject().selectPath(xquery); | XmlObject[] rs = getXmlObject().selectPath(xquery); | ||||
if (rs.length == 0) return null; | |||||
if (rs.length == 0) { | |||||
return null; | |||||
} | |||||
return (resultClass.isInstance(rs[0])) ? (T)rs[0] : null; | return (resultClass.isInstance(rs[0])) ? (T)rs[0] : null; | ||||
} | } | ||||
* <li>slideLayout | * <li>slideLayout | ||||
* <li>slideMaster | * <li>slideMaster | ||||
* </ol> | * </ol> | ||||
* | |||||
* | |||||
* Currently themes and their defaults aren't correctly handled | * Currently themes and their defaults aren't correctly handled | ||||
* | * | ||||
* @param visitor the object that collects the desired property | * @param visitor the object that collects the desired property | ||||
return false; | return false; | ||||
} | } | ||||
MasterSheet<XSLFShape,XSLFTextParagraph> sm = getSheet().getMasterSheet(); | MasterSheet<XSLFShape,XSLFTextParagraph> sm = getSheet().getMasterSheet(); | ||||
// try slide layout | // try slide layout | ||||
if (sm instanceof XSLFSlideLayout) { | if (sm instanceof XSLFSlideLayout) { | ||||
XSLFSlideLayout slideLayout = (XSLFSlideLayout)sm; | XSLFSlideLayout slideLayout = (XSLFSlideLayout)sm; | ||||
} | } | ||||
sm = slideLayout.getMasterSheet(); | sm = slideLayout.getMasterSheet(); | ||||
} | } | ||||
// try slide master | // try slide master | ||||
if (sm instanceof XSLFSlideMaster) { | if (sm instanceof XSLFSlideMaster) { | ||||
XSLFSlideMaster master = (XSLFSlideMaster)sm; | XSLFSlideMaster master = (XSLFSlideMaster)sm; | ||||
XSLFSimpleShape masterShape = master.getPlaceholderByType(textType); | XSLFSimpleShape masterShape = master.getPlaceholderByType(textType); | ||||
return masterShape != null && visitor.fetch(masterShape); | return masterShape != null && visitor.fetch(masterShape); | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
private static int getPlaceholderType(CTPlaceholder ph) { | private static int getPlaceholderType(CTPlaceholder ph) { | ||||
if ( !ph.isSetType()) { | if ( !ph.isSetType()) { | ||||
return STPlaceholderType.INT_BODY; | return STPlaceholderType.INT_BODY; | ||||
} | } | ||||
switch (ph.getType().intValue()) { | switch (ph.getType().intValue()) { | ||||
case STPlaceholderType.INT_TITLE: | case STPlaceholderType.INT_TITLE: | ||||
case STPlaceholderType.INT_CTR_TITLE: | case STPlaceholderType.INT_CTR_TITLE: | ||||
throw new RuntimeException(e); | throw new RuntimeException(e); | ||||
} | } | ||||
} | } | ||||
@Override | |||||
public InputStream getImageData() { | public InputStream getImageData() { | ||||
try { | try { | ||||
return getPart().getInputStream(); | return getPart().getInputStream(); | ||||
} | } | ||||
} | } | ||||
@Override | |||||
public String getContentType() { | public String getContentType() { | ||||
/* TOOD: map content-type */ | /* TOOD: map content-type */ | ||||
return getPart().getContentType(); | return getPart().getContentType(); | ||||
} | } | ||||
@Override | |||||
public int getAlpha() { | public int getAlpha() { | ||||
return (blip.sizeOfAlphaModFixArray() > 0) | return (blip.sizeOfAlphaModFixArray() > 0) | ||||
? blip.getAlphaModFixArray(0).getAmt() | ? blip.getAlphaModFixArray(0).getAmt() | ||||
: 100000; | : 100000; | ||||
} | } | ||||
}; | |||||
}; | |||||
} | } | ||||
@SuppressWarnings("WeakerAccess") | @SuppressWarnings("WeakerAccess") | ||||
final CTGradientStop[] gs = gradFill.getGsLst().getGsArray(); | final CTGradientStop[] gs = gradFill.getGsLst().getGsArray(); | ||||
Arrays.sort(gs, (o1, o2) -> { | Arrays.sort(gs, (o1, o2) -> { | ||||
Integer pos1 = o1.getPos(); | |||||
Integer pos2 = o2.getPos(); | |||||
return pos1.compareTo(pos2); | |||||
int pos1 = o1.getPos(); | |||||
int pos2 = o2.getPos(); | |||||
return Integer.compare(pos1, pos2); | |||||
}); | }); | ||||
final ColorStyle cs[] = new ColorStyle[gs.length]; | final ColorStyle cs[] = new ColorStyle[gs.length]; | ||||
final float fractions[] = new float[gs.length]; | final float fractions[] = new float[gs.length]; | ||||
int i=0; | int i=0; | ||||
for (CTGradientStop cgs : gs) { | for (CTGradientStop cgs : gs) { | ||||
CTSchemeColor phClrCgs = phClr; | CTSchemeColor phClrCgs = phClr; | ||||
fractions[i] = cgs.getPos() / 100000.f; | fractions[i] = cgs.getPos() / 100000.f; | ||||
i++; | i++; | ||||
} | } | ||||
return new GradientPaint() { | return new GradientPaint() { | ||||
@Override | |||||
public double getGradientAngle() { | public double getGradientAngle() { | ||||
return (gradFill.isSetLin()) | return (gradFill.isSetLin()) | ||||
? gradFill.getLin().getAng() / 60000.d | ? gradFill.getLin().getAng() / 60000.d | ||||
: 0; | : 0; | ||||
} | } | ||||
@Override | |||||
public ColorStyle[] getGradientColors() { | public ColorStyle[] getGradientColors() { | ||||
return cs; | return cs; | ||||
} | } | ||||
@Override | |||||
public float[] getGradientFractions() { | public float[] getGradientFractions() { | ||||
return fractions; | return fractions; | ||||
} | } | ||||
@Override | |||||
public boolean isRotatedWithShape() { | public boolean isRotatedWithShape() { | ||||
return gradFill.getRotWithShape(); | return gradFill.getRotWithShape(); | ||||
} | } | ||||
@Override | |||||
public GradientType getGradientType() { | public GradientType getGradientType() { | ||||
if (gradFill.isSetLin()) { | if (gradFill.isSetLin()) { | ||||
return GradientType.linear; | return GradientType.linear; | ||||
} | } | ||||
if (gradFill.isSetPath()) { | if (gradFill.isSetPath()) { | ||||
/* TODO: handle rect path */ | /* TODO: handle rect path */ | ||||
STPathShadeType.Enum ps = gradFill.getPath().getPath(); | STPathShadeType.Enum ps = gradFill.getPath().getPath(); | ||||
return GradientType.shape; | return GradientType.shape; | ||||
} | } | ||||
} | } | ||||
return GradientType.linear; | return GradientType.linear; | ||||
} | } | ||||
}; | |||||
}; | |||||
} | } | ||||
@SuppressWarnings("WeakerAccess") | @SuppressWarnings("WeakerAccess") | ||||
protected static PaintStyle selectPaint(CTStyleMatrixReference fillRef, final XSLFTheme theme, boolean isLineStyle, boolean hasPlaceholder) { | protected static PaintStyle selectPaint(CTStyleMatrixReference fillRef, final XSLFTheme theme, boolean isLineStyle, boolean hasPlaceholder) { | ||||
if (fillRef == null) return null; | |||||
if (fillRef == null) { | |||||
return null; | |||||
} | |||||
// The idx attribute refers to the index of a fill style or | // The idx attribute refers to the index of a fill style or | ||||
// background fill style within the presentation's style matrix, defined by the fmtScheme element. | // background fill style within the presentation's style matrix, defined by the fmtScheme element. | ||||
// value of 0 or 1000 indicates no background, | // value of 0 or 1000 indicates no background, | ||||
fp = XSLFPropertiesDelegate.getFillDelegate(cur.getObject()); | fp = XSLFPropertiesDelegate.getFillDelegate(cur.getObject()); | ||||
} | } | ||||
cur.dispose(); | cur.dispose(); | ||||
CTSchemeColor phClr = fillRef.getSchemeClr(); | CTSchemeColor phClr = fillRef.getSchemeClr(); | ||||
PaintStyle res = selectPaint(fp, phClr, theme.getPackagePart(), theme, hasPlaceholder); | PaintStyle res = selectPaint(fp, phClr, theme.getPackagePart(), theme, hasPlaceholder); | ||||
// check for empty placeholder value | // check for empty placeholder value | ||||
XSLFColor col = new XSLFColor(fillRef, theme, phClr); | XSLFColor col = new XSLFColor(fillRef, theme, phClr); | ||||
return DrawPaint.createSolidPaint(col.getColorStyle()); | return DrawPaint.createSolidPaint(col.getColorStyle()); | ||||
} | } | ||||
@Override | @Override | ||||
public void draw(Graphics2D graphics, Rectangle2D bounds) { | public void draw(Graphics2D graphics, Rectangle2D bounds) { | ||||
DrawFactory.getInstance(graphics).drawShape(graphics, this, bounds); | DrawFactory.getInstance(graphics).drawShape(graphics, this, bounds); | ||||
} | } | ||||
/** | /** | ||||
* Return the shape specific (visual) properties | * Return the shape specific (visual) properties | ||||
* | * |
for (int i = 0; i < args.length; i++) { | for (int i = 0; i < args.length; i++) { | ||||
if (args[i].startsWith("-")) { | if (args[i].startsWith("-")) { | ||||
if ("-scale".equals(args[i])) { | if ("-scale".equals(args[i])) { | ||||
scale = Float.parseFloat(args[++i]); | |||||
scale = Float.parseFloat(args[++i]); // lgtm[java/index-out-of-bounds] | |||||
} else if ("-slide".equals(args[i])) { | } else if ("-slide".equals(args[i])) { | ||||
slidenumStr = args[++i]; | |||||
slidenumStr = args[++i]; // lgtm[java/index-out-of-bounds] | |||||
} else if ("-format".equals(args[i])) { | } else if ("-format".equals(args[i])) { | ||||
format = args[++i]; | |||||
format = args[++i]; // lgtm[java/index-out-of-bounds] | |||||
} else if ("-outdir".equals(args[i])) { | } else if ("-outdir".equals(args[i])) { | ||||
outdir = new File(args[++i]); | |||||
outdir = new File(args[++i]); // lgtm[java/index-out-of-bounds] | |||||
} else if ("-quiet".equals(args[i])) { | } else if ("-quiet".equals(args[i])) { | ||||
quiet = true; | quiet = true; | ||||
} | } | ||||
usage("Invalid format given"); | usage("Invalid format given"); | ||||
return; | return; | ||||
} | } | ||||
if (outdir == null) { | if (outdir == null) { | ||||
outdir = file.getParentFile(); | outdir = file.getParentFile(); | ||||
} | } | ||||
if (!"null".equals(format) && (outdir == null || !outdir.exists() || !outdir.isDirectory())) { | if (!"null".equals(format) && (outdir == null || !outdir.exists() || !outdir.isDirectory())) { | ||||
usage("Output directory doesn't exist"); | usage("Output directory doesn't exist"); | ||||
return; | return; | ||||
usage("Invalid scale given"); | usage("Invalid scale given"); | ||||
return; | return; | ||||
} | } | ||||
if (!quiet) { | if (!quiet) { | ||||
System.out.println("Processing " + file); | System.out.println("Processing " + file); | ||||
} | } | ||||
System.out.println("Done"); | System.out.println("Done"); | ||||
} | } | ||||
} | } | ||||
private static Set<Integer> slideIndexes(final int slideCount, String range) { | private static Set<Integer> slideIndexes(final int slideCount, String range) { | ||||
Set<Integer> slideIdx = new TreeSet<>(); | Set<Integer> slideIdx = new TreeSet<>(); | ||||
if ("-1".equals(range)) { | if ("-1".equals(range)) { |
int numBytes = 2*(int)numChars; | int numBytes = 2*(int)numChars; | ||||
offset += 4; | offset += 4; | ||||
if (offset+numBytes > data.length) { | if (offset+numBytes > data.length) { | ||||
throw new XSSFBParseException("trying to read beyond data length:" + | |||||
throw new XSSFBParseException("trying to read beyond data length: " + | |||||
"offset="+offset+", numBytes="+numBytes+", data.length="+data.length); | "offset="+offset+", numBytes="+numBytes+", data.length="+data.length); | ||||
} | } | ||||
sb.append(new String(data, offset, numBytes, StandardCharsets.UTF_16LE)); | sb.append(new String(data, offset, numBytes, StandardCharsets.UTF_16LE)); |
public class SXSSFRow implements Row, Comparable<SXSSFRow> | public class SXSSFRow implements Row, Comparable<SXSSFRow> | ||||
{ | { | ||||
private static final Boolean UNDEFINED = null; | private static final Boolean UNDEFINED = null; | ||||
private final SXSSFSheet _sheet; // parent sheet | private final SXSSFSheet _sheet; // parent sheet | ||||
private final SortedMap<Integer, SXSSFCell> _cells = new TreeMap<>(); | private final SortedMap<Integer, SXSSFCell> _cells = new TreeMap<>(); | ||||
private short _style = -1; // index of cell style in style table | private short _style = -1; // index of cell style in style table | ||||
private short _height = -1; // row height in twips (1/20 point) | private short _height = -1; // row height in twips (1/20 point) | ||||
private boolean _zHeight; // row zero-height (this is somehow different than being hidden) | private boolean _zHeight; // row zero-height (this is somehow different than being hidden) | ||||
private int _outlineLevel; // Outlining level of the row, when outlining is on | private int _outlineLevel; // Outlining level of the row, when outlining is on | ||||
// use Boolean to have a tri-state for on/off/undefined | |||||
// use Boolean to have a tri-state for on/off/undefined | |||||
private Boolean _hidden = UNDEFINED; | private Boolean _hidden = UNDEFINED; | ||||
private Boolean _collapsed = UNDEFINED; | private Boolean _collapsed = UNDEFINED; | ||||
public SXSSFRow(SXSSFSheet sheet) | public SXSSFRow(SXSSFSheet sheet) | ||||
{ | { | ||||
_sheet=sheet; | _sheet=sheet; | ||||
} | } | ||||
public Iterator<Cell> allCellsIterator() | public Iterator<Cell> allCellsIterator() | ||||
{ | { | ||||
return new CellIterator(); | return new CellIterator(); | ||||
void setOutlineLevel(int level){ | void setOutlineLevel(int level){ | ||||
_outlineLevel = level; | _outlineLevel = level; | ||||
} | } | ||||
/** | /** | ||||
* get row hidden state: Hidden (true), Unhidden (false), Undefined (null) | * get row hidden state: Hidden (true), Unhidden (false), Undefined (null) | ||||
* | * | ||||
public SXSSFCell getCell(int cellnum, MissingCellPolicy policy) | public SXSSFCell getCell(int cellnum, MissingCellPolicy policy) | ||||
{ | { | ||||
checkBounds(cellnum); | checkBounds(cellnum); | ||||
final SXSSFCell cell = _cells.get(cellnum); | final SXSSFCell cell = _cells.get(cellnum); | ||||
switch (policy) { | switch (policy) { | ||||
case RETURN_NULL_AND_BLANK: | case RETURN_NULL_AND_BLANK: | ||||
@Override | @Override | ||||
public void setHeightInPoints(float height) | public void setHeightInPoints(float height) | ||||
{ | { | ||||
if(height==-1) | |||||
if(height==-1) { | |||||
_height=-1; | _height=-1; | ||||
else | |||||
} else { | |||||
_height=(short)(height*20); | _height=(short)(height*20); | ||||
} | |||||
} | } | ||||
/** | /** | ||||
{ | { | ||||
return (float)(_height==-1?getSheet().getDefaultRowHeightInPoints():_height/20.0); | return (float)(_height==-1?getSheet().getDefaultRowHeightInPoints():_height/20.0); | ||||
} | } | ||||
/** | /** | ||||
* Is this row formatted? Most aren't, but some rows | * Is this row formatted? Most aren't, but some rows | ||||
* do have whole-row styles. For those that do, you | * do have whole-row styles. For those that do, you | ||||
*/ | */ | ||||
@Override | @Override | ||||
public CellStyle getRowStyle() { | public CellStyle getRowStyle() { | ||||
if(!isFormatted()) return null; | |||||
if(!isFormatted()) { | |||||
return null; | |||||
} | |||||
return getSheet().getWorkbook().getCellStyleAt(_style); | return getSheet().getWorkbook().getCellStyleAt(_style); | ||||
} | } | ||||
@Internal | @Internal | ||||
/*package*/ int getRowStyleIndex() { | /*package*/ int getRowStyleIndex() { | ||||
return _style; | return _style; | ||||
} | } | ||||
/** | /** | ||||
* Applies a whole-row cell styling to the row. | * Applies a whole-row cell styling to the row. | ||||
* The row style can be cleared by passing in <code>null</code>. | * The row style can be cleared by passing in <code>null</code>. | ||||
/** | /** | ||||
* Create an iterator over the cells from [0, getLastCellNum()). | * Create an iterator over the cells from [0, getLastCellNum()). | ||||
* Includes blank cells, excludes empty cells | * Includes blank cells, excludes empty cells | ||||
* | |||||
* | |||||
* Returns an iterator over all filled cells (created via Row.createCell()) | * Returns an iterator over all filled cells (created via Row.createCell()) | ||||
* Throws ConcurrentModificationException if cells are added, moved, or | * Throws ConcurrentModificationException if cells are added, moved, or | ||||
* removed after the iterator is created. | * removed after the iterator is created. | ||||
@Override | @Override | ||||
public Cell next() throws NoSuchElementException | public Cell next() throws NoSuchElementException | ||||
{ | { | ||||
if (hasNext()) | |||||
if (hasNext()) { | |||||
return _cells.get(pos++); | return _cells.get(pos++); | ||||
else | |||||
} else { | |||||
throw new NoSuchElementException(); | throw new NoSuchElementException(); | ||||
} | |||||
} | } | ||||
@Override | @Override | ||||
public void remove() | public void remove() | ||||
throw new UnsupportedOperationException(); | throw new UnsupportedOperationException(); | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Compares two <code>SXSSFRow</code> objects. Two rows are equal if they belong to the same worksheet and | * Compares two <code>SXSSFRow</code> objects. Two rows are equal if they belong to the same worksheet and | ||||
* their row indexes are equal. | * their row indexes are equal. | ||||
throw new IllegalArgumentException("The compared rows must belong to the same sheet"); | throw new IllegalArgumentException("The compared rows must belong to the same sheet"); | ||||
} | } | ||||
Integer thisRow = this.getRowNum(); | |||||
Integer otherRow = other.getRowNum(); | |||||
return thisRow.compareTo(otherRow); | |||||
int thisRow = this.getRowNum(); | |||||
int otherRow = other.getRowNum(); | |||||
return Integer.compare(thisRow, otherRow); | |||||
} | } | ||||
@Override | @Override |
XSSFSheet sheet = getSheet(); | XSSFSheet sheet = getSheet(); | ||||
XSSFWorkbook wb = sheet.getWorkbook(); | XSSFWorkbook wb = sheet.getWorkbook(); | ||||
int sheetIndex = wb.getSheetIndex(sheet); | int sheetIndex = wb.getSheetIndex(sheet); | ||||
long shapeId = (sheetIndex + 1) * 1024 + newShapeId(); | |||||
long shapeId = (sheetIndex + 1L) * 1024 + newShapeId(); | |||||
// add reference to OLE part | // add reference to OLE part | ||||
PackagePartName olePN; | PackagePartName olePN; |
import java.util.Set; | import java.util.Set; | ||||
import java.util.TreeMap; | import java.util.TreeMap; | ||||
import org.apache.poi.ss.formula.FormulaShifter; | |||||
import org.apache.poi.ss.SpreadsheetVersion; | import org.apache.poi.ss.SpreadsheetVersion; | ||||
import org.apache.poi.ss.formula.FormulaShifter; | |||||
import org.apache.poi.ss.usermodel.Cell; | import org.apache.poi.ss.usermodel.Cell; | ||||
import org.apache.poi.ss.usermodel.CellCopyPolicy; | import org.apache.poi.ss.usermodel.CellCopyPolicy; | ||||
import org.apache.poi.ss.usermodel.CellStyle; | import org.apache.poi.ss.usermodel.CellStyle; | ||||
_cells.put(colI, cell); | _cells.put(colI, cell); | ||||
sheet.onReadCell(cell); | sheet.onReadCell(cell); | ||||
} | } | ||||
if (! row.isSetR()) { | if (! row.isSetR()) { | ||||
// Certain file format writers skip the row number | // Certain file format writers skip the row number | ||||
// Assume no gaps, and give this the next row number | // Assume no gaps, and give this the next row number | ||||
throw new IllegalArgumentException("The compared rows must belong to the same sheet"); | throw new IllegalArgumentException("The compared rows must belong to the same sheet"); | ||||
} | } | ||||
Integer thisRow = this.getRowNum(); | |||||
Integer otherRow = other.getRowNum(); | |||||
return thisRow.compareTo(otherRow); | |||||
int thisRow = this.getRowNum(); | |||||
int otherRow = other.getRowNum(); | |||||
return Integer.compare(thisRow, otherRow); | |||||
} | } | ||||
@Override | @Override | ||||
*/ | */ | ||||
@Override | @Override | ||||
public XSSFCell getCell(int cellnum, MissingCellPolicy policy) { | public XSSFCell getCell(int cellnum, MissingCellPolicy policy) { | ||||
if(cellnum < 0) throw new IllegalArgumentException("Cell index must be >= 0"); | |||||
if(cellnum < 0) { | |||||
throw new IllegalArgumentException("Cell index must be >= 0"); | |||||
} | |||||
// Performance optimization for bug 57840: explicit boxing is slightly faster than auto-unboxing, though may use more memory | // Performance optimization for bug 57840: explicit boxing is slightly faster than auto-unboxing, though may use more memory | ||||
final Integer colI = Integer.valueOf(cellnum); // NOSONAR | final Integer colI = Integer.valueOf(cellnum); // NOSONAR | ||||
@Override | @Override | ||||
public void setHeight(short height) { | public void setHeight(short height) { | ||||
if (height == -1) { | if (height == -1) { | ||||
if (_row.isSetHt()) _row.unsetHt(); | |||||
if (_row.isSetCustomHeight()) _row.unsetCustomHeight(); | |||||
if (_row.isSetHt()) { | |||||
_row.unsetHt(); | |||||
} | |||||
if (_row.isSetCustomHeight()) { | |||||
_row.unsetCustomHeight(); | |||||
} | |||||
} else { | } else { | ||||
_row.setHt((double) height / 20); | _row.setHt((double) height / 20); | ||||
_row.setCustomHeight(true); | _row.setCustomHeight(true); | ||||
*/ | */ | ||||
@Override | @Override | ||||
public XSSFCellStyle getRowStyle() { | public XSSFCellStyle getRowStyle() { | ||||
if(!isFormatted()) return null; | |||||
if(!isFormatted()) { | |||||
return null; | |||||
} | |||||
StylesTable stylesSource = getSheet().getWorkbook().getStylesSource(); | StylesTable stylesSource = getSheet().getWorkbook().getStylesSource(); | ||||
if(stylesSource.getNumCellStyles() > 0) { | if(stylesSource.getNumCellStyles() > 0) { | ||||
return stylesSource.getStyleAt((int)_row.getS()); | return stylesSource.getStyleAt((int)_row.getS()); | ||||
return null; | return null; | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Applies a whole-row cell styling to the row. | * Applies a whole-row cell styling to the row. | ||||
* If the value is null then the style information is removed, | * If the value is null then the style information is removed, | ||||
} | } | ||||
} else { | } else { | ||||
StylesTable styleSource = getSheet().getWorkbook().getStylesSource(); | StylesTable styleSource = getSheet().getWorkbook().getStylesSource(); | ||||
XSSFCellStyle xStyle = (XSSFCellStyle)style; | XSSFCellStyle xStyle = (XSSFCellStyle)style; | ||||
xStyle.verifyBelongsToStylesSource(styleSource); | xStyle.verifyBelongsToStylesSource(styleSource); | ||||
_row.setCustomFormat(true); | _row.setCustomFormat(true); | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Remove the Cell from this row. | * Remove the Cell from this row. | ||||
* | * | ||||
int i = 0; | int i = 0; | ||||
for (XSSFCell xssfCell : _cells.values()) { | for (XSSFCell xssfCell : _cells.values()) { | ||||
cArray[i] = (CTCell) xssfCell.getCTCell().copy(); | cArray[i] = (CTCell) xssfCell.getCTCell().copy(); | ||||
// we have to copy and re-create the XSSFCell here because the | |||||
// we have to copy and re-create the XSSFCell here because the | |||||
// elements as otherwise setCArray below invalidates all the columns! | // elements as otherwise setCArray below invalidates all the columns! | ||||
// see Bug 56170, XMLBeans seems to always release previous objects | // see Bug 56170, XMLBeans seems to always release previous objects | ||||
// in the CArray, so we need to provide completely new ones here! | // in the CArray, so we need to provide completely new ones here! | ||||
} | } | ||||
setRowNum(rownum); | setRowNum(rownum); | ||||
} | } | ||||
/** | /** | ||||
* Copy the cells from srcRow to this row | * Copy the cells from srcRow to this row | ||||
* If this row is not a blank row, this will merge the two rows, overwriting | * If this row is not a blank row, this will merge the two rows, overwriting | ||||
final int srcRowNum = srcRow.getRowNum(); | final int srcRowNum = srcRow.getRowNum(); | ||||
final int destRowNum = getRowNum(); | final int destRowNum = getRowNum(); | ||||
final int rowDifference = destRowNum - srcRowNum; | final int rowDifference = destRowNum - srcRowNum; | ||||
final FormulaShifter formulaShifter = FormulaShifter.createForRowCopy(sheetIndex, sheetName, srcRowNum, srcRowNum, rowDifference, SpreadsheetVersion.EXCEL2007); | final FormulaShifter formulaShifter = FormulaShifter.createForRowCopy(sheetIndex, sheetName, srcRowNum, srcRowNum, rowDifference, SpreadsheetVersion.EXCEL2007); | ||||
final XSSFRowShifter rowShifter = new XSSFRowShifter(_sheet); | final XSSFRowShifter rowShifter = new XSSFRowShifter(_sheet); | ||||
rowShifter.updateRowFormulas(this, formulaShifter); | rowShifter.updateRowFormulas(this, formulaShifter); | ||||
public int getOutlineLevel() { | public int getOutlineLevel() { | ||||
return _row.getOutlineLevel(); | return _row.getOutlineLevel(); | ||||
} | } | ||||
/** | /** | ||||
* Shifts column range [firstShiftColumnIndex-lastShiftColumnIndex] step places to the right. | * Shifts column range [firstShiftColumnIndex-lastShiftColumnIndex] step places to the right. | ||||
* @param firstShiftColumnIndex the column to start shifting | * @param firstShiftColumnIndex the column to start shifting | ||||
*/ | */ | ||||
@Override | @Override | ||||
public void shiftCellsRight(int firstShiftColumnIndex, int lastShiftColumnIndex, int step) { | public void shiftCellsRight(int firstShiftColumnIndex, int lastShiftColumnIndex, int step) { | ||||
if(step < 0) | |||||
if(step < 0) { | |||||
throw new IllegalArgumentException("Shifting step may not be negative "); | throw new IllegalArgumentException("Shifting step may not be negative "); | ||||
if(firstShiftColumnIndex > lastShiftColumnIndex) | |||||
} | |||||
if(firstShiftColumnIndex > lastShiftColumnIndex) { | |||||
throw new IllegalArgumentException(String.format(LocaleUtil.getUserLocale(), | throw new IllegalArgumentException(String.format(LocaleUtil.getUserLocale(), | ||||
"Incorrect shifting range : %d-%d", firstShiftColumnIndex, lastShiftColumnIndex)); | "Incorrect shifting range : %d-%d", firstShiftColumnIndex, lastShiftColumnIndex)); | ||||
for (int columnIndex = lastShiftColumnIndex; columnIndex >= firstShiftColumnIndex; columnIndex--){ // process cells backwards, because of shifting | |||||
} | |||||
for (int columnIndex = lastShiftColumnIndex; columnIndex >= firstShiftColumnIndex; columnIndex--){ // process cells backwards, because of shifting | |||||
shiftCell(columnIndex, step); | shiftCell(columnIndex, step); | ||||
} | } | ||||
for (int columnIndex = firstShiftColumnIndex; columnIndex <= firstShiftColumnIndex+step-1; columnIndex++) | for (int columnIndex = firstShiftColumnIndex; columnIndex <= firstShiftColumnIndex+step-1; columnIndex++) | ||||
{ | { | ||||
_cells.remove(columnIndex); | _cells.remove(columnIndex); | ||||
XSSFCell targetCell = getCell(columnIndex); | XSSFCell targetCell = getCell(columnIndex); | ||||
if(targetCell != null) | |||||
if(targetCell != null) { | |||||
targetCell.getCTCell().set(CTCell.Factory.newInstance()); | targetCell.getCTCell().set(CTCell.Factory.newInstance()); | ||||
} | |||||
} | } | ||||
} | } | ||||
/** | /** | ||||
*/ | */ | ||||
@Override | @Override | ||||
public void shiftCellsLeft(int firstShiftColumnIndex, int lastShiftColumnIndex, int step) { | public void shiftCellsLeft(int firstShiftColumnIndex, int lastShiftColumnIndex, int step) { | ||||
if(step < 0) | |||||
if(step < 0) { | |||||
throw new IllegalArgumentException("Shifting step may not be negative "); | throw new IllegalArgumentException("Shifting step may not be negative "); | ||||
if(firstShiftColumnIndex > lastShiftColumnIndex) | |||||
} | |||||
if(firstShiftColumnIndex > lastShiftColumnIndex) { | |||||
throw new IllegalArgumentException(String.format(LocaleUtil.getUserLocale(), | throw new IllegalArgumentException(String.format(LocaleUtil.getUserLocale(), | ||||
"Incorrect shifting range : %d-%d", firstShiftColumnIndex, lastShiftColumnIndex)); | "Incorrect shifting range : %d-%d", firstShiftColumnIndex, lastShiftColumnIndex)); | ||||
if(firstShiftColumnIndex - step < 0) | |||||
} | |||||
if(firstShiftColumnIndex - step < 0) { | |||||
throw new IllegalStateException("Column index less than zero : " + (Integer.valueOf(firstShiftColumnIndex + step)).toString()); | throw new IllegalStateException("Column index less than zero : " + (Integer.valueOf(firstShiftColumnIndex + step)).toString()); | ||||
for (int columnIndex = firstShiftColumnIndex; columnIndex <= lastShiftColumnIndex; columnIndex++){ | |||||
} | |||||
for (int columnIndex = firstShiftColumnIndex; columnIndex <= lastShiftColumnIndex; columnIndex++){ | |||||
shiftCell(columnIndex, -step); | shiftCell(columnIndex, -step); | ||||
} | } | ||||
for (int columnIndex = lastShiftColumnIndex-step+1; columnIndex <= lastShiftColumnIndex; columnIndex++){ | for (int columnIndex = lastShiftColumnIndex-step+1; columnIndex <= lastShiftColumnIndex; columnIndex++){ | ||||
_cells.remove(columnIndex); | _cells.remove(columnIndex); | ||||
XSSFCell targetCell = getCell(columnIndex); | XSSFCell targetCell = getCell(columnIndex); | ||||
if(targetCell != null) | |||||
if(targetCell != null) { | |||||
targetCell.getCTCell().set(CTCell.Factory.newInstance()); | targetCell.getCTCell().set(CTCell.Factory.newInstance()); | ||||
} | |||||
} | } | ||||
} | } | ||||
private void shiftCell(int columnIndex, int step/*pass negative value for left shift*/){ | private void shiftCell(int columnIndex, int step/*pass negative value for left shift*/){ | ||||
if(columnIndex + step < 0) // only for shifting left | |||||
if(columnIndex + step < 0) { | |||||
throw new IllegalStateException("Column index less than zero : " + (Integer.valueOf(columnIndex + step)).toString()); | throw new IllegalStateException("Column index less than zero : " + (Integer.valueOf(columnIndex + step)).toString()); | ||||
} | |||||
XSSFCell currentCell = getCell(columnIndex); | XSSFCell currentCell = getCell(columnIndex); | ||||
if(currentCell != null){ | if(currentCell != null){ | ||||
currentCell.setCellNum(columnIndex+step); | currentCell.setCellNum(columnIndex+step); | ||||
else { | else { | ||||
_cells.remove(columnIndex+step); | _cells.remove(columnIndex+step); | ||||
XSSFCell targetCell = getCell(columnIndex+step); | XSSFCell targetCell = getCell(columnIndex+step); | ||||
if(targetCell != null) | |||||
if(targetCell != null) { | |||||
targetCell.getCTCell().set(CTCell.Factory.newInstance()); | targetCell.getCTCell().set(CTCell.Factory.newInstance()); | ||||
} | |||||
} | } | ||||
} | } | ||||
} | } |
// private final IBody part; | // private final IBody part; | ||||
// private final XWPFDocument document; | // private final XWPFDocument document; | ||||
private List<XWPFParagraph> paragraphs = new ArrayList<>(); | |||||
private List<XWPFTable> tables = new ArrayList<>(); | |||||
private List<XWPFRun> runs = new ArrayList<>(); | |||||
private List<XWPFSDT> contentControls = new ArrayList<>(); | |||||
// private List<XWPFParagraph> paragraphs = new ArrayList<>(); | |||||
// private List<XWPFTable> tables = new ArrayList<>(); | |||||
// private List<XWPFRun> runs = new ArrayList<>(); | |||||
// private List<XWPFSDT> contentControls = new ArrayList<>(); | |||||
private List<ISDTContents> bodyElements = new ArrayList<>(); | private List<ISDTContents> bodyElements = new ArrayList<>(); | ||||
public XWPFSDTContent(CTSdtContentRun sdtRun, IBody part, IRunBody parent) { | public XWPFSDTContent(CTSdtContentRun sdtRun, IBody part, IRunBody parent) { | ||||
for (CTR ctr : sdtRun.getRArray()) { | for (CTR ctr : sdtRun.getRArray()) { | ||||
XWPFRun run = new XWPFRun(ctr, parent); | XWPFRun run = new XWPFRun(ctr, parent); | ||||
runs.add(run); | |||||
// runs.add(run); | |||||
bodyElements.add(run); | bodyElements.add(run); | ||||
} | } | ||||
} | } | ||||
if (o instanceof CTP) { | if (o instanceof CTP) { | ||||
XWPFParagraph p = new XWPFParagraph((CTP) o, part); | XWPFParagraph p = new XWPFParagraph((CTP) o, part); | ||||
bodyElements.add(p); | bodyElements.add(p); | ||||
paragraphs.add(p); | |||||
// paragraphs.add(p); | |||||
} else if (o instanceof CTTbl) { | } else if (o instanceof CTTbl) { | ||||
XWPFTable t = new XWPFTable((CTTbl) o, part); | XWPFTable t = new XWPFTable((CTTbl) o, part); | ||||
bodyElements.add(t); | bodyElements.add(t); | ||||
tables.add(t); | |||||
// tables.add(t); | |||||
} else if (o instanceof CTSdtBlock) { | } else if (o instanceof CTSdtBlock) { | ||||
XWPFSDT c = new XWPFSDT(((CTSdtBlock) o), part); | XWPFSDT c = new XWPFSDT(((CTSdtBlock) o), part); | ||||
bodyElements.add(c); | bodyElements.add(c); | ||||
contentControls.add(c); | |||||
// contentControls.add(c); | |||||
} else if (o instanceof CTR) { | } else if (o instanceof CTR) { | ||||
XWPFRun run = new XWPFRun((CTR) o, parent); | XWPFRun run = new XWPFRun((CTR) o, parent); | ||||
runs.add(run); | |||||
// runs.add(run); | |||||
bodyElements.add(run); | bodyElements.add(run); | ||||
} | } | ||||
} | } | ||||
cursor.dispose(); | cursor.dispose(); | ||||
} | } | ||||
@Override | |||||
public String getText() { | public String getText() { | ||||
StringBuilder text = new StringBuilder(); | StringBuilder text = new StringBuilder(); | ||||
boolean addNewLine = false; | boolean addNewLine = false; | ||||
} | } | ||||
} | } | ||||
@Override | |||||
public String toString() { | public String toString() { | ||||
return getText(); | return getText(); | ||||
} | } |
this.ctTbl = table; | this.ctTbl = table; | ||||
// is an empty table: I add one row and one column as default | // is an empty table: I add one row and one column as default | ||||
if (table.sizeOfTrArray() == 0) | |||||
if (table.sizeOfTrArray() == 0) { | |||||
createEmptyTable(table); | createEmptyTable(table); | ||||
} | |||||
for (CTRow row : table.getTrList()) { | for (CTRow row : table.getTrList()) { | ||||
StringBuilder rowText = new StringBuilder(); | StringBuilder rowText = new StringBuilder(); | ||||
return text.toString(); | return text.toString(); | ||||
} | } | ||||
/** | /** | ||||
* This method has existed since 2008 without an implementation. | * This method has existed since 2008 without an implementation. | ||||
* It will be removed unless an implementation is provided. | * It will be removed unless an implementation is provided. | ||||
/** | /** | ||||
* Get the width value as an integer. | * Get the width value as an integer. | ||||
* <p>If the width type is AUTO, DXA, or NIL, the value is 20ths of a point. If | * <p>If the width type is AUTO, DXA, or NIL, the value is 20ths of a point. If | ||||
* the width type is PCT, the value is the percentage times 50 (e.g., 2500 for 50%).</p> | |||||
* the width type is PCT, the value is the percentage times 50 (e.g., 2500 for 50%).</p> | |||||
* @return width value as an integer | * @return width value as an integer | ||||
*/ | */ | ||||
public int getWidth() { | public int getWidth() { | ||||
} | } | ||||
/** | /** | ||||
* Returns CTTblPr object for table. If force parameter is true, will | |||||
* Returns CTTblPr object for table. If force parameter is true, will | |||||
* create the element if necessary. If force parameter is false, returns | * create the element if necessary. If force parameter is false, returns | ||||
* null when CTTblPr element is missing. | * null when CTTblPr element is missing. | ||||
* | * | ||||
private CTTblBorders getTblBorders(boolean force) { | private CTTblBorders getTblBorders(boolean force) { | ||||
CTTblPr tblPr = getTblPr(force); | CTTblPr tblPr = getTblPr(force); | ||||
return tblPr == null ? null | return tblPr == null ? null | ||||
: tblPr.isSetTblBorders() ? tblPr.getTblBorders() | |||||
: tblPr.isSetTblBorders() ? tblPr.getTblBorders() | |||||
: force ? tblPr.addNewTblBorders() | : force ? tblPr.addNewTblBorders() | ||||
: null; | : null; | ||||
} | } | ||||
: tPr.isSetJc() ? TableRowAlign.valueOf(tPr.getJc().getVal().intValue()) | : tPr.isSetJc() ? TableRowAlign.valueOf(tPr.getJc().getVal().intValue()) | ||||
: null; | : null; | ||||
} | } | ||||
/** | /** | ||||
* Set table alignment to specified {@link TableRowAlign} | * Set table alignment to specified {@link TableRowAlign} | ||||
* | * | ||||
* @param ha {@link TableRowAlign} to set | |||||
* @param tra {@link TableRowAlign} to set | |||||
*/ | */ | ||||
public void setTableAlignment(TableRowAlign tra) { | public void setTableAlignment(TableRowAlign tra) { | ||||
CTTblPr tPr = getTblPr(true); | CTTblPr tPr = getTblPr(true); | ||||
CTJc jc = tPr.isSetJc() ? tPr.getJc() : tPr.addNewJc(); | CTJc jc = tPr.isSetJc() ? tPr.getJc() : tPr.addNewJc(); | ||||
jc.setVal(STJc.Enum.forInt(tra.getValue())); | jc.setVal(STJc.Enum.forInt(tra.getValue())); | ||||
} | } | ||||
/** | /** | ||||
* Removes the table alignment attribute from a table | * Removes the table alignment attribute from a table | ||||
*/ | */ | ||||
tPr.unsetJc(); | tPr.unsetJc(); | ||||
} | } | ||||
} | } | ||||
private void addColumn(XWPFTableRow tabRow, int sizeCol) { | private void addColumn(XWPFTableRow tabRow, int sizeCol) { | ||||
if (sizeCol > 0) { | if (sizeCol > 0) { | ||||
for (int i = 0; i < sizeCol; i++) { | for (int i = 0; i < sizeCol; i++) { | ||||
/** | /** | ||||
* Get inside horizontal border size | * Get inside horizontal border size | ||||
* | |||||
* | |||||
* @return The width of the Inside Horizontal borders in 1/8th points, | * @return The width of the Inside Horizontal borders in 1/8th points, | ||||
* -1 if missing. | * -1 if missing. | ||||
*/ | */ | ||||
/** | /** | ||||
* Get inside horizontal border spacing | * Get inside horizontal border spacing | ||||
* | |||||
* | |||||
* @return The offset to the Inside Horizontal borders in points, | * @return The offset to the Inside Horizontal borders in points, | ||||
* -1 if missing. | * -1 if missing. | ||||
*/ | */ | ||||
/** | /** | ||||
* Get inside horizontal border color | * Get inside horizontal border color | ||||
* | |||||
* | |||||
* @return The color of the Inside Horizontal borders, null if missing. | * @return The color of the Inside Horizontal borders, null if missing. | ||||
*/ | */ | ||||
public String getInsideHBorderColor() { | public String getInsideHBorderColor() { | ||||
/** | /** | ||||
* Get inside vertical border size | * Get inside vertical border size | ||||
* | |||||
* | |||||
* @return The width of the Inside vertical borders in 1/8th points, | * @return The width of the Inside vertical borders in 1/8th points, | ||||
* -1 if missing. | * -1 if missing. | ||||
*/ | */ | ||||
/** | /** | ||||
* Get inside vertical border spacing | * Get inside vertical border spacing | ||||
* | |||||
* | |||||
* @return The offset to the Inside vertical borders in points, | * @return The offset to the Inside vertical borders in points, | ||||
* -1 if missing. | * -1 if missing. | ||||
*/ | */ | ||||
/** | /** | ||||
* Get inside vertical border color | * Get inside vertical border color | ||||
* | |||||
* | |||||
* @return The color of the Inside vertical borders, null if missing. | * @return The color of the Inside vertical borders, null if missing. | ||||
*/ | */ | ||||
public String getInsideVBorderColor() { | public String getInsideVBorderColor() { | ||||
/** | /** | ||||
* Get top border size | * Get top border size | ||||
* | |||||
* | |||||
* @return The width of the top borders in 1/8th points, | * @return The width of the top borders in 1/8th points, | ||||
* -1 if missing. | * -1 if missing. | ||||
*/ | */ | ||||
/** | /** | ||||
* Get top border spacing | * Get top border spacing | ||||
* | |||||
* | |||||
* @return The offset to the top borders in points, | * @return The offset to the top borders in points, | ||||
* -1 if missing. | * -1 if missing. | ||||
*/ | */ | ||||
/** | /** | ||||
* Get top border color | * Get top border color | ||||
* | |||||
* | |||||
* @return The color of the top borders, null if missing. | * @return The color of the top borders, null if missing. | ||||
*/ | */ | ||||
public String getTopBorderColor() { | public String getTopBorderColor() { | ||||
/** | /** | ||||
* Get bottom border size | * Get bottom border size | ||||
* | |||||
* | |||||
* @return The width of the bottom borders in 1/8th points, | * @return The width of the bottom borders in 1/8th points, | ||||
* -1 if missing. | * -1 if missing. | ||||
*/ | */ | ||||
/** | /** | ||||
* Get bottom border spacing | * Get bottom border spacing | ||||
* | |||||
* | |||||
* @return The offset to the bottom borders in points, | * @return The offset to the bottom borders in points, | ||||
* -1 if missing. | * -1 if missing. | ||||
*/ | */ | ||||
/** | /** | ||||
* Get bottom border color | * Get bottom border color | ||||
* | |||||
* | |||||
* @return The color of the bottom borders, null if missing. | * @return The color of the bottom borders, null if missing. | ||||
*/ | */ | ||||
public String getBottomBorderColor() { | public String getBottomBorderColor() { | ||||
/** | /** | ||||
* Get Left border size | * Get Left border size | ||||
* | |||||
* | |||||
* @return The width of the Left borders in 1/8th points, | * @return The width of the Left borders in 1/8th points, | ||||
* -1 if missing. | * -1 if missing. | ||||
*/ | */ | ||||
/** | /** | ||||
* Get Left border spacing | * Get Left border spacing | ||||
* | |||||
* | |||||
* @return The offset to the Left borders in points, | * @return The offset to the Left borders in points, | ||||
* -1 if missing. | * -1 if missing. | ||||
*/ | */ | ||||
/** | /** | ||||
* Get Left border color | * Get Left border color | ||||
* | |||||
* | |||||
* @return The color of the Left borders, null if missing. | * @return The color of the Left borders, null if missing. | ||||
*/ | */ | ||||
public String getLeftBorderColor() { | public String getLeftBorderColor() { | ||||
/** | /** | ||||
* Get Right border size | * Get Right border size | ||||
* | |||||
* | |||||
* @return The width of the Right borders in 1/8th points, | * @return The width of the Right borders in 1/8th points, | ||||
* -1 if missing. | * -1 if missing. | ||||
*/ | */ | ||||
/** | /** | ||||
* Get Right border spacing | * Get Right border spacing | ||||
* | |||||
* | |||||
* @return The offset to the Right borders in points, | * @return The offset to the Right borders in points, | ||||
* -1 if missing. | * -1 if missing. | ||||
*/ | */ | ||||
/** | /** | ||||
* Get Right border color | * Get Right border color | ||||
* | |||||
* | |||||
* @return The color of the Right borders, null if missing. | * @return The color of the Right borders, null if missing. | ||||
*/ | */ | ||||
public String getRightBorderColor() { | public String getRightBorderColor() { | ||||
* of a point) and a maximum value of 96 (twelve points). Any values outside this | * of a point) and a maximum value of 96 (twelve points). Any values outside this | ||||
* range may be reassigned to a more appropriate value. | * range may be reassigned to a more appropriate value. | ||||
* @param space - Specifies the spacing offset that shall be used to place this border on the table | * @param space - Specifies the spacing offset that shall be used to place this border on the table | ||||
* @param rgbColor - This color may either be presented as a hex value (in RRGGBB format), | |||||
* @param rgbColor - This color may either be presented as a hex value (in RRGGBB format), | |||||
* or auto to allow a consumer to automatically determine the border color as appropriate. | * or auto to allow a consumer to automatically determine the border color as appropriate. | ||||
*/ | */ | ||||
public void setInsideHBorder(XWPFBorderType type, int size, int space, String rgbColor) { | public void setInsideHBorder(XWPFBorderType type, int size, int space, String rgbColor) { | ||||
* of a point) and a maximum value of 96 (twelve points). Any values outside this | * of a point) and a maximum value of 96 (twelve points). Any values outside this | ||||
* range may be reassigned to a more appropriate value. | * range may be reassigned to a more appropriate value. | ||||
* @param space - Specifies the spacing offset that shall be used to place this border on the table | * @param space - Specifies the spacing offset that shall be used to place this border on the table | ||||
* @param rgbColor - This color may either be presented as a hex value (in RRGGBB format), | |||||
* @param rgbColor - This color may either be presented as a hex value (in RRGGBB format), | |||||
* or auto to allow a consumer to automatically determine the border color as appropriate. | * or auto to allow a consumer to automatically determine the border color as appropriate. | ||||
*/ | */ | ||||
public void setInsideVBorder(XWPFBorderType type, int size, int space, String rgbColor) { | public void setInsideVBorder(XWPFBorderType type, int size, int space, String rgbColor) { | ||||
* of a point) and a maximum value of 96 (twelve points). Any values outside this | * of a point) and a maximum value of 96 (twelve points). Any values outside this | ||||
* range may be reassigned to a more appropriate value. | * range may be reassigned to a more appropriate value. | ||||
* @param space - Specifies the spacing offset that shall be used to place this border on the table | * @param space - Specifies the spacing offset that shall be used to place this border on the table | ||||
* @param rgbColor - This color may either be presented as a hex value (in RRGGBB format), | |||||
* @param rgbColor - This color may either be presented as a hex value (in RRGGBB format), | |||||
* or auto to allow a consumer to automatically determine the border color as appropriate. | * or auto to allow a consumer to automatically determine the border color as appropriate. | ||||
*/ | */ | ||||
public void setTopBorder(XWPFBorderType type, int size, int space, String rgbColor) { | public void setTopBorder(XWPFBorderType type, int size, int space, String rgbColor) { | ||||
* of a point) and a maximum value of 96 (twelve points). Any values outside this | * of a point) and a maximum value of 96 (twelve points). Any values outside this | ||||
* range may be reassigned to a more appropriate value. | * range may be reassigned to a more appropriate value. | ||||
* @param space - Specifies the spacing offset that shall be used to place this border on the table | * @param space - Specifies the spacing offset that shall be used to place this border on the table | ||||
* @param rgbColor - This color may either be presented as a hex value (in RRGGBB format), | |||||
* @param rgbColor - This color may either be presented as a hex value (in RRGGBB format), | |||||
* or auto to allow a consumer to automatically determine the border color as appropriate. | * or auto to allow a consumer to automatically determine the border color as appropriate. | ||||
*/ | */ | ||||
public void setBottomBorder(XWPFBorderType type, int size, int space, String rgbColor) { | public void setBottomBorder(XWPFBorderType type, int size, int space, String rgbColor) { | ||||
* of a point) and a maximum value of 96 (twelve points). Any values outside this | * of a point) and a maximum value of 96 (twelve points). Any values outside this | ||||
* range may be reassigned to a more appropriate value. | * range may be reassigned to a more appropriate value. | ||||
* @param space - Specifies the spacing offset that shall be used to place this border on the table | * @param space - Specifies the spacing offset that shall be used to place this border on the table | ||||
* @param rgbColor - This color may either be presented as a hex value (in RRGGBB format), | |||||
* @param rgbColor - This color may either be presented as a hex value (in RRGGBB format), | |||||
* or auto to allow a consumer to automatically determine the border color as appropriate. | * or auto to allow a consumer to automatically determine the border color as appropriate. | ||||
*/ | */ | ||||
public void setLeftBorder(XWPFBorderType type, int size, int space, String rgbColor) { | public void setLeftBorder(XWPFBorderType type, int size, int space, String rgbColor) { | ||||
* of a point) and a maximum value of 96 (twelve points). Any values outside this | * of a point) and a maximum value of 96 (twelve points). Any values outside this | ||||
* range may be reassigned to a more appropriate value. | * range may be reassigned to a more appropriate value. | ||||
* @param space - Specifies the spacing offset that shall be used to place this border on the table | * @param space - Specifies the spacing offset that shall be used to place this border on the table | ||||
* @param rgbColor - This color may either be presented as a hex value (in RRGGBB format), | |||||
* @param rgbColor - This color may either be presented as a hex value (in RRGGBB format), | |||||
* or auto to allow a consumer to automatically determine the border color as appropriate. | * or auto to allow a consumer to automatically determine the border color as appropriate. | ||||
*/ | */ | ||||
public void setRightBorder(XWPFBorderType type, int size, int space, String rgbColor) { | public void setRightBorder(XWPFBorderType type, int size, int space, String rgbColor) { | ||||
public void removeInsideHBorder() { | public void removeInsideHBorder() { | ||||
removeBorder(Border.INSIDE_H); | removeBorder(Border.INSIDE_H); | ||||
} | } | ||||
/** | /** | ||||
* Remove inside vertical borders for table | * Remove inside vertical borders for table | ||||
*/ | */ | ||||
public void removeInsideVBorder() { | public void removeInsideVBorder() { | ||||
removeBorder(Border.INSIDE_V); | removeBorder(Border.INSIDE_V); | ||||
} | } | ||||
/** | /** | ||||
* Remove top borders for table | * Remove top borders for table | ||||
*/ | */ | ||||
public void removeBottomBorder() { | public void removeBottomBorder() { | ||||
removeBorder(Border.BOTTOM); | removeBorder(Border.BOTTOM); | ||||
} | } | ||||
/** | /** | ||||
* Remove left borders for table | * Remove left borders for table | ||||
*/ | */ | ||||
public void removeLeftBorder() { | public void removeLeftBorder() { | ||||
removeBorder(Border.LEFT); | removeBorder(Border.LEFT); | ||||
} | } | ||||
/** | /** | ||||
* Remove right borders for table | * Remove right borders for table | ||||
*/ | */ | ||||
public void removeRightBorder() { | public void removeRightBorder() { | ||||
removeBorder(Border.RIGHT); | removeBorder(Border.RIGHT); | ||||
} | } | ||||
/** | /** | ||||
* Remove all borders from table | * Remove all borders from table | ||||
*/ | */ | ||||
} | } | ||||
/** | /** | ||||
* removes the Borders node from Table properties if there are | |||||
* removes the Borders node from Table properties if there are | |||||
* no border elements | * no border elements | ||||
*/ | */ | ||||
private void cleanupTblBorders() { | private void cleanupTblBorders() { | ||||
} | } | ||||
} | } | ||||
} | } | ||||
public int getCellMarginTop() { | public int getCellMarginTop() { | ||||
return getCellMargin(CTTblCellMar::getTop); | return getCellMargin(CTTblCellMar::getTop); | ||||
} | } | ||||
* | * | ||||
* @see org.apache.poi.xwpf.usermodel.IBodyElement#getElementType() | * @see org.apache.poi.xwpf.usermodel.IBodyElement#getElementType() | ||||
*/ | */ | ||||
@Override | |||||
public BodyElementType getElementType() { | public BodyElementType getElementType() { | ||||
return BodyElementType.TABLE; | return BodyElementType.TABLE; | ||||
} | } | ||||
@Override | |||||
public IBody getBody() { | public IBody getBody() { | ||||
return part; | return part; | ||||
} | } | ||||
* | * | ||||
* @see org.apache.poi.xwpf.usermodel.IBody#getPart() | * @see org.apache.poi.xwpf.usermodel.IBody#getPart() | ||||
*/ | */ | ||||
@Override | |||||
public POIXMLDocumentPart getPart() { | public POIXMLDocumentPart getPart() { | ||||
if (part != null) { | if (part != null) { | ||||
return part.getPart(); | return part.getPart(); | ||||
* | * | ||||
* @see org.apache.poi.xwpf.usermodel.IBody#getPartType() | * @see org.apache.poi.xwpf.usermodel.IBody#getPartType() | ||||
*/ | */ | ||||
@Override | |||||
public BodyType getPartType() { | public BodyType getPartType() { | ||||
return part.getPartType(); | return part.getPartType(); | ||||
} | } | ||||
*/ | */ | ||||
public XWPFTableRow getRow(CTRow row) { | public XWPFTableRow getRow(CTRow row) { | ||||
for (int i = 0; i < getRows().size(); i++) { | for (int i = 0; i < getRows().size(); i++) { | ||||
if (getRows().get(i).getCtRow() == row) return getRow(i); | |||||
if (getRows().get(i).getCtRow() == row) { | |||||
return getRow(i); | |||||
} | |||||
} | } | ||||
return null; | return null; | ||||
} | } | ||||
/** | /** | ||||
* Get the table width as a decimal value. | * Get the table width as a decimal value. | ||||
* <p>If the width type is DXA or AUTO, then the value will always have | * <p>If the width type is DXA or AUTO, then the value will always have | ||||
protected static double getWidthDecimal(CTTblWidth ctWidth) { | protected static double getWidthDecimal(CTTblWidth ctWidth) { | ||||
double result = 0.0; | double result = 0.0; | ||||
STTblWidth.Enum typeValue = ctWidth.getType(); | STTblWidth.Enum typeValue = ctWidth.getType(); | ||||
if (typeValue == STTblWidth.DXA | |||||
|| typeValue == STTblWidth.AUTO | |||||
if (typeValue == STTblWidth.DXA | |||||
|| typeValue == STTblWidth.AUTO | |||||
|| typeValue == STTblWidth.NIL) { | || typeValue == STTblWidth.NIL) { | ||||
result = 0.0 + ctWidth.getW().intValue(); | result = 0.0 + ctWidth.getW().intValue(); | ||||
} else if (typeValue == STTblWidth.PCT) { | } else if (typeValue == STTblWidth.PCT) { | ||||
// Percentage values are stored as integers that are 50 times | // Percentage values are stored as integers that are 50 times | ||||
// percentage. | // percentage. | ||||
result = ctWidth.getW().intValue() / 50.0; | |||||
result = ctWidth.getW().intValue() / 50.0; | |||||
} else { | } else { | ||||
// Should never get here | // Should never get here | ||||
} | } | ||||
protected static void setWidthValue(String widthValue, CTTblWidth ctWidth) { | protected static void setWidthValue(String widthValue, CTTblWidth ctWidth) { | ||||
if (!widthValue.matches(REGEX_WIDTH_VALUE)) { | if (!widthValue.matches(REGEX_WIDTH_VALUE)) { | ||||
throw new RuntimeException("Table width value \"" + widthValue + "\" " | throw new RuntimeException("Table width value \"" + widthValue + "\" " | ||||
+ "must match regular expression \"" + REGEX_WIDTH_VALUE + "\"."); | |||||
+ "must match regular expression \"" + REGEX_WIDTH_VALUE + "\"."); | |||||
} | } | ||||
if (widthValue.matches("auto")) { | if (widthValue.matches("auto")) { | ||||
ctWidth.setType(STTblWidth.AUTO); | ctWidth.setType(STTblWidth.AUTO); | ||||
} else { | } else { | ||||
// Must be an integer | // Must be an integer | ||||
ctWidth.setW(new BigInteger(widthValue)); | ctWidth.setW(new BigInteger(widthValue)); | ||||
ctWidth.setType(STTblWidth.DXA); | |||||
ctWidth.setType(STTblWidth.DXA); | |||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Set the underlying table width value to a percentage value. | * Set the underlying table width value to a percentage value. | ||||
* @param ctWidth The CTTblWidth to set the value on | |||||
* @param ctWidth The CTTblWidth to set the value on | |||||
* @param widthValue String width value in form "33.3%" or an integer that is 50 times desired percentage value (e.g, | * @param widthValue String width value in form "33.3%" or an integer that is 50 times desired percentage value (e.g, | ||||
* 2500 for 50%) | * 2500 for 50%) | ||||
* @since 4.0.0 | * @since 4.0.0 | ||||
String numberPart = widthValue.substring(0, widthValue.length() - 1); | String numberPart = widthValue.substring(0, widthValue.length() - 1); | ||||
double percentage = Double.parseDouble(numberPart) * 50; | double percentage = Double.parseDouble(numberPart) * 50; | ||||
long intValue = Math.round(percentage); | long intValue = Math.round(percentage); | ||||
ctWidth.setW(BigInteger.valueOf(intValue)); | |||||
ctWidth.setW(BigInteger.valueOf(intValue)); | |||||
} else if (widthValue.matches("[0-9]+")) { | } else if (widthValue.matches("[0-9]+")) { | ||||
ctWidth.setW(new BigInteger(widthValue)); | ctWidth.setW(new BigInteger(widthValue)); | ||||
} else { | } else { | ||||
* @since 4.0.0 | * @since 4.0.0 | ||||
*/ | */ | ||||
public void setWidthType(TableWidthType widthType) { | public void setWidthType(TableWidthType widthType) { | ||||
setWidthType(widthType, getTblPr().getTblW()); | |||||
setWidthType(widthType, getTblPr().getTblW()); | |||||
} | } | ||||
/** | /** |