Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,20 @@ public function adjustAction(Request $request, int $documentId): Response

/** @var UploadedFile $uploadedFile */
$uploadedFile = $fileForm->get('editDocument')->getData();

$uploadedFileMimeType = $uploadedFile->getMimeType();
if (
\is_string($uploadedFileMimeType)
&& \str_starts_with($uploadedFileMimeType, 'image/')
&& 'image/svg+xml' !== $uploadedFileMimeType
) {
$imageSize = \getimagesize($uploadedFile->getPathname());
if (\is_array($imageSize)) {
$document->setImageWidth((int) $imageSize[0]);
$document->setImageHeight((int) $imageSize[1]);
}
}

$this->documentFactory->setFile($uploadedFile);
$this->documentFactory->updateDocument($document);
$em->flush();
Expand All @@ -103,6 +117,8 @@ public function adjustAction(Request $request, int $documentId): Response
return new JsonResponse([
'message' => $msg,
'path' => $this->documentsStorage->publicUrl($mountPath).'?'.\random_int(10, 999),
'imageWidth' => $document->getImageWidth(),
'imageHeight' => $document->getImageHeight(),
]);
}

Expand Down
2 changes: 0 additions & 2 deletions lib/RoadizRozierBundle/src/TwigExtension/RozierExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,6 @@ public function getNodeSourceHref(mixed $node, ?TranslationInterface $translatio
return null;
}

/** @var NodeInterface&object{isHidingChildren(): bool} $node */

if ($node->isHidingChildren()) {
return $this->urlGenerator->generate('nodesTreePage', [
'nodeId' => $node->getId(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
data-vuejs
src-url="{{ document|url({ 'noProcess': true }) }}"
filename="{{ document.filename }}"
mime-type="{{ document.mimeType }}"
width="{{ document.imageWidth }}"
height="{{ document.imageHeight }}"
>
Expand Down
79 changes: 64 additions & 15 deletions lib/Rozier/app/containers/BlanchetteEditorContainer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@
:alt="name"
@load="load"
ref="image"
:width="width ? width : ''"
:height="height ? height : ''"
:width="currentWidth ? currentWidth : ''"
:height="currentHeight ? currentHeight : ''"
/>
</template>
</div>
Expand Down Expand Up @@ -94,6 +94,10 @@ export default {
required: true,
type: String,
},
mimeType: {
type: String,
default: '',
},
width: {
type: [Number, String],
},
Expand All @@ -110,14 +114,20 @@ export default {
canvasData: null,
cropBoxData: null,
image: null,
type: '',
type: 'image/png',
originalMimeType: '',
name: '',
url: this.srcUrl,
cropped: false,
aspectRatio: null,
currentWidth: this.width ? Number(this.width) : null,
currentHeight: this.height ? Number(this.height) : null,
}
},
mounted() {
this.originalMimeType = this.normalizeMimeType(this.mimeType)
this.type = this.resolveOutputMimeType(this.originalMimeType)

this.blanchetteEditorInit({
url: this.url,
editor: this.$refs.blanchetteEditor,
Expand All @@ -133,11 +143,50 @@ export default {
methods: {
...mapActions(['blanchetteEditorInit', 'blanchetteEditorLoaded', 'blanchetteEditorSave']),
overwrite() {
console.log(this)
this.blanchetteEditorSave({
url: this.url,
filename: this.filename,
}).then(() => this.restore())
filename: this.getOverwriteFilename(),
}).then((data) => {
if (
data &&
typeof data.imageWidth !== 'undefined' &&
typeof data.imageHeight !== 'undefined'
) {
this.currentWidth = Number(data.imageWidth)
this.currentHeight = Number(data.imageHeight)
}
this.restore()
})
},
normalizeMimeType(mimeType) {
if (!mimeType || typeof mimeType !== 'string') {
return ''
}

return mimeType.toLowerCase()
},
resolveOutputMimeType(mimeType) {
switch (mimeType) {
case 'image/jpeg':
case 'image/png':
case 'image/webp':
return mimeType
case 'image/gif':
default:
return 'image/png'
}
Comment on lines +168 to +177
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolveOutputMimeType now allows image/webp, but the crop implementation later treats any non-PNG output as needing a white fillColor background. Since WebP supports alpha like PNG, this will flatten transparency when outputting WebP. Adjust the crop canvas options logic to avoid forcing a fill color for WebP outputs.

Copilot uses AI. Check for mistakes.
},
getOverwriteFilename() {
if (this.type !== 'image/png' || this.originalMimeType === 'image/png') {
return this.filename
}

const extensionPosition = this.filename.lastIndexOf('.')
if (extensionPosition > 0) {
return `${this.filename.slice(0, extensionPosition)}.png`
}

return `${this.filename}.png`
},
async load() {
await sleep(1000)
Expand Down Expand Up @@ -246,15 +295,15 @@ export default {
this.data = cropper.getData()
this.canvasData = cropper.getCanvasData()
this.cropBoxData = cropper.getCropBoxData()
this.url = cropper
.getCroppedCanvas(
type === 'image/png'
? null
: {
fillColor: '#fff',
}
)
.toDataURL(type)
if (type === 'image/png') {
this.url = cropper.getCroppedCanvas().toDataURL(type)
} else {
this.url = cropper
.getCroppedCanvas({
fillColor: '#fff',
})
.toDataURL(type)
}

this.cropped = true
this.stop()
Expand Down
36 changes: 33 additions & 3 deletions lib/Rozier/app/store/modules/BlanchetteEditorStoreModule.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,17 @@ const actions = {

commit(BLANCHETTE_EDITOR_IS_LOADING)

// TODO: update document width and height
return DocumentApi.setDocument(formData)
.then(async (response) => {
const data = await response.json()
const data = await response.json().catch(() => null)

if (!response.ok) {
throw (
data ??
new Error(response.statusText || 'Request failed')
)
}

commit(BLANCHETTE_EDITOR_LOADED)
if (data && data.path) {
window.dispatchEvent(
Expand All @@ -56,13 +63,36 @@ const actions = {
}),
)
commit(BLANCHETTE_EDITOR_SAVE_SUCCESS, { path: data.path })
return data
} else {
throw new Error('No path found')
}
Comment on lines +66 to 69
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

throw new Error('No path found') will be handled by the .catch(...) below, but that catch currently expects an HTTP-style error with error.response.json(). For this thrown Error (and for fetch network failures), error.response will be undefined and the error handling will itself throw. Update the catch logic to safely handle plain Error objects (and consider checking response.ok before parsing JSON).

Copilot uses AI. Check for mistakes.
})
.catch(async (error) => {
let parsedError = null

if (
error?.response &&
typeof error.response.json === 'function'
) {
parsedError = await error.response.json().catch(() => null)
} else if (
error &&
typeof error === 'object' &&
!(error instanceof Error)
) {
parsedError = error
}

const errorMessage =
parsedError?.humanMessage ||
parsedError?.message ||
(error instanceof Error ? error.message : 'Request failed')

commit(BLANCHETTE_EDITOR_ERROR, {
error: await error.response.json(),
error: {
message: errorMessage,
},
})
commit(BLANCHETTE_EDITOR_LOADED)
})
Expand Down