Skip to content
This repository was archived by the owner on Jan 23, 2025. It is now read-only.

Commit d11cc84

Browse files
committed
Merge pull request #389 from flytoj2ee/dev
PoC Assembly - TC API - Generate Source Code Image API
2 parents 06d96db + 5c5dbbb commit d11cc84

File tree

18 files changed

+7325
-11
lines changed

18 files changed

+7325
-11
lines changed

actions/sourceCodeImage.js

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
* Copyright (C) 2014 TopCoder Inc., All Rights Reserved.
3+
*
4+
* The api to convert the source code to image.
5+
*
6+
* @version 1.0
7+
* @author TCASSEMBLER
8+
*/
9+
/*jslint node: true, nomen: true, plusplus: true, unparam: true */
10+
"use strict";
11+
var async = require('async');
12+
var _ = require('underscore');
13+
var IllegalArgumentError = require('../errors/IllegalArgumentError');
14+
var highlight = require('highlight.js');
15+
var wkhtmltoimage = require('wkhtmltoimage');
16+
var BadRequestError = require('../errors/BadRequestError');
17+
var exec = require('child_process').exec;
18+
19+
// The style name array.
20+
var STYLE_NAMES = ['arta', 'ascetic', 'atelier-dune.dark', 'atelier-dune.light', 'atelier-forest.dark', 'atelier-forest.light',
21+
'atelier-heath.dark', 'atelier-heath.light', 'atelier-lakeside.dark', 'atelier-lakeside.light', 'atelier-seaside.dark',
22+
'atelier-seaside.light', 'brown_paper', 'codepen-embed', 'color-brewer', 'dark', 'default', 'docco',
23+
'far', 'foundation', 'github', 'googlecode', 'hybrid', 'idea', 'ir_black', 'kimbie.dark', 'kimbie.light', 'magula',
24+
'mono-blue', 'monokai', 'monokai_sublime', 'obsidian', 'paraiso.dark', 'paraiso.light', 'pojoaque', 'railscasts',
25+
'rainbow', 'school_book', 'solarized_dark', 'solarized_light', 'sunburst', 'tomorrow-night-blue',
26+
'tomorrow-night-bright', 'tomorrow-night-eighties', 'tomorrow-night', 'tomorrow', 'vs', 'xcode', 'zenburn'];
27+
28+
/**
29+
* Convert the source code to image.
30+
*
31+
* @param api - the api instance
32+
* @param connection - the request connection instance
33+
* @param next - the callback method.
34+
*/
35+
var convertSourceCodeToImage = function (api, connection, next) {
36+
var helper = api.helper,
37+
highlightResult = '',
38+
emptyStr = '',
39+
code = connection.params.code + emptyStr,
40+
style = connection.params.style,
41+
language = connection.params.lang;
42+
43+
async.waterfall([
44+
function (cb) {
45+
if (_.isNull(language) || _.isEmpty(language) || !highlight.getLanguage(language + emptyStr)) {
46+
cb(new IllegalArgumentError("The language name is invalid."));
47+
return;
48+
}
49+
50+
if (!_.isUndefined(style) && !_.isNull(style) && !_.isEmpty(style) && !_.contains(STYLE_NAMES, style + emptyStr)) {
51+
cb(new IllegalArgumentError("The style name is invalid."));
52+
return;
53+
}
54+
55+
exec(api.config.tcConfig.generateSourceCodeImage.wkhtmltoimageCommandPath + ' -H', function(error, stdout, stderr) {
56+
if (stderr !== null && stderr !== '') {
57+
cb(new IllegalArgumentError('The wkhtmltoimageCommandPath in configuration is invalid. The return error is ' + stderr));
58+
return;
59+
}
60+
cb();
61+
});
62+
}, function (cb) {
63+
var styleLink = api.config.tcConfig.generateSourceCodeImage.styleLink;
64+
if (!_.isUndefined(style) && !_.isNull(language) && !_.isEmpty(language)) {
65+
styleLink = styleLink.replace('%OVERRIDE_STYLE_NAME%', style);
66+
} else {
67+
styleLink = styleLink.replace('%OVERRIDE_STYLE_NAME%', 'default');
68+
}
69+
highlight.configure({ 'useBR': true });
70+
highlightResult = highlight.highlight(language, code, true).value;
71+
highlightResult = '<html><head><link rel="stylesheet" href="' + styleLink + '"></head><body><pre>' + highlightResult;
72+
highlightResult = highlightResult + '</pre></body></html>';
73+
cb();
74+
}, function (cb) {
75+
var response = connection.rawConnection.res,
76+
tempFileName = new Date().getTime() + (Math.floor(Math.random() * 1000) + '.jpg');
77+
78+
response.writeHead(200, {
79+
'Content-Type': 'image/jpeg',
80+
'Content-Disposition': 'inline; filename=' + tempFileName
81+
});
82+
83+
wkhtmltoimage.setCommand(api.config.tcConfig.generateSourceCodeImage.wkhtmltoimageCommandPath);
84+
wkhtmltoimage.generate(highlightResult, api.config.tcConfig.generateSourceCodeImage.wkhtmlToImageOptions, function (code, signal) {
85+
if (code !== null && code === 0) {
86+
// all success
87+
cb();
88+
} else {
89+
cb(new BadRequestError("Failed to generate the image, the return code is " + code));
90+
}
91+
92+
}).pipe(response);
93+
}
94+
], function (err) {
95+
if (err) {
96+
helper.handleError(api, connection, err);
97+
next(connection, true);
98+
} else {
99+
next(connection, false); //false = response has been set
100+
}
101+
});
102+
};
103+
104+
/**
105+
* The API for converted source code to image.
106+
*/
107+
exports.convertSourceCodeToImage = {
108+
name: "convertSourceCodeToImage",
109+
description: "Convert source code to image",
110+
inputs: {
111+
required: ['code', 'lang'],
112+
optional: ['style']
113+
},
114+
blockedConnectionTypes: [],
115+
cacheEnabled: false,
116+
outputExample: {},
117+
version: 'v2',
118+
run: function (api, connection, next) {
119+
api.log("Execute convertSourceCodeToImage#run", 'debug');
120+
convertSourceCodeToImage(api, connection, next);
121+
}
122+
};

