Skip to content

Commit 4e46fcf

Browse files
committed
perf: stream output instead of buffering it
1 parent f7a862f commit 4e46fcf

File tree

2 files changed

+59
-60
lines changed

2 files changed

+59
-60
lines changed

src/chromium.patch

+57-43
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
2-
index 97cf24ad5f4a6..ae9f65e7bfcaa 100644
2+
index 97cf24ad5f4a6..859cb35da1f54 100644
33
--- a/content/renderer/render_frame_impl.cc
44
+++ b/content/renderer/render_frame_impl.cc
5-
@@ -256,6 +256,13 @@
5+
@@ -256,6 +256,15 @@
66
#include "content/renderer/java/gin_java_bridge_dispatcher.h"
77
#endif
88

9+
+// html2svg includes
10+
+#include <iostream>
911
+#include "cc/paint/paint_recorder.h"
1012
+#include "cc/paint/skia_paint_canvas.h"
1113
+#include "third_party/skia/include/core/SkStream.h"
@@ -16,11 +18,33 @@ index 97cf24ad5f4a6..ae9f65e7bfcaa 100644
1618
using base::Time;
1719
using blink::ContextMenuData;
1820
using blink::WebContentDecryptionModule;
19-
@@ -3822,6 +3829,96 @@ void RenderFrameImpl::DidClearWindowObject() {
21+
@@ -3822,6 +3831,108 @@ void RenderFrameImpl::DidClearWindowObject() {
2022

2123
for (auto& observer : observers_)
2224
observer.DidClearWindowObject();
2325
+
26+
+ // A Skia stream writing to stdout
27+
+ class StdoutStream : public SkWStream {
28+
+ public:
29+
+ bool write(const void* data, size_t size) override {
30+
+ std::cout.write(static_cast<const char*>(data), size);
31+
+ fBytesWritten += size;
32+
+
33+
+ return true;
34+
+ }
35+
+
36+
+ void flush() override {
37+
+ std::cout.flush();
38+
+ }
39+
+
40+
+ size_t bytesWritten() const override {
41+
+ return fBytesWritten;
42+
+ }
43+
+
44+
+ private:
45+
+ size_t fBytesWritten = 0;
46+
+ };
47+
+
2448
+ // Get access to the JS VM for this process (each tab is a process)
2549
+ v8::Isolate *isolate = blink::MainThreadIsolate();
2650
+ // Auto-clean v8 handles
@@ -49,58 +73,48 @@ index 97cf24ad5f4a6..ae9f65e7bfcaa 100644
4973
+ // Get the page size
5074
+ auto size = frame->DocumentSize();
5175
+ // Create a memory stream to save the SVG content
52-
+ SkDynamicMemoryWStream stream;
53-
+
54-
+ {
55-
+ // Create an SVG canvas with the dimensions of the layer
56-
+ int width = size.width(), height = size.height();
76+
+ StdoutStream stream;
77+
+ // Create an SVG canvas with the dimensions of the layer
78+
+ int width = size.width(), height = size.height();
5779
+
58-
+ cc::PaintRecorder recorder;
59-
+ auto rect = SkRect::MakeWH(width, height);
80+
+ cc::PaintRecorder recorder;
81+
+ auto rect = SkRect::MakeWH(width, height);
6082
+
61-
+ frame->CapturePaintPreview(
62-
+ gfx::Rect(0, 0, width, height),
63-
+ recorder.beginRecording(rect),
64-
+ false,
65-
+ true
66-
+ );
83+
+ frame->CapturePaintPreview(
84+
+ gfx::Rect(0, 0, width, height),
85+
+ recorder.beginRecording(rect),
86+
+ false,
87+
+ true
88+
+ );
6789
+
68-
+ auto picture = recorder.finishRecordingAsPicture();
90+
+ auto picture = recorder.finishRecordingAsPicture();
6991
+
70-
+ switch(args[0]->ToUint32(context).ToLocalChecked()->Value()) {
71-
+ // SVG
72-
+ case 0: {
73-
+ picture->Playback(SkSVGCanvas::Make(rect, &stream).get());
92+
+ switch(args[0]->ToUint32(context).ToLocalChecked()->Value()) {
93+
+ // SVG
94+
+ case 0: {
95+
+ picture->Playback(SkSVGCanvas::Make(rect, &stream).get());
7496
+
75-
+ break;
76-
+ }
77-
+ // PDF
78-
+ case 1: {
79-
+ SkPDF::Metadata metadata;
80-
+ v8::String::Utf8Value title(isolate, args[1]);
97+
+ break;
98+
+ }
99+
+ // PDF
100+
+ case 1: {
101+
+ SkPDF::Metadata metadata;
102+
+ v8::String::Utf8Value title(isolate, args[1]);
81103
+
82-
+ metadata.fTitle = *title;
83-
+ metadata.fCreator = "html2svg PDF back-end";
104+
+ metadata.fTitle = *title;
105+
+ metadata.fCreator = "html2svg PDF back-end";
84106
+
85-
+ auto document = SkPDF::MakeDocument(&stream, metadata);
107+
+ auto document = SkPDF::MakeDocument(&stream, metadata);
86108
+
87-
+ picture->Playback(document->beginPage(rect.width(), rect.height()));
88-
+ document->endPage();
89-
+ document->close();
109+
+ picture->Playback(document->beginPage(rect.width(), rect.height()));
110+
+ document->endPage();
111+
+ document->close();
90112
+
91-
+ break;
92-
+ }
113+
+ break;
93114
+ }
94115
+ }
95116
+
96-
+ // Allocate a buffer to hold the SVG data
97-
+ auto buffer = v8::ArrayBuffer::New(isolate, stream.bytesWritten());
98-
+
99-
+ // Copy from the stream to the buffer
100-
+ stream.copyTo(buffer->GetBackingStore()->Data());
101-
+
102-
+ // Return the data to the JS world
103-
+ args.GetReturnValue().Set(buffer);
117+
+ stream.flush();
104118
+ }
105119
+ );
106120
+

src/html2svg.ts

+2-17
Original file line numberDiff line numberDiff line change
@@ -89,34 +89,19 @@ program
8989
}).then(() => getPageContentsAsSVG(${mode}, document.title))
9090
`,
9191
)
92-
93-
await print(new Uint8Array(result))
9492
} finally {
9593
page.destroy()
9694
}
95+
96+
process.exit(0)
9797
})
9898
.parseAsync(args, { from: 'user' })
99-
.then(() => process.exit(0))
10099
.catch((error) => {
101100
console.error(error)
102101

103102
process.exit(1)
104103
})
105104

106-
// Electron seems to drop lines if we send them too fast on slow streams like Docker..
107-
async function print(output: Uint8Array) {
108-
const awfulBugSizeHeuristic = 1024
109-
110-
for (let i = 0; i < output.length; i += awfulBugSizeHeuristic) {
111-
await new Promise<void>((resolve, reject) =>
112-
process.stdout.write(
113-
output.slice(i, i + awfulBugSizeHeuristic),
114-
(error) => (error ? reject(error) : resolve()),
115-
),
116-
)
117-
}
118-
}
119-
120105
function getMode(format: string) {
121106
switch (format) {
122107
case 'svg':

0 commit comments

Comments
 (0)