diff --git a/CHANGES.md b/CHANGES.md index ce50d79f5e..ffe5e83df7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # CHANGELOG +## 2.13.1 + +### :bug: Bugs fixed + +- Fix PDF Exporter outputs empty page when board has A4 format [Taiga #13181](https://tree.taiga.io/project/penpot/issue/13181) + ## 2.13.0 ### :heart: Community contributions (Thank you!) diff --git a/backend/src/app/media.clj b/backend/src/app/media.clj index de1a90fe20..bbb3123e73 100644 --- a/backend/src/app/media.clj +++ b/backend/src/app/media.clj @@ -35,8 +35,7 @@ javax.xml.parsers.SAXParserFactory org.apache.commons.io.IOUtils org.im4java.core.ConvertCmd - org.im4java.core.IMOperation - org.im4java.core.Info)) + org.im4java.core.IMOperation)) (def default-max-file-size (* 1024 1024 10)) ; 10 MiB @@ -224,17 +223,18 @@ ;; If we are processing an animated gif we use the first frame with -scene 0 (let [dim-result (sh/sh "identify" "-format" "%w %h\n" path) orient-result (sh/sh "identify" "-format" "%[EXIF:Orientation]\n" path)] - (if (and (= 0 (:exit dim-result)) - (= 0 (:exit orient-result))) + (when (= 0 (:exit dim-result)) (let [[w h] (-> (:out dim-result) str/trim (clojure.string/split #"\s+") (->> (mapv #(Integer/parseInt %)))) - orientation (-> orient-result :out str/trim)] - (case orientation - ("6" "8") {:width h :height w} ; Rotated 90 or 270 degrees - {:width w :height h})) ; Normal or unknown orientation - nil))) + orientation-exit (:exit orient-result) + orientation (-> orient-result :out str/trim)] + (if (= 0 orientation-exit) + (case orientation + ("6" "8") {:width h :height w} ; Rotated 90 or 270 degrees + {:width w :height h}) ; Normal or unknown orientation + {:width w :height h}))))) ; If orientation can't be read, use dimensions as-is (defmethod process :info [{:keys [input] :as params}] @@ -247,26 +247,37 @@ :hint "uploaded svg does not provides dimensions")) (merge input info {:ts (ct/now) :size (fs/size path)})) - (let [instance (Info. (str path)) - mtype' (.getProperty instance "Mime type")] + (let [path-str (str path) + identify-res (sh/sh "identify" "-format" "image/%[magick]\n" path-str) + ;; identify prints one line per frame (animated GIFs, etc.); we take the first one + mtype' (if (zero? (:exit identify-res)) + (-> identify-res + :out + str/trim + (str/split #"\s+" 2) + first + str/lower) + (ex/raise :type :validation + :code :invalid-image + :hint "invalid image")) + {:keys [width height]} + (or (get-dimensions-with-orientation path-str) + (do + (l/warn "Failed to read image dimensions with orientation" {:path path}) + (ex/raise :type :validation + :code :invalid-image + :hint "invalid image")))] (when (and (string? mtype) - (not= mtype mtype')) + (not= (str/lower mtype) mtype')) (ex/raise :type :validation :code :media-type-mismatch :hint (str "Seems like you are uploading a file whose content does not match the extension." "Expected: " mtype ". Got: " mtype'))) - (let [{:keys [width height]} - (or (get-dimensions-with-orientation (str path)) - (do - (l/warn "Failed to read image dimensions with orientation; falling back to im4java" - {:path path}) - {:width (.getPageWidth instance) - :height (.getPageHeight instance)}))] - (assoc input - :width width - :height height - :size (fs/size path) - :ts (ct/now))))))) + (assoc input + :width width + :height height + :size (fs/size path) + :ts (ct/now)))))) (defmethod process-error org.im4java.core.InfoException [error] diff --git a/docker/imagemagick/Dockerfile b/docker/imagemagick/Dockerfile index c13221d244..d06b0d2d3a 100644 --- a/docker/imagemagick/Dockerfile +++ b/docker/imagemagick/Dockerfile @@ -24,6 +24,7 @@ RUN set -e; \ libltdl-dev \ liblzma-dev \ libopenexr-dev \ + libxml2-dev \ libpng-dev \ librsvg2-dev \ libtiff-dev \ @@ -52,6 +53,7 @@ RUN set -e; \ libfftw3-dev \ libheif-dev \ libjpeg-dev \ + libxml2-dev \ liblcms2-dev \ libltdl-dev \ liblzma-dev \ @@ -77,6 +79,7 @@ RUN set -e; \ libopenjp2-7 \ libpng16-16 \ librsvg2-2 \ + libxml2 \ libtiff6 \ libwebp7 \ libwebpdemux2 \ diff --git a/docker/images/Dockerfile.backend b/docker/images/Dockerfile.backend index a651415f4e..9d5500ecc8 100644 --- a/docker/images/Dockerfile.backend +++ b/docker/images/Dockerfile.backend @@ -125,7 +125,7 @@ RUN set -ex; \ COPY --from=build /opt/jre /opt/jre COPY --from=build /opt/node /opt/node -COPY --from=penpotapp/imagemagick:7.1.2-0 /opt/imagick /opt/imagick +COPY --from=penpotapp/imagemagick:7.1.2-13 /opt/imagick /opt/imagick ARG BUNDLE_PATH="./bundle-backend/" COPY --chown=penpot:penpot $BUNDLE_PATH /opt/penpot/backend/ diff --git a/docker/images/Dockerfile.exporter b/docker/images/Dockerfile.exporter index 3b7883ae04..dbd8459255 100644 --- a/docker/images/Dockerfile.exporter +++ b/docker/images/Dockerfile.exporter @@ -107,7 +107,7 @@ RUN set -eux; \ ARG BUNDLE_PATH="./bundle-exporter/" COPY --chown=penpot:penpot $BUNDLE_PATH /opt/penpot/exporter/ -COPY --from=penpotapp/imagemagick:7.1.2-0 /opt/imagick /opt/imagick +COPY --from=penpotapp/imagemagick:7.1.2-13 /opt/imagick /opt/imagick WORKDIR /opt/penpot/exporter USER penpot:penpot diff --git a/exporter/src/app/renderer/pdf.cljs b/exporter/src/app/renderer/pdf.cljs index c7558184c4..25bcfc036b 100644 --- a/exporter/src/app/renderer/pdf.cljs +++ b/exporter/src/app/renderer/pdf.cljs @@ -38,6 +38,24 @@ (assoc :path "/render.html") (assoc :query (u/map->query-string params))))) + (sync-page-size! [dom] + (bw/eval! dom + (fn [elem] + ;; IMPORTANT: No CLJS runtime allowed. Use only JS + ;; primitives. This runs in a context without access to + ;; cljs.core. Avoid any functions that transpile to + ;; cljs.core/* calls, as they will break in the browser + ;; runtime. + + (let [width (.getAttribute ^js elem "width") + height (.getAttribute ^js elem "height") + style-node (let [node (.createElement js/document "style")] + (.appendChild (.-head js/document) node) + node)] + (set! (.-textContent style-node) + (dm/str "@page { size: " width "px " height "px; margin: 0; }\n" + "html, body, #app { margin: 0; padding: 0; width: " width "px; height: " height "px; overflow: visible; }")))))) + (render-object [page base-uri {:keys [id] :as object}] (p/let [uri (prepare-uri base-uri id) path (sh/tempfile :prefix "penpot.tmp.pdf." :suffix (mime/get-extension type))] @@ -45,6 +63,7 @@ (bw/nav! page uri) (p/let [dom (bw/select page (dm/str "#screenshot-" id))] (bw/wait-for dom) + (sync-page-size! dom) (bw/screenshot dom {:full-page? true}) (bw/sleep page 2000) ; the good old fix with sleep (bw/pdf page {:path path})