apiary.apib

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14635,3 +14635,65 @@ Managing Round Question Answers APIs
1463514635
"description":"Servers are up but overloaded. Try again later."
1463614636
}
1463714637

14638+
14639+
# Group Source Code Image Generation APIs
14640+
Source Code Image Generation APIs
14641+
14642+
## Source Code Image Generation API [/src2image]
14643+
14644+
### Source Code Image Generation API [POST]
14645+
14646+
+ Parameters
14647+
+ code (required, string) ... the code value to convert
14648+
+ lang (required, string) ... the code's language name, it should be one of ["1c","actionscript","apache","applescript","xml","asciidoc","autohotkey","avrasm","axapta","bash","brainfuck","capnproto","clojure","cmake","coffeescript","cpp","cs","css","d","markdown","dart","delphi","diff","django","dos","dust","elixir","ruby","erb","erlang-repl","erlang","fix","fsharp","gcode","gherkin","glsl","go","gradle","groovy","haml","handlebars","haskell","haxe","http","ini","java","javascript","json","lasso","less","lisp","livecodeserver","livescript","lua","makefile","mathematica","matlab","mel","mizar","monkey","nginx","nimrod","nix","nsis","objectivec","ocaml","oxygene","parser3","perl","php","powershell","processing","profile","protobuf","puppet","python","q","r","rib","rsl","ruleslanguage","rust","scala","scheme","scilab","scss","smalltalk","sql","stylus","swift","tcl","tex","thrift","twig","typescript","vala","vbnet","vbscript","vbscript-html","vhdl","vim","x86asm","xl"]
14649+
+ style (optional, string) ... the code's format style, it should be one of ['arta', 'ascetic', 'atelier-dune.dark', 'atelier-dune.light', 'atelier-forest.dark', 'atelier-forest.light', 'atelier-heath.dark', 'atelier-heath.light', 'atelier-lakeside.dark', 'atelier-lakeside.light', 'atelier-seaside.dark', 'atelier-seaside.light', 'brown_paper', 'codepen-embed', 'color-brewer', 'dark', 'default', 'docco', 'far', 'foundation', 'github', 'googlecode', 'hybrid', 'idea', 'ir_black', 'kimbie.dark', 'kimbie.light', 'magula', 'mono-blue', 'monokai', 'monokai_sublime', 'obsidian', 'paraiso.dark', 'paraiso.light', 'pojoaque', 'railscasts', 'rainbow', 'school_book', 'solarized_dark', 'solarized_light', 'sunburst', 'tomorrow-night-blue', 'tomorrow-night-bright', 'tomorrow-night-eighties', 'tomorrow-night', 'tomorrow', 'vs', 'xcode', 'zenburn']
14650+
14651+
+ Response 200 (image/jpeg)
14652+
14653+
{
14654+
// it contains the generation jpeg in response.
14655+
}
14656+
14657+
+ Response 200 (application/json)
14658+
14659+
{
14660+
"error": "Error: code is a required parameter for this action"
14661+
}
14662+
14663+
+ Response 200 (application/json)
14664+
14665+
{
14666+
"error": "Error: lang is a required parameter for this action"
14667+
}
14668+
14669+
+ Response 400 (application/json)
14670+
14671+
{
14672+
"name":"Bad Request",
14673+
"value":"400",
14674+
"description":"The language name is invalid."
14675+
}
14676+
14677+
+ Response 400 (application/json)
14678+
14679+
{
14680+
"name":"Bad Request",
14681+
"value":"400",
14682+
"description":"The style name is invalid."
14683+
}
14684+
14685+
+ Response 500 (application/json)
14686+
14687+
{
14688+
"name":"Internal Server Error",
14689+
"value":"500",
14690+
"description":"Unknown server error. Please contact support."
14691+
}
14692+
14693+
+ Response 503 (application/json)
14694+
14695+
{
14696+
"name":"Service Unavailable",
14697+
"value":"503",
14698+
"description":"Servers are up but overloaded. Try again later."
14699+
}

