1 | package pro.verron.officestamper.core; | |
2 | ||
3 | import jakarta.xml.bind.JAXBElement; | |
4 | import org.docx4j.dml.picture.Pic; | |
5 | import org.docx4j.dml.wordprocessingDrawing.Inline; | |
6 | import org.docx4j.openpackaging.exceptions.Docx4JException; | |
7 | import org.docx4j.openpackaging.packages.WordprocessingMLPackage; | |
8 | import org.docx4j.wml.Drawing; | |
9 | import org.docx4j.wml.R; | |
10 | import pro.verron.officestamper.api.OfficeStamperException; | |
11 | ||
12 | import java.io.IOException; | |
13 | import java.io.InputStream; | |
14 | import java.util.Arrays; | |
15 | ||
16 | /// Extracts images from a docx document. | |
17 | /// | |
18 | /// @author Joseph Verron | |
19 | /// @version ${version} | |
20 | /// @since 1.4.7 | |
21 | public class DocxImageExtractor { | |
22 | ||
23 | private final WordprocessingMLPackage wordprocessingMLPackage; | |
24 | ||
25 | /// Creates a new image extractor for the given docx document. | |
26 | /// | |
27 | /// @param wordprocessingMLPackage the docx document to extract images from. | |
28 | public DocxImageExtractor(WordprocessingMLPackage wordprocessingMLPackage) { | |
29 | this.wordprocessingMLPackage = wordprocessingMLPackage; | |
30 | } | |
31 | ||
32 | /// Extract an image bytes from an embedded image run. | |
33 | /// | |
34 | /// @param run run containing the embedded drawing. | |
35 | byte[] getRunDrawingData(R run) { | |
36 | String imageRelId = getPic(run).getBlipFill() | |
37 | .getBlip() | |
38 | .getEmbed(); | |
39 | String imageRelPartName = getImageRelPartName(imageRelId); | |
40 | long size = getImageSize(imageRelPartName); | |
41 | InputStream stream = getImageStream(imageRelPartName); | |
42 |
1
1. getRunDrawingData : replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::getRunDrawingData → KILLED |
return streamToByteArray(size, stream); |
43 | } | |
44 | ||
45 | private static Pic getPic(R run) { | |
46 | var content = run.getContent(); | |
47 | for (Object runContent : content) { | |
48 |
2
1. getPic : negated conditional → KILLED 2. getPic : negated conditional → KILLED |
if (runContent instanceof JAXBElement<?> runElement && runElement.getValue() instanceof Drawing drawing) { |
49 |
1
1. getPic : replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::getPic → KILLED |
return getPic(drawing); |
50 | } | |
51 | } | |
52 | throw new OfficeStamperException("Run drawing not found !"); | |
53 | } | |
54 | ||
55 | private String getImageRelPartName(String imageRelId) { | |
56 | // TODO: find a better way to find image rel part name in source part store | |
57 |
1
1. getImageRelPartName : replaced return value with "" for pro/verron/officestamper/core/DocxImageExtractor::getImageRelPartName → KILLED |
return wordprocessingMLPackage.getMainDocumentPart() |
58 | .getRelationshipsPart() | |
59 | .getPart(imageRelId) | |
60 | .getPartName() | |
61 | .getName() | |
62 | .substring(1); | |
63 | } | |
64 | ||
65 | private long getImageSize(String imageRelPartName) { | |
66 | try { | |
67 |
1
1. getImageSize : replaced long return with 0 for pro/verron/officestamper/core/DocxImageExtractor::getImageSize → KILLED |
return wordprocessingMLPackage.getSourcePartStore() |
68 | .getPartSize(imageRelPartName); | |
69 | } catch (Docx4JException e) { | |
70 | throw new OfficeStamperException(e); | |
71 | } | |
72 | } | |
73 | ||
74 | private InputStream getImageStream(String imageRelPartName) { | |
75 | try { | |
76 |
1
1. getImageStream : replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::getImageStream → KILLED |
return wordprocessingMLPackage.getSourcePartStore() |
77 | .loadPart(imageRelPartName); | |
78 | } catch (Docx4JException e) { | |
79 | throw new OfficeStamperException(e); | |
80 | } | |
81 | } | |
82 | ||
83 | /// Converts an InputStream to a byte array. | |
84 | /// | |
85 | /// @param size expected size of the byte array. | |
86 | /// @param is input stream to read data from. | |
87 | /// | |
88 | /// @return the data from the input stream. | |
89 | private static byte[] streamToByteArray(long size, InputStream is) { | |
90 |
2
1. streamToByteArray : changed conditional boundary → SURVIVED 2. streamToByteArray : negated conditional → KILLED |
if (size > Integer.MAX_VALUE) throw new OfficeStamperException("Image size exceeds maximum allowed (2GB)"); |
91 | ||
92 | int intSize = (int) size; | |
93 | byte[] data = new byte[intSize]; | |
94 | int numRead = tryRead(is, data); | |
95 |
1
1. streamToByteArray : replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::streamToByteArray → KILLED |
return Arrays.copyOfRange(data, 0, numRead); |
96 | } | |
97 | ||
98 | private static Pic getPic(Drawing drawing) { | |
99 | var anchorOrInline = drawing.getAnchorOrInline(); | |
100 |
1
1. getPic : negated conditional → KILLED |
if (anchorOrInline.isEmpty()) throw new OfficeStamperException("Anchor or Inline is empty !"); |
101 |
1
1. getPic : negated conditional → KILLED |
if (!(anchorOrInline.getFirst() instanceof Inline inline)) |
102 | throw new OfficeStamperException("Don't know how to process anchor !"); | |
103 |
1
1. getPic : replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::getPic → KILLED |
return inline.getGraphic() |
104 | .getGraphicData() | |
105 | .getPic(); | |
106 | } | |
107 | ||
108 | private static int tryRead(InputStream is, byte[] data) { | |
109 | try { | |
110 |
1
1. tryRead : replaced int return with 0 for pro/verron/officestamper/core/DocxImageExtractor::tryRead → KILLED |
return is.read(data); |
111 | } catch (IOException e) { | |
112 | throw new OfficeStamperException(e); | |
113 | } | |
114 | } | |
115 | ||
116 | /// Extract the width of the image from an embedded image run. | |
117 | /// | |
118 | /// @param run run containing the embedded drawing. | |
119 | /// | |
120 | /// @return a [Integer] object | |
121 | public Integer getRunDrawingMaxWidth(R run) { | |
122 |
1
1. getRunDrawingMaxWidth : replaced Integer return value with 0 for pro/verron/officestamper/core/DocxImageExtractor::getRunDrawingMaxWidth → SURVIVED |
return (int) getPic(run).getSpPr() |
123 | .getXfrm() | |
124 | .getExt() | |
125 | .getCx(); | |
126 | } | |
127 | } | |
Mutations | ||
42 |
1.1 |
|
48 |
1.1 2.2 |
|
49 |
1.1 |
|
57 |
1.1 |
|
67 |
1.1 |
|
76 |
1.1 |
|
90 |
1.1 2.2 |
|
95 |
1.1 |
|
100 |
1.1 |
|
101 |
1.1 |
|
103 |
1.1 |
|
110 |
1.1 |
|
122 |
1.1 |