DocxImageExtractor.java

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
Location : getRunDrawingData
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#9]
replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::getRunDrawingData → KILLED

51

1.1
Location : getPic
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#9]
negated conditional → KILLED

52

1.1
Location : getPic
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#9]
negated conditional → KILLED

53

1.1
Location : getPic
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#9]
negated conditional → KILLED

55

1.1
Location : getPic
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#9]
replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::getPic → KILLED

62

1.1
Location : getImageRelPartName
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#9]
replaced return value with "" for pro/verron/officestamper/core/DocxImageExtractor::getImageRelPartName → KILLED

78

1.1
Location : getInlineGraphic
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#9]
negated conditional → KILLED

83

1.1
Location : getInlineGraphic
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#9]
negated conditional → KILLED

84

1.1
Location : getInlineGraphic
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#9]
replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::getInlineGraphic → KILLED

99

1.1
Location : streamToByteArray
Killed by : none
changed conditional boundary → SURVIVED
Covering tests

2.2
Location : streamToByteArray
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#9]
negated conditional → KILLED

105

1.1
Location : streamToByteArray
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#9]
replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::streamToByteArray → KILLED

110

1.1
Location : tryRead
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#9]
replaced int return with 0 for pro/verron/officestamper/core/DocxImageExtractor::tryRead → KILLED

118

1.1
Location : getImageSize
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#9]
replaced long return with 0 for pro/verron/officestamper/core/DocxImageExtractor::getImageSize → KILLED

128

1.1
Location : getImageStream
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#9]
replaced return value with null for pro/verron/officestamper/core/DocxImageExtractor::getImageStream → KILLED

143

1.1
Location : getRunDrawingFilename
Killed by : none
replaced return value with "" for pro/verron/officestamper/core/DocxImageExtractor::getRunDrawingFilename → NO_COVERAGE

153

1.1
Location : getRunDrawingAltText
Killed by : none
replaced return value with "" for pro/verron/officestamper/core/DocxImageExtractor::getRunDrawingAltText → NO_COVERAGE

163

1.1
Location : getRunDrawingMaxWidth
Killed by : none
replaced Integer return value with 0 for pro/verron/officestamper/core/DocxImageExtractor::getRunDrawingMaxWidth → SURVIVED
Covering tests

Active mutators

Tests examined


Report generated by PIT 1.17.0