config/tc-config.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* Copyright (C) 2013 - 2014 TopCoder Inc., All Rights Reserved.
33
*
44
* @author vangavroche, Ghost_141, kurtrips, Sky_, isv, bugbuka, flytoj2ee, TCSASSEMBLER
5-
* @version 1.27
5+
* @version 1.28
66
* changes in 1.1:
77
* - add defaultCacheLifetime parameter
88
* changes in 1.2:
@@ -64,6 +64,8 @@
6464
* - Add studioReview object for get studio review opportunities api.
6565
* Changes in 1.27:
6666
* Add userActivationResendLimit and userActivationCacheLifeTime for user activation email api.
67+
* Changes in 1.28:
68+
* Add source code image generation configuration.
6769
*/
6870

6971
"use strict";
@@ -224,6 +226,16 @@ var config = {
224226
studioReview: {
225227
specTerms: 'http://studio.topcoder.com/?module=SpecViewReviewTerms&ct=',
226228
reviewTerms: 'http://studio.topcoder.com/?module=ViewReviewTerms&ct='
229+
},
230+
231+
generateSourceCodeImage: {
232+
wkhtmltoimageCommandPath: process.env.WKHTMLTOIMAGE_COMMAND_PATH || 'wkhtmltoimage',
233+
styleLink: process.env.HIGHLIGHT_STYLE_LINK || 'http://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.3/styles/%OVERRIDE_STYLE_NAME%.min.css',
234+
wkhtmlToImageOptions: {
235+
Format: 'jpg',
236+
Quality: 94,
237+
width: process.env.WKHTMLTOIMAGE_IMAGE_WIDTH || 1024
238+
}
227239
}
228240
};
229241
module.exports.tcConfig = config;

