api.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. import gm from 'gm';
  2. import axios from 'axios';
  3. import sizeOf from 'image-size';
  4. import path from 'node:path';
  5. import fs from 'node:fs';
  6. import PDFDocument from 'pdfkit';
  7. import log from 'electron-log/renderer';
  8. import { getImagicPath, getTempPath } from './utils';
  9. import {
  10. DrawTrackItem,
  11. DrawTrackCircleOption,
  12. DrawTrackLineOption,
  13. DrawTrackTextOption,
  14. } from './types';
  15. // macos install gm imagemagick https://github.com/aheckmann/gm/blob/master/README.md
  16. const gmInst =
  17. process.platform === 'win32'
  18. ? gm.subClass({
  19. imageMagick: '7+',
  20. appPath: getImagicPath(),
  21. })
  22. : gm.subClass({ imageMagick: '7+' });
  23. function cropImage(imgPath: string): Promise<string> {
  24. return new Promise((resolve, reject) => {
  25. const outpath = path.join(getTempPath(), '001.png');
  26. gmInst(imgPath)
  27. .crop(500, 200, 0, 0)
  28. .write(outpath, (err) => {
  29. if (!err) {
  30. return resolve(outpath);
  31. }
  32. return reject(err);
  33. });
  34. });
  35. }
  36. function drawTrack(
  37. imgPath: string,
  38. drawTrackList: DrawTrackItem[],
  39. outpath: string
  40. ): Promise<string> {
  41. return new Promise((resolve, reject) => {
  42. const gmObj = gmInst(imgPath);
  43. const defaultColor = '#f53f3f';
  44. const defaultFontSize = 14;
  45. drawTrackList.forEach((track) => {
  46. // text
  47. if (track.type === 'text') {
  48. const { x, y, text, color, fontSize } =
  49. track.option as DrawTrackTextOption;
  50. gmObj
  51. .fill(color || defaultColor)
  52. .fontSize(fontSize || defaultFontSize)
  53. .drawText(x, y, text);
  54. return;
  55. }
  56. // circle
  57. if (track.type === 'circle') {
  58. const { x0, y0, x1, y1 } = track.option as DrawTrackCircleOption;
  59. gmObj.drawCircle(x0, y0, x1, y1).stroke(defaultColor, 2);
  60. return;
  61. }
  62. // line
  63. if (track.type === 'line') {
  64. const { x0, y0, x1, y1 } = track.option as DrawTrackLineOption;
  65. gmObj.drawLine(x0, y0, x1, y1).stroke(defaultColor, 2);
  66. }
  67. });
  68. gmObj.write(outpath, (err) => {
  69. if (!err) {
  70. return resolve(outpath);
  71. }
  72. return reject(err);
  73. });
  74. });
  75. }
  76. async function downloadFile(url: string, outputPath: string) {
  77. const writer = fs.createWriteStream(outputPath);
  78. const response = await axios({
  79. url,
  80. method: 'GET',
  81. responseType: 'stream',
  82. });
  83. response.data.pipe(writer);
  84. return new Promise((resolve, reject) => {
  85. writer.on('finish', resolve);
  86. writer.on('error', reject);
  87. });
  88. }
  89. async function downloadImage(url: string, outputPath: string) {
  90. await downloadFile(url, outputPath);
  91. const size = sizeOf(outputPath);
  92. return {
  93. url: outputPath,
  94. width: size.width || 100,
  95. height: size.height || 100,
  96. };
  97. }
  98. function joinPath(paths: string[]) {
  99. return path.join(...paths);
  100. }
  101. interface ImageItem {
  102. url: string;
  103. width: number;
  104. height: number;
  105. }
  106. async function imagesToPdf(
  107. images: ImageItem[],
  108. outpath: string
  109. ): Promise<string> {
  110. return new Promise((resolve, reject) => {
  111. const doc = new PDFDocument();
  112. const steam = fs.createWriteStream(outpath);
  113. doc.pipe(steam);
  114. images.forEach((image) => {
  115. const { url, width, height } = image;
  116. doc.addPage({ size: [width, height] });
  117. doc.image(url, 0, 0, { width, height });
  118. });
  119. doc.end();
  120. steam.on('finish', () => {
  121. resolve(outpath);
  122. });
  123. steam.on('error', (err) => {
  124. reject(err);
  125. });
  126. });
  127. }
  128. function logger(content: string, type?: 'info' | 'error') {
  129. if (type === 'error') {
  130. log.error(content);
  131. } else {
  132. log.info(content);
  133. }
  134. }
  135. const commonApi = {
  136. cropImage,
  137. drawTrack,
  138. joinPath,
  139. downloadImage,
  140. imagesToPdf,
  141. logger,
  142. };
  143. export type CommonApi = typeof commonApi;
  144. export default commonApi;