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