deploy/ci.sh

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,18 @@
33
#
44
# Copyright (C) 2013-2014 TopCoder Inc., All Rights Reserved.
55
#
6-
# Version: 1.1
7-
# Author: vangavroche, delemach, isv
6+
# Version: 1.2
7+
# Author: vangavroche, delemach, isv, TCASSEMBLER
88
#
99
# changes in 1.1:
1010
# - added RESET_PASSWORD_TOKEN_CACHE_EXPIRY environment variable
1111
# - added RESET_PASSWORD_TOKEN_EMAIL_SUBJECT environment variable
1212
# - added REDIS_HOST environment variable
1313
# - added REDIS_PORT environment variable
14+
# changes in 1.2
15+
# - added WKHTMLTOIMAGE_COMMAND_PATH environment variable
16+
# - added WKHTMLTOIMAGE_IMAGE_WIDTH environment variable
17+
# - added HIGHLIGHT_STYLE_LINK environment variable
1418
#
1519
export CACHE_EXPIRY=-1
1620

@@ -86,3 +90,7 @@ export REDIS_PORT=6379
8690

8791
export DEVELOP_SUBMISSION_MAX_SIZE=6144
8892
export WATERMARK_FILE_PATH=test/test_files/design_image_file_generator/studio_logo_watermark.png
93+
94+
export WKHTMLTOIMAGE_COMMAND_PATH=/home/ubuntu/tmp/wkhtmltox-0.12.1/static-build/posix-local/wkhtmltox-0.12.1/bin/wkhtmltoimage
95+
export WKHTMLTOIMAGE_IMAGE_WIDTH=1024
96+
export HIGHLIGHT_STYLE_LINK=http://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.3/styles/%OVERRIDE_STYLE_NAME%.min.css

deploy/development.bat

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,18 @@
22
REM
33
REM Copyright (C) 2014 TopCoder Inc., All Rights Reserved.
44
REM
5-
REM Version: 1.1
6-
REM Author: TrePe, isv
5+
REM Version: 1.2
6+
REM Author: TrePe, isv, TCASSEMBLER
77
REM
88
REM Changes in 1.1
99
REM - added RESET_PASSWORD_TOKEN_CACHE_EXPIRY environment variable
1010
REM - added RESET_PASSWORD_TOKEN_EMAIL_SUBJECT environment variable
1111
REM - added REDIS_HOST environment variable
1212
REM - added REDIS_PORT environment variable
13+
REM Changes in 1.2
14+
REM - added WKHTMLTOIMAGE_COMMAND_PATH environment variable
15+
REM - added WKHTMLTOIMAGE_IMAGE_WIDTH environment variable
16+
REM - added HIGHLIGHT_STYLE_LINK environment variable
1317

1418
REM tests rely on caching being off. But set this to a real value (or remove) while coding.
1519

@@ -83,3 +87,7 @@ rem set REDIS_HOST=localhost
8387
rem set REDIS_PORT=6379
8488

8589
set WATERMARK_FILE_PATH=test/test_files/design_image_file_generator/studio_logo_watermark.png
90+
91+
set WKHTMLTOIMAGE_COMMAND_PATH=/home/ubuntu/tmp/wkhtmltox-0.12.1/static-build/posix-local/wkhtmltox-0.12.1/bin/wkhtmltoimage
92+
set WKHTMLTOIMAGE_IMAGE_WIDTH=1024
93+
set HIGHLIGHT_STYLE_LINK=http://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.3/styles/%OVERRIDE_STYLE_NAME%.min.css

