Canvas.cc 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965
  1. // Copyright (c) 2010 LearnBoost <tj@learnboost.com>
  2. #include "Canvas.h"
  3. #include <algorithm> // std::min
  4. #include <assert.h>
  5. #include <cairo-pdf.h>
  6. #include <cairo-svg.h>
  7. #include "CanvasRenderingContext2d.h"
  8. #include "closure.h"
  9. #include <cstring>
  10. #include <cctype>
  11. #include <ctime>
  12. #include <glib.h>
  13. #include "PNG.h"
  14. #include "register_font.h"
  15. #include <sstream>
  16. #include <stdlib.h>
  17. #include <string>
  18. #include <unordered_set>
  19. #include "Util.h"
  20. #include <vector>
  21. #include "node_buffer.h"
  22. #ifdef HAVE_JPEG
  23. #include "JPEGStream.h"
  24. #endif
  25. #include "backend/ImageBackend.h"
  26. #include "backend/PdfBackend.h"
  27. #include "backend/SvgBackend.h"
  28. #define GENERIC_FACE_ERROR \
  29. "The second argument to registerFont is required, and should be an object " \
  30. "with at least a family (string) and optionally weight (string/number) " \
  31. "and style (string)."
  32. #define CHECK_RECEIVER(prop) \
  33. if (!Canvas::constructor.Get(info.GetIsolate())->HasInstance(info.This())) { \
  34. Nan::ThrowTypeError("Method " #prop " called on incompatible receiver"); \
  35. return; \
  36. }
  37. using namespace v8;
  38. using namespace std;
  39. Nan::Persistent<FunctionTemplate> Canvas::constructor;
  40. std::vector<FontFace> font_face_list;
  41. /*
  42. * Initialize Canvas.
  43. */
  44. void
  45. Canvas::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) {
  46. Nan::HandleScope scope;
  47. // Constructor
  48. Local<FunctionTemplate> ctor = Nan::New<FunctionTemplate>(Canvas::New);
  49. constructor.Reset(ctor);
  50. ctor->InstanceTemplate()->SetInternalFieldCount(1);
  51. ctor->SetClassName(Nan::New("Canvas").ToLocalChecked());
  52. // Prototype
  53. Local<ObjectTemplate> proto = ctor->PrototypeTemplate();
  54. Nan::SetPrototypeMethod(ctor, "toBuffer", ToBuffer);
  55. Nan::SetPrototypeMethod(ctor, "streamPNGSync", StreamPNGSync);
  56. Nan::SetPrototypeMethod(ctor, "streamPDFSync", StreamPDFSync);
  57. #ifdef HAVE_JPEG
  58. Nan::SetPrototypeMethod(ctor, "streamJPEGSync", StreamJPEGSync);
  59. #endif
  60. Nan::SetAccessor(proto, Nan::New("type").ToLocalChecked(), GetType);
  61. Nan::SetAccessor(proto, Nan::New("stride").ToLocalChecked(), GetStride);
  62. Nan::SetAccessor(proto, Nan::New("width").ToLocalChecked(), GetWidth, SetWidth);
  63. Nan::SetAccessor(proto, Nan::New("height").ToLocalChecked(), GetHeight, SetHeight);
  64. Nan::SetTemplate(proto, "PNG_NO_FILTERS", Nan::New<Uint32>(PNG_NO_FILTERS));
  65. Nan::SetTemplate(proto, "PNG_FILTER_NONE", Nan::New<Uint32>(PNG_FILTER_NONE));
  66. Nan::SetTemplate(proto, "PNG_FILTER_SUB", Nan::New<Uint32>(PNG_FILTER_SUB));
  67. Nan::SetTemplate(proto, "PNG_FILTER_UP", Nan::New<Uint32>(PNG_FILTER_UP));
  68. Nan::SetTemplate(proto, "PNG_FILTER_AVG", Nan::New<Uint32>(PNG_FILTER_AVG));
  69. Nan::SetTemplate(proto, "PNG_FILTER_PAETH", Nan::New<Uint32>(PNG_FILTER_PAETH));
  70. Nan::SetTemplate(proto, "PNG_ALL_FILTERS", Nan::New<Uint32>(PNG_ALL_FILTERS));
  71. // Class methods
  72. Nan::SetMethod(ctor, "_registerFont", RegisterFont);
  73. Nan::SetMethod(ctor, "_deregisterAllFonts", DeregisterAllFonts);
  74. Local<Context> ctx = Nan::GetCurrentContext();
  75. Nan::Set(target,
  76. Nan::New("Canvas").ToLocalChecked(),
  77. ctor->GetFunction(ctx).ToLocalChecked());
  78. }
  79. /*
  80. * Initialize a Canvas with the given width and height.
  81. */
  82. NAN_METHOD(Canvas::New) {
  83. if (!info.IsConstructCall()) {
  84. return Nan::ThrowTypeError("Class constructors cannot be invoked without 'new'");
  85. }
  86. Backend* backend = NULL;
  87. if (info[0]->IsNumber()) {
  88. int width = Nan::To<uint32_t>(info[0]).FromMaybe(0), height = 0;
  89. if (info[1]->IsNumber()) height = Nan::To<uint32_t>(info[1]).FromMaybe(0);
  90. if (info[2]->IsString()) {
  91. if (0 == strcmp("pdf", *Nan::Utf8String(info[2])))
  92. backend = new PdfBackend(width, height);
  93. else if (0 == strcmp("svg", *Nan::Utf8String(info[2])))
  94. backend = new SvgBackend(width, height);
  95. else
  96. backend = new ImageBackend(width, height);
  97. }
  98. else
  99. backend = new ImageBackend(width, height);
  100. }
  101. else if (info[0]->IsObject()) {
  102. if (Nan::New(ImageBackend::constructor)->HasInstance(info[0]) ||
  103. Nan::New(PdfBackend::constructor)->HasInstance(info[0]) ||
  104. Nan::New(SvgBackend::constructor)->HasInstance(info[0])) {
  105. backend = Nan::ObjectWrap::Unwrap<Backend>(Nan::To<Object>(info[0]).ToLocalChecked());
  106. }else{
  107. return Nan::ThrowTypeError("Invalid arguments");
  108. }
  109. }
  110. else {
  111. backend = new ImageBackend(0, 0);
  112. }
  113. if (!backend->isSurfaceValid()) {
  114. delete backend;
  115. return Nan::ThrowError(backend->getError());
  116. }
  117. Canvas* canvas = new Canvas(backend);
  118. canvas->Wrap(info.This());
  119. backend->setCanvas(canvas);
  120. info.GetReturnValue().Set(info.This());
  121. }
  122. /*
  123. * Get type string.
  124. */
  125. NAN_GETTER(Canvas::GetType) {
  126. CHECK_RECEIVER(Canvas.GetType);
  127. Canvas *canvas = Nan::ObjectWrap::Unwrap<Canvas>(info.This());
  128. info.GetReturnValue().Set(Nan::New<String>(canvas->backend()->getName()).ToLocalChecked());
  129. }
  130. /*
  131. * Get stride.
  132. */
  133. NAN_GETTER(Canvas::GetStride) {
  134. CHECK_RECEIVER(Canvas.GetStride);
  135. Canvas *canvas = Nan::ObjectWrap::Unwrap<Canvas>(info.This());
  136. info.GetReturnValue().Set(Nan::New<Number>(canvas->stride()));
  137. }
  138. /*
  139. * Get width.
  140. */
  141. NAN_GETTER(Canvas::GetWidth) {
  142. CHECK_RECEIVER(Canvas.GetWidth);
  143. Canvas *canvas = Nan::ObjectWrap::Unwrap<Canvas>(info.This());
  144. info.GetReturnValue().Set(Nan::New<Number>(canvas->getWidth()));
  145. }
  146. /*
  147. * Set width.
  148. */
  149. NAN_SETTER(Canvas::SetWidth) {
  150. CHECK_RECEIVER(Canvas.SetWidth);
  151. if (value->IsNumber()) {
  152. Canvas *canvas = Nan::ObjectWrap::Unwrap<Canvas>(info.This());
  153. canvas->backend()->setWidth(Nan::To<uint32_t>(value).FromMaybe(0));
  154. canvas->resurface(info.This());
  155. }
  156. }
  157. /*
  158. * Get height.
  159. */
  160. NAN_GETTER(Canvas::GetHeight) {
  161. CHECK_RECEIVER(Canvas.GetHeight);
  162. Canvas *canvas = Nan::ObjectWrap::Unwrap<Canvas>(info.This());
  163. info.GetReturnValue().Set(Nan::New<Number>(canvas->getHeight()));
  164. }
  165. /*
  166. * Set height.
  167. */
  168. NAN_SETTER(Canvas::SetHeight) {
  169. CHECK_RECEIVER(Canvas.SetHeight);
  170. if (value->IsNumber()) {
  171. Canvas *canvas = Nan::ObjectWrap::Unwrap<Canvas>(info.This());
  172. canvas->backend()->setHeight(Nan::To<uint32_t>(value).FromMaybe(0));
  173. canvas->resurface(info.This());
  174. }
  175. }
  176. /*
  177. * EIO toBuffer callback.
  178. */
  179. void
  180. Canvas::ToPngBufferAsync(uv_work_t *req) {
  181. PngClosure* closure = static_cast<PngClosure*>(req->data);
  182. closure->status = canvas_write_to_png_stream(
  183. closure->canvas->surface(),
  184. PngClosure::writeVec,
  185. closure);
  186. }
  187. #ifdef HAVE_JPEG
  188. void
  189. Canvas::ToJpegBufferAsync(uv_work_t *req) {
  190. JpegClosure* closure = static_cast<JpegClosure*>(req->data);
  191. write_to_jpeg_buffer(closure->canvas->surface(), closure);
  192. }
  193. #endif
  194. /*
  195. * EIO after toBuffer callback.
  196. */
  197. void
  198. Canvas::ToBufferAsyncAfter(uv_work_t *req) {
  199. Nan::HandleScope scope;
  200. Nan::AsyncResource async("canvas:ToBufferAsyncAfter");
  201. Closure* closure = static_cast<Closure*>(req->data);
  202. delete req;
  203. if (closure->status) {
  204. Local<Value> argv[1] = { Canvas::Error(closure->status) };
  205. closure->cb.Call(1, argv, &async);
  206. } else {
  207. Local<Object> buf = Nan::CopyBuffer((char*)&closure->vec[0], closure->vec.size()).ToLocalChecked();
  208. Local<Value> argv[2] = { Nan::Null(), buf };
  209. closure->cb.Call(sizeof argv / sizeof *argv, argv, &async);
  210. }
  211. closure->canvas->Unref();
  212. delete closure;
  213. }
  214. static void parsePNGArgs(Local<Value> arg, PngClosure& pngargs) {
  215. if (arg->IsObject()) {
  216. Local<Object> obj = Nan::To<Object>(arg).ToLocalChecked();
  217. Local<Value> cLevel = Nan::Get(obj, Nan::New("compressionLevel").ToLocalChecked()).ToLocalChecked();
  218. if (cLevel->IsUint32()) {
  219. uint32_t val = Nan::To<uint32_t>(cLevel).FromMaybe(0);
  220. // See quote below from spec section 4.12.5.5.
  221. if (val <= 9) pngargs.compressionLevel = val;
  222. }
  223. Local<Value> rez = Nan::Get(obj, Nan::New("resolution").ToLocalChecked()).ToLocalChecked();
  224. if (rez->IsUint32()) {
  225. uint32_t val = Nan::To<uint32_t>(rez).FromMaybe(0);
  226. if (val > 0) pngargs.resolution = val;
  227. }
  228. Local<Value> filters = Nan::Get(obj, Nan::New("filters").ToLocalChecked()).ToLocalChecked();
  229. if (filters->IsUint32()) pngargs.filters = Nan::To<uint32_t>(filters).FromMaybe(0);
  230. Local<Value> palette = Nan::Get(obj, Nan::New("palette").ToLocalChecked()).ToLocalChecked();
  231. if (palette->IsUint8ClampedArray()) {
  232. Local<Uint8ClampedArray> palette_ta = palette.As<Uint8ClampedArray>();
  233. pngargs.nPaletteColors = palette_ta->Length();
  234. if (pngargs.nPaletteColors % 4 != 0) {
  235. throw "Palette length must be a multiple of 4.";
  236. }
  237. pngargs.nPaletteColors /= 4;
  238. Nan::TypedArrayContents<uint8_t> _paletteColors(palette_ta);
  239. pngargs.palette = *_paletteColors;
  240. // Optional background color index:
  241. Local<Value> backgroundIndexVal = Nan::Get(obj, Nan::New("backgroundIndex").ToLocalChecked()).ToLocalChecked();
  242. if (backgroundIndexVal->IsUint32()) {
  243. pngargs.backgroundIndex = static_cast<uint8_t>(Nan::To<uint32_t>(backgroundIndexVal).FromMaybe(0));
  244. }
  245. }
  246. }
  247. }
  248. #ifdef HAVE_JPEG
  249. static void parseJPEGArgs(Local<Value> arg, JpegClosure& jpegargs) {
  250. // "If Type(quality) is not Number, or if quality is outside that range, the
  251. // user agent must use its default quality value, as if the quality argument
  252. // had not been given." - 4.12.5.5
  253. if (arg->IsObject()) {
  254. Local<Object> obj = Nan::To<Object>(arg).ToLocalChecked();
  255. Local<Value> qual = Nan::Get(obj, Nan::New("quality").ToLocalChecked()).ToLocalChecked();
  256. if (qual->IsNumber()) {
  257. double quality = Nan::To<double>(qual).FromMaybe(0);
  258. if (quality >= 0.0 && quality <= 1.0) {
  259. jpegargs.quality = static_cast<uint32_t>(100.0 * quality);
  260. }
  261. }
  262. Local<Value> chroma = Nan::Get(obj, Nan::New("chromaSubsampling").ToLocalChecked()).ToLocalChecked();
  263. if (chroma->IsBoolean()) {
  264. bool subsample = Nan::To<bool>(chroma).FromMaybe(0);
  265. jpegargs.chromaSubsampling = subsample ? 2 : 1;
  266. } else if (chroma->IsNumber()) {
  267. jpegargs.chromaSubsampling = Nan::To<uint32_t>(chroma).FromMaybe(0);
  268. }
  269. Local<Value> progressive = Nan::Get(obj, Nan::New("progressive").ToLocalChecked()).ToLocalChecked();
  270. if (!progressive->IsUndefined()) {
  271. jpegargs.progressive = Nan::To<bool>(progressive).FromMaybe(0);
  272. }
  273. }
  274. }
  275. #endif
  276. #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 16, 0)
  277. static inline void setPdfMetaStr(cairo_surface_t* surf, Local<Object> opts,
  278. cairo_pdf_metadata_t t, const char* pName) {
  279. auto propName = Nan::New(pName).ToLocalChecked();
  280. auto propValue = Nan::Get(opts, propName).ToLocalChecked();
  281. if (propValue->IsString()) {
  282. // (copies char data)
  283. cairo_pdf_surface_set_metadata(surf, t, *Nan::Utf8String(propValue));
  284. }
  285. }
  286. static inline void setPdfMetaDate(cairo_surface_t* surf, Local<Object> opts,
  287. cairo_pdf_metadata_t t, const char* pName) {
  288. auto propName = Nan::New(pName).ToLocalChecked();
  289. auto propValue = Nan::Get(opts, propName).ToLocalChecked();
  290. if (propValue->IsDate()) {
  291. auto date = static_cast<time_t>(propValue.As<v8::Date>()->ValueOf() / 1000); // ms -> s
  292. char buf[sizeof "2011-10-08T07:07:09Z"];
  293. strftime(buf, sizeof buf, "%FT%TZ", gmtime(&date));
  294. cairo_pdf_surface_set_metadata(surf, t, buf);
  295. }
  296. }
  297. static void setPdfMetadata(Canvas* canvas, Local<Object> opts) {
  298. cairo_surface_t* surf = canvas->surface();
  299. setPdfMetaStr(surf, opts, CAIRO_PDF_METADATA_TITLE, "title");
  300. setPdfMetaStr(surf, opts, CAIRO_PDF_METADATA_AUTHOR, "author");
  301. setPdfMetaStr(surf, opts, CAIRO_PDF_METADATA_SUBJECT, "subject");
  302. setPdfMetaStr(surf, opts, CAIRO_PDF_METADATA_KEYWORDS, "keywords");
  303. setPdfMetaStr(surf, opts, CAIRO_PDF_METADATA_CREATOR, "creator");
  304. setPdfMetaDate(surf, opts, CAIRO_PDF_METADATA_CREATE_DATE, "creationDate");
  305. setPdfMetaDate(surf, opts, CAIRO_PDF_METADATA_MOD_DATE, "modDate");
  306. }
  307. #endif // CAIRO 16+
  308. /*
  309. * Converts/encodes data to a Buffer. Async when a callback function is passed.
  310. * PDF canvases:
  311. (any) => Buffer
  312. ("application/pdf", config) => Buffer
  313. * SVG canvases:
  314. (any) => Buffer
  315. * ARGB data:
  316. ("raw") => Buffer
  317. * PNG-encoded
  318. () => Buffer
  319. (undefined|"image/png", {compressionLevel?: number, filter?: number}) => Buffer
  320. ((err: null|Error, buffer) => any)
  321. ((err: null|Error, buffer) => any, undefined|"image/png", {compressionLevel?: number, filter?: number})
  322. * JPEG-encoded
  323. ("image/jpeg") => Buffer
  324. ("image/jpeg", {quality?: number, progressive?: Boolean, chromaSubsampling?: Boolean|number}) => Buffer
  325. ((err: null|Error, buffer) => any, "image/jpeg")
  326. ((err: null|Error, buffer) => any, "image/jpeg", {quality?: number, progressive?: Boolean, chromaSubsampling?: Boolean|number})
  327. */
  328. NAN_METHOD(Canvas::ToBuffer) {
  329. cairo_status_t status;
  330. Canvas *canvas = Nan::ObjectWrap::Unwrap<Canvas>(info.This());
  331. // Vector canvases, sync only
  332. const std::string name = canvas->backend()->getName();
  333. if (name == "pdf" || name == "svg") {
  334. // mime type may be present, but it's not checked
  335. PdfSvgClosure* closure;
  336. if (name == "pdf") {
  337. closure = static_cast<PdfBackend*>(canvas->backend())->closure();
  338. #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 16, 0)
  339. if (info[1]->IsObject()) { // toBuffer("application/pdf", config)
  340. setPdfMetadata(canvas, Nan::To<Object>(info[1]).ToLocalChecked());
  341. }
  342. #endif // CAIRO 16+
  343. } else {
  344. closure = static_cast<SvgBackend*>(canvas->backend())->closure();
  345. }
  346. cairo_surface_finish(canvas->surface());
  347. Local<Object> buf = Nan::CopyBuffer((char*)&closure->vec[0], closure->vec.size()).ToLocalChecked();
  348. info.GetReturnValue().Set(buf);
  349. return;
  350. }
  351. // Raw ARGB data -- just a memcpy()
  352. if (info[0]->StrictEquals(Nan::New<String>("raw").ToLocalChecked())) {
  353. cairo_surface_t *surface = canvas->surface();
  354. cairo_surface_flush(surface);
  355. if (canvas->nBytes() > node::Buffer::kMaxLength) {
  356. Nan::ThrowError("Data exceeds maximum buffer length.");
  357. return;
  358. }
  359. const unsigned char *data = cairo_image_surface_get_data(surface);
  360. Isolate* iso = Nan::GetCurrentContext()->GetIsolate();
  361. Local<Object> buf = node::Buffer::Copy(iso, reinterpret_cast<const char*>(data), canvas->nBytes()).ToLocalChecked();
  362. info.GetReturnValue().Set(buf);
  363. return;
  364. }
  365. // Sync PNG, default
  366. if (info[0]->IsUndefined() || info[0]->StrictEquals(Nan::New<String>("image/png").ToLocalChecked())) {
  367. try {
  368. PngClosure closure(canvas);
  369. parsePNGArgs(info[1], closure);
  370. if (closure.nPaletteColors == 0xFFFFFFFF) {
  371. Nan::ThrowError("Palette length must be a multiple of 4.");
  372. return;
  373. }
  374. Nan::TryCatch try_catch;
  375. status = canvas_write_to_png_stream(canvas->surface(), PngClosure::writeVec, &closure);
  376. if (try_catch.HasCaught()) {
  377. try_catch.ReThrow();
  378. } else if (status) {
  379. throw status;
  380. } else {
  381. // TODO it's possible to avoid this copy
  382. Local<Object> buf = Nan::CopyBuffer((char *)&closure.vec[0], closure.vec.size()).ToLocalChecked();
  383. info.GetReturnValue().Set(buf);
  384. }
  385. } catch (cairo_status_t ex) {
  386. Nan::ThrowError(Canvas::Error(ex));
  387. } catch (const char* ex) {
  388. Nan::ThrowError(ex);
  389. }
  390. return;
  391. }
  392. // Async PNG
  393. if (info[0]->IsFunction() &&
  394. (info[1]->IsUndefined() || info[1]->StrictEquals(Nan::New<String>("image/png").ToLocalChecked()))) {
  395. PngClosure* closure;
  396. try {
  397. closure = new PngClosure(canvas);
  398. parsePNGArgs(info[2], *closure);
  399. } catch (cairo_status_t ex) {
  400. Nan::ThrowError(Canvas::Error(ex));
  401. return;
  402. } catch (const char* ex) {
  403. Nan::ThrowError(ex);
  404. return;
  405. }
  406. canvas->Ref();
  407. closure->cb.Reset(info[0].As<Function>());
  408. uv_work_t* req = new uv_work_t;
  409. req->data = closure;
  410. // Make sure the surface exists since we won't have an isolate context in the async block:
  411. canvas->surface();
  412. uv_queue_work(uv_default_loop(), req, ToPngBufferAsync, (uv_after_work_cb)ToBufferAsyncAfter);
  413. return;
  414. }
  415. #ifdef HAVE_JPEG
  416. // Sync JPEG
  417. Local<Value> jpegStr = Nan::New<String>("image/jpeg").ToLocalChecked();
  418. if (info[0]->StrictEquals(jpegStr)) {
  419. try {
  420. JpegClosure closure(canvas);
  421. parseJPEGArgs(info[1], closure);
  422. Nan::TryCatch try_catch;
  423. write_to_jpeg_buffer(canvas->surface(), &closure);
  424. if (try_catch.HasCaught()) {
  425. try_catch.ReThrow();
  426. } else {
  427. // TODO it's possible to avoid this copy.
  428. Local<Object> buf = Nan::CopyBuffer((char *)&closure.vec[0], closure.vec.size()).ToLocalChecked();
  429. info.GetReturnValue().Set(buf);
  430. }
  431. } catch (cairo_status_t ex) {
  432. Nan::ThrowError(Canvas::Error(ex));
  433. }
  434. return;
  435. }
  436. // Async JPEG
  437. if (info[0]->IsFunction() && info[1]->StrictEquals(jpegStr)) {
  438. JpegClosure* closure = new JpegClosure(canvas);
  439. parseJPEGArgs(info[2], *closure);
  440. canvas->Ref();
  441. closure->cb.Reset(info[0].As<Function>());
  442. uv_work_t* req = new uv_work_t;
  443. req->data = closure;
  444. // Make sure the surface exists since we won't have an isolate context in the async block:
  445. canvas->surface();
  446. uv_queue_work(uv_default_loop(), req, ToJpegBufferAsync, (uv_after_work_cb)ToBufferAsyncAfter);
  447. return;
  448. }
  449. #endif
  450. }
  451. /*
  452. * Canvas::StreamPNG callback.
  453. */
  454. static cairo_status_t
  455. streamPNG(void *c, const uint8_t *data, unsigned len) {
  456. Nan::HandleScope scope;
  457. Nan::AsyncResource async("canvas:StreamPNG");
  458. PngClosure* closure = (PngClosure*) c;
  459. Local<Object> buf = Nan::CopyBuffer((char *)data, len).ToLocalChecked();
  460. Local<Value> argv[3] = {
  461. Nan::Null()
  462. , buf
  463. , Nan::New<Number>(len) };
  464. closure->cb.Call(sizeof argv / sizeof *argv, argv, &async);
  465. return CAIRO_STATUS_SUCCESS;
  466. }
  467. /*
  468. * Stream PNG data synchronously. TODO async
  469. * StreamPngSync(this, options: {palette?: Uint8ClampedArray, backgroundIndex?: uint32, compressionLevel: uint32, filters: uint32})
  470. */
  471. NAN_METHOD(Canvas::StreamPNGSync) {
  472. if (!info[0]->IsFunction())
  473. return Nan::ThrowTypeError("callback function required");
  474. Canvas *canvas = Nan::ObjectWrap::Unwrap<Canvas>(info.This());
  475. PngClosure closure(canvas);
  476. parsePNGArgs(info[1], closure);
  477. closure.cb.Reset(Local<Function>::Cast(info[0]));
  478. Nan::TryCatch try_catch;
  479. cairo_status_t status = canvas_write_to_png_stream(canvas->surface(), streamPNG, &closure);
  480. if (try_catch.HasCaught()) {
  481. try_catch.ReThrow();
  482. return;
  483. } else if (status) {
  484. Local<Value> argv[1] = { Canvas::Error(status) };
  485. Nan::Call(closure.cb, Nan::GetCurrentContext()->Global(), sizeof argv / sizeof *argv, argv);
  486. } else {
  487. Local<Value> argv[3] = {
  488. Nan::Null()
  489. , Nan::Null()
  490. , Nan::New<Uint32>(0) };
  491. Nan::Call(closure.cb, Nan::GetCurrentContext()->Global(), sizeof argv / sizeof *argv, argv);
  492. }
  493. return;
  494. }
  495. struct PdfStreamInfo {
  496. Local<Function> fn;
  497. uint32_t len;
  498. uint8_t* data;
  499. };
  500. /*
  501. * Canvas::StreamPDF FreeCallback
  502. */
  503. void stream_pdf_free(char *, void *) {}
  504. /*
  505. * Canvas::StreamPDF callback.
  506. */
  507. static cairo_status_t
  508. streamPDF(void *c, const uint8_t *data, unsigned len) {
  509. Nan::HandleScope scope;
  510. Nan::AsyncResource async("canvas:StreamPDF");
  511. PdfStreamInfo* streaminfo = static_cast<PdfStreamInfo*>(c);
  512. // TODO this is technically wrong, we're returning a pointer to the data in a
  513. // vector in a class with automatic storage duration. If the canvas goes out
  514. // of scope while we're in the handler, a use-after-free could happen.
  515. Local<Object> buf = Nan::NewBuffer(const_cast<char *>(reinterpret_cast<const char *>(data)), len, stream_pdf_free, 0).ToLocalChecked();
  516. Local<Value> argv[3] = {
  517. Nan::Null()
  518. , buf
  519. , Nan::New<Number>(len) };
  520. async.runInAsyncScope(Nan::GetCurrentContext()->Global(), streaminfo->fn, sizeof argv / sizeof *argv, argv);
  521. return CAIRO_STATUS_SUCCESS;
  522. }
  523. cairo_status_t canvas_write_to_pdf_stream(cairo_surface_t *surface, cairo_write_func_t write_func, PdfStreamInfo* streaminfo) {
  524. size_t whole_chunks = streaminfo->len / PAGE_SIZE;
  525. size_t remainder = streaminfo->len - whole_chunks * PAGE_SIZE;
  526. for (size_t i = 0; i < whole_chunks; ++i) {
  527. write_func(streaminfo, &streaminfo->data[i * PAGE_SIZE], PAGE_SIZE);
  528. }
  529. if (remainder) {
  530. write_func(streaminfo, &streaminfo->data[whole_chunks * PAGE_SIZE], remainder);
  531. }
  532. return CAIRO_STATUS_SUCCESS;
  533. }
  534. /*
  535. * Stream PDF data synchronously.
  536. */
  537. NAN_METHOD(Canvas::StreamPDFSync) {
  538. if (!info[0]->IsFunction())
  539. return Nan::ThrowTypeError("callback function required");
  540. Canvas *canvas = Nan::ObjectWrap::Unwrap<Canvas>(info.Holder());
  541. if (canvas->backend()->getName() != "pdf")
  542. return Nan::ThrowTypeError("wrong canvas type");
  543. #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 16, 0)
  544. if (info[1]->IsObject()) {
  545. setPdfMetadata(canvas, Nan::To<Object>(info[1]).ToLocalChecked());
  546. }
  547. #endif
  548. cairo_surface_finish(canvas->surface());
  549. PdfSvgClosure* closure = static_cast<PdfBackend*>(canvas->backend())->closure();
  550. Local<Function> fn = info[0].As<Function>();
  551. PdfStreamInfo streaminfo;
  552. streaminfo.fn = fn;
  553. streaminfo.data = &closure->vec[0];
  554. streaminfo.len = closure->vec.size();
  555. Nan::TryCatch try_catch;
  556. cairo_status_t status = canvas_write_to_pdf_stream(canvas->surface(), streamPDF, &streaminfo);
  557. if (try_catch.HasCaught()) {
  558. try_catch.ReThrow();
  559. } else if (status) {
  560. Local<Value> error = Canvas::Error(status);
  561. Nan::Call(fn, Nan::GetCurrentContext()->Global(), 1, &error);
  562. } else {
  563. Local<Value> argv[3] = {
  564. Nan::Null()
  565. , Nan::Null()
  566. , Nan::New<Uint32>(0) };
  567. Nan::Call(fn, Nan::GetCurrentContext()->Global(), sizeof argv / sizeof *argv, argv);
  568. }
  569. }
  570. /*
  571. * Stream JPEG data synchronously.
  572. */
  573. #ifdef HAVE_JPEG
  574. static uint32_t getSafeBufSize(Canvas* canvas) {
  575. // Don't allow the buffer size to exceed the size of the canvas (#674)
  576. // TODO not sure if this is really correct, but it fixed #674
  577. return (std::min)(canvas->getWidth() * canvas->getHeight() * 4, static_cast<int>(PAGE_SIZE));
  578. }
  579. NAN_METHOD(Canvas::StreamJPEGSync) {
  580. if (!info[1]->IsFunction())
  581. return Nan::ThrowTypeError("callback function required");
  582. Canvas *canvas = Nan::ObjectWrap::Unwrap<Canvas>(info.This());
  583. JpegClosure closure(canvas);
  584. parseJPEGArgs(info[0], closure);
  585. closure.cb.Reset(Local<Function>::Cast(info[1]));
  586. Nan::TryCatch try_catch;
  587. uint32_t bufsize = getSafeBufSize(canvas);
  588. write_to_jpeg_stream(canvas->surface(), bufsize, &closure);
  589. if (try_catch.HasCaught()) {
  590. try_catch.ReThrow();
  591. }
  592. return;
  593. }
  594. #endif
  595. char *
  596. str_value(Local<Value> val, const char *fallback, bool can_be_number) {
  597. if (val->IsString() || (can_be_number && val->IsNumber())) {
  598. return strdup(*Nan::Utf8String(val));
  599. } else if (fallback) {
  600. return strdup(fallback);
  601. } else {
  602. return NULL;
  603. }
  604. }
  605. NAN_METHOD(Canvas::RegisterFont) {
  606. if (!info[0]->IsString()) {
  607. return Nan::ThrowError("Wrong argument type");
  608. } else if (!info[1]->IsObject()) {
  609. return Nan::ThrowError(GENERIC_FACE_ERROR);
  610. }
  611. Nan::Utf8String filePath(info[0]);
  612. PangoFontDescription *sys_desc = get_pango_font_description((unsigned char *) *filePath);
  613. if (!sys_desc) return Nan::ThrowError("Could not parse font file");
  614. PangoFontDescription *user_desc = pango_font_description_new();
  615. // now check the attrs, there are many ways to be wrong
  616. Local<Object> js_user_desc = Nan::To<Object>(info[1]).ToLocalChecked();
  617. Local<String> family_prop = Nan::New<String>("family").ToLocalChecked();
  618. Local<String> weight_prop = Nan::New<String>("weight").ToLocalChecked();
  619. Local<String> style_prop = Nan::New<String>("style").ToLocalChecked();
  620. char *family = str_value(Nan::Get(js_user_desc, family_prop).ToLocalChecked(), NULL, false);
  621. char *weight = str_value(Nan::Get(js_user_desc, weight_prop).ToLocalChecked(), "normal", true);
  622. char *style = str_value(Nan::Get(js_user_desc, style_prop).ToLocalChecked(), "normal", false);
  623. if (family && weight && style) {
  624. pango_font_description_set_weight(user_desc, Canvas::GetWeightFromCSSString(weight));
  625. pango_font_description_set_style(user_desc, Canvas::GetStyleFromCSSString(style));
  626. pango_font_description_set_family(user_desc, family);
  627. auto found = std::find_if(font_face_list.begin(), font_face_list.end(), [&](FontFace& f) {
  628. return pango_font_description_equal(f.sys_desc, sys_desc);
  629. });
  630. if (found != font_face_list.end()) {
  631. pango_font_description_free(found->user_desc);
  632. found->user_desc = user_desc;
  633. } else if (register_font((unsigned char *) *filePath)) {
  634. FontFace face;
  635. face.user_desc = user_desc;
  636. face.sys_desc = sys_desc;
  637. strncpy((char *)face.file_path, (char *) *filePath, 1023);
  638. font_face_list.push_back(face);
  639. } else {
  640. pango_font_description_free(user_desc);
  641. Nan::ThrowError("Could not load font to the system's font host");
  642. }
  643. } else {
  644. pango_font_description_free(user_desc);
  645. Nan::ThrowError(GENERIC_FACE_ERROR);
  646. }
  647. free(family);
  648. free(weight);
  649. free(style);
  650. }
  651. NAN_METHOD(Canvas::DeregisterAllFonts) {
  652. // Unload all fonts from pango to free up memory
  653. bool success = true;
  654. std::for_each(font_face_list.begin(), font_face_list.end(), [&](FontFace& f) {
  655. if (!deregister_font( (unsigned char *)f.file_path )) success = false;
  656. pango_font_description_free(f.user_desc);
  657. pango_font_description_free(f.sys_desc);
  658. });
  659. font_face_list.clear();
  660. if (!success) Nan::ThrowError("Could not deregister one or more fonts");
  661. }
  662. /*
  663. * Initialize cairo surface.
  664. */
  665. Canvas::Canvas(Backend* backend) : ObjectWrap() {
  666. _backend = backend;
  667. }
  668. /*
  669. * Destroy cairo surface.
  670. */
  671. Canvas::~Canvas() {
  672. if (_backend != NULL) {
  673. delete _backend;
  674. }
  675. }
  676. /*
  677. * Get a PangoStyle from a CSS string (like "italic")
  678. */
  679. PangoStyle
  680. Canvas::GetStyleFromCSSString(const char *style) {
  681. PangoStyle s = PANGO_STYLE_NORMAL;
  682. if (strlen(style) > 0) {
  683. if (0 == strcmp("italic", style)) {
  684. s = PANGO_STYLE_ITALIC;
  685. } else if (0 == strcmp("oblique", style)) {
  686. s = PANGO_STYLE_OBLIQUE;
  687. }
  688. }
  689. return s;
  690. }
  691. /*
  692. * Get a PangoWeight from a CSS string ("bold", "100", etc)
  693. */
  694. PangoWeight
  695. Canvas::GetWeightFromCSSString(const char *weight) {
  696. PangoWeight w = PANGO_WEIGHT_NORMAL;
  697. if (strlen(weight) > 0) {
  698. if (0 == strcmp("bold", weight)) {
  699. w = PANGO_WEIGHT_BOLD;
  700. } else if (0 == strcmp("100", weight)) {
  701. w = PANGO_WEIGHT_THIN;
  702. } else if (0 == strcmp("200", weight)) {
  703. w = PANGO_WEIGHT_ULTRALIGHT;
  704. } else if (0 == strcmp("300", weight)) {
  705. w = PANGO_WEIGHT_LIGHT;
  706. } else if (0 == strcmp("400", weight)) {
  707. w = PANGO_WEIGHT_NORMAL;
  708. } else if (0 == strcmp("500", weight)) {
  709. w = PANGO_WEIGHT_MEDIUM;
  710. } else if (0 == strcmp("600", weight)) {
  711. w = PANGO_WEIGHT_SEMIBOLD;
  712. } else if (0 == strcmp("700", weight)) {
  713. w = PANGO_WEIGHT_BOLD;
  714. } else if (0 == strcmp("800", weight)) {
  715. w = PANGO_WEIGHT_ULTRABOLD;
  716. } else if (0 == strcmp("900", weight)) {
  717. w = PANGO_WEIGHT_HEAVY;
  718. }
  719. }
  720. return w;
  721. }
  722. /*
  723. * Given a user description, return a description that will select the
  724. * font either from the system or @font-face
  725. */
  726. PangoFontDescription *
  727. Canvas::ResolveFontDescription(const PangoFontDescription *desc) {
  728. // One of the user-specified families could map to multiple SFNT family names
  729. // if someone registered two different fonts under the same family name.
  730. // https://drafts.csswg.org/css-fonts-3/#font-style-matching
  731. FontFace best;
  732. istringstream families(pango_font_description_get_family(desc));
  733. unordered_set<string> seen_families;
  734. string resolved_families;
  735. bool first = true;
  736. for (string family; getline(families, family, ','); ) {
  737. string renamed_families;
  738. for (auto& ff : font_face_list) {
  739. string pangofamily = string(pango_font_description_get_family(ff.user_desc));
  740. if (streq_casein(family, pangofamily)) {
  741. const char* sys_desc_family_name = pango_font_description_get_family(ff.sys_desc);
  742. bool unseen = seen_families.find(sys_desc_family_name) == seen_families.end();
  743. bool better = best.user_desc == nullptr || pango_font_description_better_match(desc, best.user_desc, ff.user_desc);
  744. // Avoid sending duplicate SFNT font names due to a bug in Pango for macOS:
  745. // https://bugzilla.gnome.org/show_bug.cgi?id=762873
  746. if (unseen) {
  747. seen_families.insert(sys_desc_family_name);
  748. if (better) {
  749. renamed_families = string(sys_desc_family_name) + (renamed_families.size() ? "," : "") + renamed_families;
  750. } else {
  751. renamed_families = renamed_families + (renamed_families.size() ? "," : "") + sys_desc_family_name;
  752. }
  753. }
  754. if (first && better) best = ff;
  755. }
  756. }
  757. if (resolved_families.size()) resolved_families += ',';
  758. resolved_families += renamed_families.size() ? renamed_families : family;
  759. first = false;
  760. }
  761. PangoFontDescription* ret = pango_font_description_copy(best.sys_desc ? best.sys_desc : desc);
  762. pango_font_description_set_family(ret, resolved_families.c_str());
  763. return ret;
  764. }
  765. /*
  766. * Re-alloc the surface, destroying the previous.
  767. */
  768. void
  769. Canvas::resurface(Local<Object> canvas) {
  770. Nan::HandleScope scope;
  771. Local<Value> context;
  772. backend()->recreateSurface();
  773. // Reset context
  774. context = Nan::Get(canvas, Nan::New<String>("context").ToLocalChecked()).ToLocalChecked();
  775. if (!context->IsUndefined()) {
  776. Context2d *context2d = ObjectWrap::Unwrap<Context2d>(Nan::To<Object>(context).ToLocalChecked());
  777. cairo_t *prev = context2d->context();
  778. context2d->setContext(createCairoContext());
  779. context2d->resetState();
  780. cairo_destroy(prev);
  781. }
  782. }
  783. /**
  784. * Wrapper around cairo_create()
  785. * (do not call cairo_create directly, call this instead)
  786. */
  787. cairo_t*
  788. Canvas::createCairoContext() {
  789. cairo_t* ret = cairo_create(surface());
  790. cairo_set_line_width(ret, 1); // Cairo defaults to 2
  791. return ret;
  792. }
  793. /*
  794. * Construct an Error from the given cairo status.
  795. */
  796. Local<Value>
  797. Canvas::Error(cairo_status_t status) {
  798. return Exception::Error(Nan::New<String>(cairo_status_to_string(status)).ToLocalChecked());
  799. }
  800. #undef CHECK_RECEIVER