BitmapExportWorker.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. /**
  2. * Copyright (c) 2011-2018 by Andrew Mustun. All rights reserved.
  3. *
  4. * This file is part of the QCAD project.
  5. *
  6. * QCAD is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * QCAD is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with QCAD.
  18. */
  19. /**
  20. * Exports the given RDocument (doc) to a bitmap (BMP, PNG, JPEG, TIFF, ...).
  21. * \param doc RDocument document
  22. * \param scene Graphics scene to export (e.g. RGraphicsSceneQt)
  23. * \param fileName File name for exported bitmap. Extension determines format.
  24. * \param properties Various properties:
  25. *
  26. * - properties["width"]: width of bitmap in pixels (ignored if resolution is present)
  27. * - properties["height"]: height of bitmap in pixels (ignored if resolution is present)
  28. * - properties["resolution"]: resolution in pixels / drawing unit
  29. * - properties["margin"]: margin at borders in pixels
  30. * - properties["noWeightMargin"]: true to disable margin for lineweight, defaults to false
  31. * - properties["backgroundColor"]: background color (RColor)
  32. * - properties["origin"]: true: export origin point as red cross
  33. * - properties["antialiasing"]: true: use antialiasing
  34. * - properties["quality"]: Export quality (0..100), JPEG only
  35. * - properties["monochrome"]: true: Export as black / white
  36. * - properties["grayscale"]: true: Export as grayscale
  37. * - properties["window"]: RBox: window to export in drawing coordinates
  38. * - properties["entityIds"]: Array: zoom to bounding box of given entities
  39. * - properties["initView"]: Callback to initialize view
  40. * - properties["zoomAll"]: Auto zoom to all entities, including those on invisible layers
  41. * - properties["metaData"]: Key / value pairs of meta data to write to image header
  42. *
  43. * \param view Optional graphics view to use.
  44. */
  45. function exportBitmap(doc, scene, fileName, properties, view) {
  46. var ret;
  47. var viewCreated = false;
  48. if (typeof(view)==="undefined") {
  49. view = new RGraphicsViewImage();
  50. view.setScene(scene, false);
  51. viewCreated = true;
  52. }
  53. var numThreadsOri = view.getNumThreads();
  54. var antialiasing = (properties["antialiasing"]==null ? true : properties["antialiasing"]);
  55. // crashes with multiple threads:
  56. view.setNumThreads(1);
  57. view.setAlphaEnabled(true);
  58. view.setPaintOrigin(properties["origin"]==null ? false : properties["origin"]);
  59. view.setTextHeightThresholdOverride(0);
  60. view.setAntialiasing(antialiasing);
  61. if (properties["monochrome"]===true) {
  62. view.setColorMode(RGraphicsView.BlackWhite);
  63. }
  64. else if (properties["grayscale"]===true) {
  65. view.setColorMode(RGraphicsView.GrayScale);
  66. }
  67. if (properties["backgroundColor"]) {
  68. view.setBackgroundColor(properties["backgroundColor"]);
  69. }
  70. if (typeof(properties["colorCorrection"])!=="undefined") {
  71. view.setColorCorrectionOverride(properties["colorCorrection"]);
  72. }
  73. if (typeof(properties["margin"])==="undefined") {
  74. properties["margin"] = 20;
  75. }
  76. if (typeof(properties["noWeightMargin"])==="undefined") {
  77. properties["noWeightMargin"] = false;
  78. }
  79. if (typeof(properties["zoomAll"])==="undefined") {
  80. properties["zoomAll"] = false;
  81. }
  82. if (properties["resolution"]) {
  83. var bb;
  84. if (typeof(properties["entityIds"])!=="undefined") {
  85. bb = doc.getEntitiesBox(properties["entityIds"]);
  86. }
  87. else {
  88. bb = doc.getBoundingBox(true, true);
  89. }
  90. if (properties["noWeightMargin"]===false) {
  91. // grow bounding box by max line weight:
  92. var wm = RUnit.convert(doc.getMaxLineweight()/100.0/2, RS.Millimeter, doc.getUnit());
  93. bb.growXY(wm);
  94. }
  95. properties["width"] = Math.ceil(bb.getWidth() * properties["resolution"] + 2 * properties["margin"]);
  96. properties["height"] = Math.ceil(bb.getHeight() * properties["resolution"] + 2 * properties["margin"]);
  97. }
  98. if (!properties["width"]) {
  99. properties["width"] = 640;
  100. }
  101. if (!properties["height"]) {
  102. properties["height"] = 480;
  103. }
  104. if (properties["width"] * properties["height"] > 2147483647/4) {
  105. qDebug("invalid image size");
  106. ret = [ false, qsTr("Invalid image size (width x height must be less than %1)").arg(536870911) ];
  107. view.setNumThreads(numThreadsOri);
  108. return ret;
  109. }
  110. view.resizeImage(properties["width"], properties["height"]);
  111. if (properties["window"]) {
  112. view.zoomTo(properties["window"], properties["margin"]);
  113. }
  114. else if (properties["zoomAll"]) {
  115. var bbz = doc.getBoundingBox(false, true);
  116. view.zoomTo(bbz, properties["margin"]);
  117. }
  118. else if (typeof(properties["entityIds"])!=="undefined") {
  119. view.zoomToEntities(properties["entityIds"], properties["margin"]);
  120. }
  121. else {
  122. view.autoZoom(properties["margin"], true, properties["noWeightMargin"]);
  123. }
  124. // make sure we use the desired resolution:
  125. // auto zoom might be slightly off, due to rounding canvas to pixels:
  126. if (properties["resolution"]) {
  127. view.setFactor(properties["resolution"]);
  128. }
  129. view.clear();
  130. view.setExporting(true);
  131. view.setHairlineMinimumMode(true);
  132. if (properties["regen"]!==false) {
  133. scene.regenerate();
  134. view.repaintView();
  135. }
  136. if (isFunction(properties["initView"])) {
  137. properties["initView"](view);
  138. }
  139. // export file
  140. var buffer = view.getBuffer();
  141. if (properties["monochrome"]===true && !antialiasing) {
  142. buffer = buffer.convertToFormat(QImage.Format_Mono);
  143. }
  144. view.setNumThreads(numThreadsOri);
  145. if (viewCreated) {
  146. scene.unregisterView(view);
  147. destr(view);
  148. }
  149. var iw = new QImageWriter(fileName);
  150. var ext = new QFileInfo(fileName).suffix().toLowerCase();
  151. if (ext === "png") {
  152. iw.setQuality(9);
  153. } else if (ext === "jpg" || ext === "jpeg") {
  154. iw.setQuality(properties["quality"]==null ? 100 : properties["quality"]);
  155. } else if (ext === "tif" || ext === "tiff") {
  156. iw.setCompression(1);
  157. } else if (ext === "bmp") {
  158. iw.setCompression(1);
  159. }
  160. if (isArray(properties["metaData"])) {
  161. for (var i=0; i<properties["metaData"].length; i++) {
  162. // meta data:
  163. iw.setText(properties["metaData"][0], properties["metaData"][1]);
  164. }
  165. }
  166. if (!iw.write(buffer)) {
  167. ret = [false, iw.errorString()];
  168. } else {
  169. ret = [true, ""];
  170. }
  171. destr(iw);
  172. return ret;
  173. }