deploy/development.sh

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,19 @@
33
#
44
# Copyright (C) 2013-2014 TopCoder Inc., All Rights Reserved.
55
#
6-
# Version: 1.2
7-
# Author: vangavroche, isv
6+
# Version: 1.3
7+
# Author: vangavroche, isv, TCASSEMBLER
88
# changes in 1.1:
99
# - add JIRA_USERNAME and JIRA_PASSWORD
1010
# changes in 1.2:
1111
# - added RESET_PASSWORD_TOKEN_CACHE_EXPIRY environment variable
1212
# - added RESET_PASSWORD_TOKEN_EMAIL_SUBJECT environment variable
1313
# - added REDIS_HOST environment variable
1414
# - added REDIS_PORT environment variable
15+
# changes in 1.3
16+
# - added WKHTMLTOIMAGE_COMMAND_PATH environment variable
17+
# - added WKHTMLTOIMAGE_IMAGE_WIDTH environment variable
18+
# - added HIGHLIGHT_STYLE_LINK environment variable
1519
#
1620

1721
# tests rely on caching being off. But set this to a real value (or remove) while coding.
@@ -88,3 +92,7 @@ export REDIS_PORT=6379
8892
export DEVELOP_SUBMISSION_MAX_SIZE=6144
8993

9094
export WATERMARK_FILE_PATH=test/test_files/design_image_file_generator/studio_logo_watermark.png
95+
96+
export WKHTMLTOIMAGE_COMMAND_PATH=/home/ubuntu/tmp/wkhtmltox-0.12.1/static-build/posix-local/wkhtmltox-0.12.1/bin/wkhtmltoimage
97+
export WKHTMLTOIMAGE_IMAGE_WIDTH=1024
98+
export HIGHLIGHT_STYLE_LINK=http://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.3/styles/%OVERRIDE_STYLE_NAME%.min.css

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@
4343
"archiver": "~0.6.1",
4444
"redis": "0.10.x",
4545
"temp": "0.7.0",
46-
"ssha": "*"
46+
"ssha": "*",
47+
"highlight.js": ">= 8.3.0",
48+
"wkhtmltoimage": ">= 0.1.3"
4749
},
4850
"devDependencies": {
4951
"supertest": "0.8.x",

routes.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
/*
22
* Copyright (C) 2013 - 2014 TopCoder Inc., All Rights Reserved.
33
*
4-
* @version 1.61
4+
* @version 1.62
55
* @author vangavroche, Sky_, muzehyun, kurtrips, Ghost_141, ecnu_haozi, hesibo, LazyChild, isv, flytoj2ee,
6-
* @author panoptimum, bugbuka, Easyhard
6+
* @author panoptimum, bugbuka, Easyhard, TCASSEMBLER
77
*
88
* Changes in 1.1:
99
* - add routes for search challenges
@@ -142,6 +142,8 @@
142142
* - Add route for get user identity api.
143143
* Changes in 1.61:
144144
* - Added routes for modifying/deleting round question answers.
145+
* Changes in 1.62:
146+
* - Added route for src2image api.
145147
*/
146148
/*jslint node:true, nomen: true */
147149
"use strict";
@@ -344,6 +346,7 @@ exports.routes = {
344346
{ path: "/:apiVersion/software/reviewers/:contestType", action: "getChallengeReviewers" },
345347
{ path: "/:apiVersion/design/statistics/tops/:challengeType", action: "getStudioTops" },
346348
{ path: "/:apiVersion/data/challengetypes", action: "algorithmsChallengeTypes" }
349+
347350
].concat(testMethods.get),
348351
post: [
349352
{ path: "/:apiVersion/users/resetPassword/:handle", action: "resetPassword" },
@@ -381,7 +384,8 @@ exports.routes = {
381384
{ path: "/:apiVersion/data/srm/rounds/:questionId/question", action: "modifyRoundQuestion"},
382385
{ path: "/:apiVersion/data/srm/rounds/:roundId/components", action: "setRoundComponents"},
383386
{ path: "/:apiVersion/data/srm/rounds/:roundId/terms", action: "setRoundTerms"},
384-
{ path: "/:apiVersion/data/srm/rounds", action: "createSRMContestRound" }
387+
{ path: "/:apiVersion/data/srm/rounds", action: "createSRMContestRound" },
388+
{ path: "/:apiVersion/src2image", action: "convertSourceCodeToImage" }
385389
],
386390
put: [
387391

0 commit comments

Comments
 (0)