Skip to content

Commit b3885f6

Browse files
authored
feat: add Docker image (#4)
1 parent e692e20 commit b3885f6

8 files changed

+188
-259
lines changed

Dockerfile

+81-9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
FROM debian:11 AS chromium-build
1+
# Build environment
2+
# ==========================
3+
FROM --platform=linux/amd64 debian:11 AS chromium-build
24

35
WORKDIR /app
46

@@ -9,34 +11,104 @@ ENV DEBIAN_FRONTEND=noninteractive
911
ENV CHROMIUM_BUILDTOOLS_PATH=/app/electron/src/buildtools
1012
RUN apt-get update && \
1113
apt-get install -y git sudo curl ccache python3 bzip2 xz-utils && \
12-
curl -fsSL https://deb.nodesource.com/setup_18.x | bash - && \
14+
curl -fsSL https://deb.nodesource.com/setup_16.x | bash - && \
1315
apt-get install -y nodejs && \
1416
git clone --depth 1 --single-branch https://chromium.googlesource.com/chromium/tools/depot_tools.git
1517

1618
COPY electron/.gclient electron/
1719
COPY scripts/gclient.sh scripts/
18-
RUN --mount=type=cache,target=/app/.git_cache scripts/gclient.sh
20+
RUN --mount=type=cache,target=/app/.git_cache scripts/gclient.sh --revision "src/electron@cb22573c3e76e09df9fbad36dc372080c04d349e"
1921

2022
RUN electron/src/build/install-build-deps.sh
2123

2224
COPY scripts/patch.sh /app/scripts/
2325
COPY src/chromium.patch /app/src/
24-
RUN scripts/patch.sh
26+
COPY src/skia.patch /app/src/
27+
RUN scripts/patch.sh && ccache --max-size=0
28+
29+
ENV CCACHE_DIR=/app/.ccache
30+
ENV CCACHE_CPP2=yes
31+
ENV CCACHE_SLOPPINESS=time_macros
2532

26-
FROM chromium-build AS chromium-arm64
33+
34+
# ARM64 binaries
35+
# ==============
36+
FROM --platform=linux/amd64 chromium-build AS chromium-arm64
2737

2838
RUN electron/src/build/linux/sysroot_scripts/install-sysroot.py --arch=arm64
2939

3040
COPY scripts/gn.sh /app/scripts/
31-
RUN GN_ARGS='target_cpu="arm64" cc_wrapper="env CCACHE_DIR=/app/.ccache CCACHE_SLOPPINESS=time_macros ccache"' \
41+
RUN GN_ARGS='cc_wrapper="ccache" target_cpu="arm64"' \
42+
scripts/gn.sh release
43+
44+
COPY scripts/ninja.sh /app/scripts/
45+
RUN --mount=type=cache,target=/app/.ccache \
46+
--mount=type=cache,target=/app/.git_cache \
47+
scripts/ninja.sh release -j200
48+
49+
RUN electron/src/electron/script/strip-binaries.py -d electron/src/out/release --target-cpu=arm64 && \
50+
ninja -C electron/src/out/release electron:electron_dist_zip
51+
52+
53+
# AMD64 binaries
54+
# ==============
55+
FROM --platform=linux/amd64 chromium-build AS chromium-amd64
56+
57+
RUN electron/src/build/linux/sysroot_scripts/install-sysroot.py --arch=amd64
58+
59+
COPY scripts/gn.sh /app/scripts/
60+
RUN GN_ARGS='cc_wrapper="ccache"' \
3261
scripts/gn.sh release
3362

3463
COPY scripts/ninja.sh /app/scripts/
3564
RUN --mount=type=cache,target=/app/.ccache \
3665
--mount=type=cache,target=/app/.git_cache \
37-
electron/src/build/linux/sysroot_scripts/install-sysroot.py --arch=arm64 && \
3866
scripts/ninja.sh release -j200
3967

40-
FROM chromium-arm64
68+
RUN electron/src/electron/script/strip-binaries.py -d electron/src/out/release && \
69+
ninja -C electron/src/out/release electron:electron_dist_zip
70+
71+
72+
# Release binaries
73+
# ================
74+
FROM debian:11 AS html2svg-binaries
75+
76+
RUN apt-get update && apt-get install -y unzip
77+
78+
COPY --from=chromium-arm64 /app/electron/src/out/release/dist.zip /arm64.zip
79+
COPY --from=chromium-amd64 /app/electron/src/out/release/dist.zip /amd64.zip
80+
RUN unzip /arm64.zip -d /arm64
81+
RUN unzip /amd64.zip -d /amd64
82+
83+
# TypeScript build
84+
# ================
85+
FROM --platform=$BUILDPLATFORM node:18 AS html2svg-js
86+
87+
WORKDIR /app
88+
COPY package.json yarn.lock /app/
89+
RUN yarn
90+
91+
COPY tsconfig.json /app/
92+
COPY src /app/src
93+
RUN yarn tsc -b
94+
95+
# Main image
96+
# ==========
97+
FROM node:18
98+
99+
RUN apt-get update && \
100+
apt-get install --yes \
101+
libglib2.0-0 libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libgtk-3-0 libgbm1 libasound2 \
102+
xvfb x11-xkb-utils xfonts-100dpi xfonts-75dpi xfonts-scalable xfonts-cyrillic x11-apps
103+
104+
WORKDIR /app
105+
COPY package.json yarn.lock /app/
106+
RUN yarn --production
107+
108+
ARG TARGETARCH
109+
COPY --from=html2svg-js /app/build /app/build
110+
COPY --from=html2svg-binaries /${TARGETARCH} /runtime
111+
COPY /scripts/docker-entrypoint.sh /app/scripts/docker-entrypoint.sh
112+
113+
ENTRYPOINT ["/app/scripts/docker-entrypoint.sh"]
41114

42-
COPY --from=chromium-build /app/electron/src/out /app/electron/src/out

google.svg

-106
This file was deleted.

readme.md

+8-11
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,21 @@ Convert HTML and `<canvas>` to SVG using Chromium.
44

55
## Usage
66

7-
> A Docker image is currently being built.
8-
9-
### CLI
10-
117
```shell
12-
$ html2svg http://google.com > google.svg
8+
$ docker run fathyb/html2svg https://google.com > google.svg
139
```
1410

15-
### Server
11+
## Development
12+
13+
> - Building Chromium for ARM on Linux or Windows is not officially supported, cross-compile from x64 instead.
14+
15+
### Docker
1616

1717
```shell
18-
$ html2svg serve --port 8765
19-
$ curl -d http://google.com http://localhost:8765 > google.svg
18+
$ docker buildx build . --platform linux/arm64,linux/amd64
2019
```
2120

22-
## Development
23-
24-
> - Building Chromium for ARM on Linux or Windows is not officially supported, cross-compile from x64 instead.
21+
### Local
2522

2623
1. Fetch dependencies:
2724
```shell

scripts/docker-entrypoint.sh

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/bin/bash
2+
3+
set -e
4+
5+
export DISPLAY=:99
6+
7+
Xvfb $DISPLAY -screen 0 1920x1080x8 &
8+
/runtime/electron --no-sandbox --headless --disable-dev-shm-usage /app/build/html2svg.js "$@"
9+

scripts/patch.sh

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22

33
set -eo pipefail
44

5-
git -C electron/src apply --ignore-space-change --ignore-whitespace < src/chromium.patch
6-
git -C electron/src/third_party/skia apply --ignore-space-change --ignore-whitespace < src/skia.patch
5+
git -C electron/src apply --ignore-space-change --ignore-whitespace --reject < src/chromium.patch
6+
git -C electron/src/third_party/skia apply --ignore-space-change --ignore-whitespace --reject < src/skia.patch

src/chromium.patch

+12-12
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
2-
index 6a7e5ab92e6fa..be390eac56d3c 100644
2+
index 97cf24ad5f4a6..c1191c49697ca 100644
33
--- a/content/renderer/render_frame_impl.cc
44
+++ b/content/renderer/render_frame_impl.cc
5-
@@ -254,6 +254,12 @@
5+
@@ -256,6 +256,12 @@
66
#include "content/renderer/java/gin_java_bridge_dispatcher.h"
77
#endif
88

@@ -15,7 +15,7 @@ index 6a7e5ab92e6fa..be390eac56d3c 100644
1515
using base::Time;
1616
using blink::ContextMenuData;
1717
using blink::WebContentDecryptionModule;
18-
@@ -3842,6 +3848,93 @@ void RenderFrameImpl::DidClearWindowObject() {
18+
@@ -3822,6 +3828,93 @@ void RenderFrameImpl::DidClearWindowObject() {
1919

2020
for (auto& observer : observers_)
2121
observer.DidClearWindowObject();
@@ -110,10 +110,10 @@ index 6a7e5ab92e6fa..be390eac56d3c 100644
110110

111111
void RenderFrameImpl::DidCreateDocumentElement() {
112112
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
113-
index edc61a93bb7a1..62c4b418aedae 100644
113+
index 7a096b9a887d9..7440e54650989 100644
114114
--- a/skia/BUILD.gn
115115
+++ b/skia/BUILD.gn
116-
@@ -307,6 +307,15 @@ component("skia") {
116+
@@ -316,6 +316,15 @@ component("skia") {
117117
]
118118
}
119119

@@ -129,18 +129,18 @@ index edc61a93bb7a1..62c4b418aedae 100644
129129
# The *_public variables should be added to 'public' not 'sources'.
130130
# However, Skia does not export enough *_public variables to make Chromium
131131
# 'gn check' clean. Until that can be done add the *_public variables to
132-
@@ -369,9 +378,6 @@ component("skia") {
132+
@@ -386,9 +395,6 @@ component("skia") {
133133
sources += [ "//third_party/skia/src/core/SkUtilsArm.cpp" ]
134134
}
135135

136136
- # Remove unused util sources.
137137
- sources -= [ "//third_party/skia/src/utils/SkParsePath.cpp" ]
138138
-
139139
if (is_win) {
140-
libs = [ "fontsub.lib" ]
141-
}
140+
sources -= [
141+
# Keeping _win.cpp
142142
diff --git a/third_party/blink/public/web/web_frame_widget.h b/third_party/blink/public/web/web_frame_widget.h
143-
index 6264d513b398c..f304767c1bcdd 100644
143+
index 9b9d54e43a1a1..e2eaad722cc08 100644
144144
--- a/third_party/blink/public/web/web_frame_widget.h
145145
+++ b/third_party/blink/public/web/web_frame_widget.h
146146
@@ -48,6 +48,10 @@
@@ -154,7 +154,7 @@ index 6264d513b398c..f304767c1bcdd 100644
154154
namespace cc {
155155
struct ApplyViewportChangesArgs;
156156
class LayerTreeHost;
157-
@@ -227,6 +231,9 @@ class WebFrameWidget : public WebWidget {
157+
@@ -223,6 +227,9 @@ class WebFrameWidget : public WebWidget {
158158
// GPU benchmarking extension needs access to the LayerTreeHost
159159
friend class GpuBenchmarkingContext;
160160

@@ -165,7 +165,7 @@ index 6264d513b398c..f304767c1bcdd 100644
165165
// WebFrameWidgetImpl is the only concrete subclass that implements
166166
// WebFrameWidget, so that it is safe to downcast to WebFrameWidgetImpl.
167167
diff --git a/third_party/blink/renderer/core/frame/remote_frame_view.cc b/third_party/blink/renderer/core/frame/remote_frame_view.cc
168-
index 52e5d82981643..1d1865d641313 100644
168+
index a3e8944556ccd..874863225fda7 100644
169169
--- a/third_party/blink/renderer/core/frame/remote_frame_view.cc
170170
+++ b/third_party/blink/renderer/core/frame/remote_frame_view.cc
171171
@@ -419,7 +419,9 @@ uint32_t RemoteFrameView::Print(const gfx::Rect& rect,
@@ -180,7 +180,7 @@ index 52e5d82981643..1d1865d641313 100644
180180
// RACE: there is a possibility that the embedding token will be null and
181181
// still be in a valid state. This can occur is the frame has recently
182182
diff --git a/third_party/blink/renderer/platform/fonts/font.cc b/third_party/blink/renderer/platform/fonts/font.cc
183-
index 7b5c0ef5121e1..34718e30fb614 100644
183+
index 67ba540bfc2b0..f22e6988cf488 100644
184184
--- a/third_party/blink/renderer/platform/fonts/font.cc
185185
+++ b/third_party/blink/renderer/platform/fonts/font.cc
186186
@@ -230,15 +230,22 @@ void Font::DrawText(cc::PaintCanvas* canvas,

src/html2svg.ts

+42-8
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,34 @@
11
import { app, BrowserWindow } from 'electron'
22

3-
app.dock.hide()
4-
// app.commandLine.appendSwitch('enable-logging')
5-
app.commandLine.appendSwitch('headless')
6-
app.whenReady()
7-
.then(async () => {
3+
4+
Promise.resolve().then(async () => {
5+
const entry = process.argv.find(a => a.endsWith('html2svg.js'))
6+
const index = entry ? process.argv.indexOf(entry) : -1
7+
const args = process.argv.slice(Math.max(2, index + 1))
8+
const [url] = args
9+
10+
if (!url) {
11+
throw new Error('Usage: html2svg [url]')
12+
}
13+
14+
app.dock?.hide()
15+
app.commandLine.appendSwitch('headless')
16+
app.commandLine.appendSwitch('no-sandbox')
17+
app.commandLine.appendSwitch('disable-gpu')
18+
19+
await app.whenReady()
20+
21+
return url
22+
})
23+
.then(async (url) => {
824
const page = new BrowserWindow({
925
show: false,
1026
width: 1920,
1127
height: 1080,
28+
29+
webPreferences: {
30+
sandbox: false,
31+
}
1232
})
1333

1434
try {
@@ -28,7 +48,7 @@ app.whenReady()
2848

2949
page.webContents.once('did-finish-load', listener)
3050

31-
await page.loadURL('https://yari-demos.prod.mdn.mozit.cloud/en-US/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes/_sample_.making_combinations.html')
51+
await page.loadURL(url)
3252
})
3353
.catch(reject),
3454
)
@@ -61,8 +81,8 @@ app.whenReady()
6181
page.destroy()
6282
}
6383
})
64-
.then((result) => {
65-
console.log(result)
84+
.then(async (result) => {
85+
await print(result)
6686

6787
process.exit(0)
6888
})
@@ -71,3 +91,17 @@ app.whenReady()
7191

7292
process.exit(1)
7393
})
94+
95+
// Electron seems to drop lines if we send them too fast on slow streams like Docker..
96+
async function print(output: string) {
97+
const awfulBugSizeHeuristic = 1024
98+
99+
for(let i = 0; i < output.length; i += awfulBugSizeHeuristic) {
100+
await new Promise<void>((resolve, reject) =>
101+
process.stdout.write(
102+
output.slice(i, i + awfulBugSizeHeuristic),
103+
error => error ? reject(error) : resolve(),
104+
)
105+
)
106+
}
107+
}

0 commit comments

Comments
 (0)