I'm using pdf.js library to render my pdf file on canvas.
First I was searching a solution for rendering pdf with the size of canvas parent element.
I've found it and it works fine.
Then I solve the problem of rendering ALL pages at once.
Finally, my code now looks this way:
pdfjsLib.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjsLib.version}/pdf.worker.min.js`;
class PdfLoader {
currPage = 1;
numPages = -1;
doc = null;
constructor($container) {
this.$container = $container;
}
load(path) {
// reset when using more than once
this.currPage = 1;
this.promise = new Promise(resolve => this.promiseResolve = resolve);
this.$container.innerHTML = "";
pdfjsLib.getDocument(path).promise.then(pdf => {
this.doc = pdf;
this.numPages = pdf.numPages;
pdf.getPage(1).then(this._handlePage.bind(this));
});
return this;
}
_handlePage(page) {
let viewport = page.getViewport({scale: 1});
const scale = this.$container.clientWidth/viewport.width;
// const outputScale = window.devicePixelRatio || 1;
const outputScale = 1;
viewport = page.getViewport({scale});
const cnv = document.createElement("canvas");
const ctx = cnv.getContext("2d");
const width = Math.floor(viewport.width);
const height = Math.floor(viewport.height);
cnv.width = Math.floor(width * outputScale);
cnv.height = Math.floor(height * outputScale);
cnv.style.width = `${width}px`;
cnv.style.height = `${height}px`;
const transform = (outputScale !== 1) ? [outputScale, 0, 0, outputScale, 0, 0] : null;
page.render({
canvasContext: ctx,
transform: transform,
viewport: viewport,
});
this.$container.appendChild(cnv);
this.currPage++;
if (this.doc !== null && this.currPage <= this.numPages) {
this.doc.getPage(this.currPage).then(this._handlePage.bind(this));
} else {
this.promiseResolve();
}
}
}
const $pages = document.getElementById("pages");
const pdfLoader = new PdfLoader($pages);
pdfLoader.load("extendedReport.pdf").promise
.then(initReport);
let arrow = null;
function initReport() {
// my other stuff
}
And now my problem is that when viewing rendered pdf it looks like its quality is very low and text is blurred so the document is unreadable on mobile devices. I tried to change passed scale like they say on the internet, but that's not it. Could you help me, plz? What am I missing?
The image that I want to get:
Image 1
The image that I'm getting when I run my code:
Image 2
To get the first image, you have to choose the tiny option in "number of regions", disable the check marks "noisy edges", "noisy fills" and "icons" in the site: link
My code is below, if you go to the site link and inspect it, you will see all the modules, is easy to see. In the draw.js file I edited document.createElement('canvas') to new Canvas.Canvas(), and I put var Canvas = require('canvas') in there too, theses things are the only thing that I edited (If I'm not mistaken) in all the modules. I'm running the code with node.
var fs = require('fs');
var Canvas = require('canvas')
const SimplexNoise = require('./simplex-noise/simplex-noise.js');
const Poisson = require('./poisson-disk-sampling/poisson-disk-sampling.js');
const DualMesh = require('./dual-mesh/index.js');
const MeshBuilder = require('./dual-mesh/create.js');
const Map = require('./mapgen2/index.js');
const Draw = require('./draw.js');
const Colormap = require('./colormap.js');
const {makeRandInt, makeRandFloat} = require('./prng/index.js');
let defaultUiState = {
seed: 187,
variant: 0,
size: 'tiny',
'noisy-fills': false,
'noisy-edges': false,
icons: false,
biomes: false,
lighting: false,
'north-temperature': 0,
'south-temperature': 0.05,
rainfall: 0,
canvasSize: 0,
persistence: 0, /* 0 means "normal" persistence value of 1/2 */
};
let uiState = {};
Object.assign(uiState, defaultUiState);
let _mapCache = [];
function getMap(size) {
const spacing = {
tiny: 38,
small: 26,
medium: 18,
large: 12.8,
huge: 9,
};
if (!_mapCache[size]) {
let mesh = new MeshBuilder({boundarySpacing: spacing[size]})
.addPoisson(Poisson, spacing[size], makeRandFloat(12345))
.create();
_mapCache[size] = new Map(
new DualMesh(mesh),
{amplitude: 0.2, length: 4, seed: 12345},
makeRandInt
);
console.log(`Map size "${size}" has ${_mapCache[size].mesh.numRegions} regions`);
}
return _mapCache[size];
}
let requestAnimationFrameQueue = [];
const mapIconsConfig = {left: 9, top: 4, filename: "./map-icons.png"};
mapIconsConfig.image = new Canvas.Image();
mapIconsConfig.image.onload = draw;
mapIconsConfig.image.src = mapIconsConfig.filename;
function draw() {
let map = getMap(uiState.size);
let noisyEdges = uiState['noisy-edges'],
noisyFills = uiState['noisy-fills'];
let canvas = new Canvas.Canvas(1000, 1000);
let ctx = canvas.getContext('2d');
let size = Math.min(canvas.width, canvas.height);
if (size != uiState.canvasSize) {
uiState.canvasSize = size;
size = 1024;
canvas.width = size;
canvas.height = size;
}
let noise = new SimplexNoise(makeRandFloat(uiState.seed));
let persistence = Math.pow(1/2, 1 + uiState.persistence);
let islandShapeAmplitudes = Array.from({length: 5}, (_,octave) => Math.pow(persistence, octave));
let biomeBias = {
north_temperature: uiState['north-temperature'],
south_temperature: uiState['south-temperature'],
moisture: uiState.rainfall,
};
let colormap = uiState.biomes? new Colormap.Discrete() : new Colormap.Smooth();
let queue = [];
if ((!noisyEdges || uiState.size === 'large' || uiState.size === 'huge')) {
queue.push(() => Draw.approximateIslandShape(ctx, 1000, 1000, noise, {round: 0.5, inflate: 0.4, amplitudes: islandShapeAmplitudes.slice(0, 3)}));
}
queue.push(
() => map.calculate({
noise: noise,
drainageSeed: uiState.variant,
riverSeed: uiState.variant,
biomeBias: biomeBias,
shape: {round: 0.5, inflate: 0.4, amplitudes: islandShapeAmplitudes},
}),
() => {
Draw.background(ctx, colormap);
Draw.noisyRegions(ctx, map, colormap, noisyEdges);
// Draw the rivers early for better user experience
Draw.rivers(ctx, map, colormap, noisyEdges, true);
}
);
for (let phase = 0; phase < 16; phase++) {
queue.push(() => Draw.noisyEdges(ctx, map, colormap, noisyEdges, phase));
}
queue.push(() => Draw.rivers(ctx, map, colormap, noisyEdges, false));
queue.push(() => Draw.coastlines(ctx, map, colormap, noisyEdges));
if (noisyFills) {
queue.push(() => Draw.noisyFill(ctx, 1000, 1000, makeRandInt(12345)));
}
if (uiState.icons) {
queue.push(() => Draw.regionIcons(ctx, map, mapIconsConfig, makeRandInt(uiState.variant)));
}
if (uiState.lighting) {
queue.push(() => Draw.lighting(ctx, 1000, 1000, map));
}
requestAnimationFrameQueue = queue.map(
(layer, i) => () => {
ctx.save();
ctx.scale(canvas.width / 1000, canvas.height / 1000);
layer();
ctx.restore();
});
while (requestAnimationFrameQueue.length > 0) {
let f = requestAnimationFrameQueue.shift();
f();
}
return canvas;
}
function saveToFile(canvas) {
const buff = canvas.toBuffer();
fs.writeFileSync("test.png", buff, {encoding: "utf-8", flag: "w+", mode: 0o666});
}
let canvas = draw();
saveToFile(canvas);
I'm trying to get a multiple tool paperjs example going that is a bit of a vector based "paint program".
I'm trying to do this with a toolStack class from a Stack Overflow suggestion. The original post has two placeholder tools and I added another. The two native tools work fine and display, my third tool "multiLine" (which runs fine as a stand along file) seems to be working, does not throw errors in the browser, and from inspecting the results seems to have data in the structures. However the third tool doesn't display.
I realize the syntax is different in different section of the code. Both syntaxes seem to work fine. I'm new to Javascripting so don't know if this is an issue. Thanks in advance for any eyes or suggestions about the code.
<!DOCTYPE html>
<html>
<style>
html,
body,
canvas {
width: 100%;
height: 100%;
margin: 0;
}
</style>
<script>
window.onload = () => {
// Setup Paper
paper.setup(document.querySelector('canvas'))
// Toolstack
class ToolStack {
constructor(tools) {
this.tools = tools.map(tool => tool())
}
activateTool(name) {
const tool = this.tools.find(tool => tool.name === name)
tool.activate()
}
// add more methods here as you see fit ...
}
// Tool Path, draws paths on mouse-drag
const toolPath = () => {
const tool = new paper.Tool()
tool.name = 'toolPath'
let path
tool.onMouseDown = function(event) {
path = new paper.Path()
path.strokeColor = '#4242ff'
path.strokeWidth = 15;
path.add(event.point)
}
tool.onMouseDrag = function(event) {
path.add(event.point)
console.log(path);
}
return tool
}
// Tool Circle, draws a 30px circle on mousedown
const toolCircle = () => {
const tool = new paper.Tool()
tool.name = 'toolCircle'
let path
tool.onMouseDown = function(event) {
path = new paper.Path.Circle({
center: event.point,
radius: 30,
fillColor: '#9C27B0'
})
}
return tool
}
// This is the tool I added which does not display
const multiLine = () => {
const tool = new paper.Tool()
tool.name = 'multiLine'
var values = {
//lines: 5,
lines: 4,
//size: 40,
size: 10,
smooth: true
};
var paths;
// tool.onMouseDown = function(event) { // this worked for debugging the tool
// path = new paper.Path()
// path.strokeColor = '#ff4242'
// path.strokeWidth = 10
// path.add(event.point)
// }
//
// tool.onMouseDrag = function(event) {
// path.add(event.point)
// }
tool.onMouseDown = function(event) {
paths = [];
for (var i = 0; i < values.lines; i++) {
var path = new paper.Path();
path.strokeColor = '#FF2222';
path.strokeWidth = 25;
paths.push(path);
console.log(i);
}
}
tool.onMouseDrag = function(event) {
var offset = event.delta;
offset.angle = offset.angle + 90;
var lineSize = values.size / values.lines;
for (var i = 0; i < values.lines; i++) {
var path = paths[values.lines - 1 - i];
//offset.length = lineSize * i + lineSize / 2;
offset.length = (-i * lineSize) * (Math.max(event.delta.length / 15, 1));
path.add(event.middlePoint + offset);
// path.smooth();
console.log(paths[1]);
}
}
return tool
}
// Construct a Toolstack, passing your Tools
const toolStack = new ToolStack([toolPath, toolCircle, multiLine])
// Activate a certain Tool
toolStack.activateTool('toolPath')
// Attach click handlers for Tool activation on all
// DOM buttons with class '.tool-button'
document.querySelectorAll('.tool-button').forEach(toolBtn => {
toolBtn.addEventListener('click', e => {
toolStack.activateTool(e.target.getAttribute('data-tool-name'))
})
})
}
// function onKeyDown(event) {
// if (event.key == 'd'){
// project.activeLayer.removeChildren();
// }
// if (event.key == 'z'){
// project.activeLayer.removeChildren(project.activeLayer.lastChild.index);
// }
//
// }
</script>
<head>
<title>
StackExchange Multiple Tools
</title>
<meta name="generator" content="BBEdit 14.1" />
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.11.5/paper-core.js">
</script>
<button class="tool-button" data-tool-name="toolPath">
Draw Paths
</button>
<button class="tool-button" data-tool-name="toolCircle">
Stamp Circles
</button>
<button class="tool-button" data-tool-name="multiLine">
Multi Lines
</button>
<canvas resize>
</canvas>
</body>
</html>
I am trying to combine controls of amcharts to the react audio player.
Here, I have amcharts line graph with a slider. Now I am trying control the slider in such a way that whenever I hit the play button of react audio player, I could move the slider with the audio player's seeker. I hope, this makes sense to you.
import React from "react";
import ReactAudioPlayer from "react-audio-player";
import audio from "/home/aniruddha/workspace/playwith_audio/anni_web_player/src/audio.flac";
import * as am4core from "#amcharts/amcharts4/core";
import * as am4charts from "#amcharts/amcharts4/charts";
import am4themes_spiritedaway from "#amcharts/amcharts4/themes/spiritedaway";
import am4themes_animated from "#amcharts/amcharts4/themes/animated";
/* Chart code */
// Themes begin
am4core.useTheme(am4themes_spiritedaway);
am4core.useTheme(am4themes_animated);
class App extends React.Component {
constructor(props) {
super(props);
this.state = {slider: 1586169460800};
}
componentDidMount() {
let chart = am4core.create("chartdiv", am4charts.XYChart);
// Add data
chart.data = this.generateChartData();
// Create axes
let dateAxis = chart.xAxes.push(new am4charts.DateAxis());
let valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
// Create series
let series = chart.series.push(new am4charts.LineSeries());
series.dataFields.valueY = "visits";
series.dataFields.dateX = "date";
series.strokeWidth = 1;
series.minBulletDistance = 10;
series.tooltipText = "{valueY}";
series.fillOpacity = 0.1;
series.tooltip.pointerOrientation = "vertical";
series.tooltip.background.cornerRadius = 20;
series.tooltip.background.fillOpacity = 0.5;
series.tooltip.label.padding(12, 12, 12, 12);
let seriesRange = dateAxis.createSeriesRange(series);
seriesRange.contents.strokeDasharray = "2,3";
seriesRange.contents.stroke = chart.colors.getIndex(8);
seriesRange.contents.strokeWidth = 1;
let pattern = new am4core.LinePattern();
pattern.rotation = -45;
pattern.stroke = seriesRange.contents.stroke;
pattern.width = 1000;
pattern.height = 1000;
pattern.gap = 6;
seriesRange.contents.fill = pattern;
seriesRange.contents.fillOpacity = 0.5;
// Add scrollbar
chart.scrollbarX = new am4core.Scrollbar();
// add range
let range = dateAxis.axisRanges.push(new am4charts.DateAxisDataItem());
range.grid.stroke = chart.colors.getIndex(0);
range.grid.strokeOpacity = 1;
range.bullet = new am4core.ResizeButton();
range.bullet.background.fill = chart.colors.getIndex(0);
range.bullet.background.states.copyFrom(
chart.zoomOutButton.background.states
);
range.bullet.minX = 0;
range.bullet.adapter.add("minY", function (minY, target) {
target.maxY = chart.plotContainer.maxHeight;
target.maxX = chart.plotContainer.maxWidth;
return chart.plotContainer.maxHeight;
});
range.bullet.events.on("dragged", function () {
range.value = dateAxis.xToValue(range.bullet.pixelX);
seriesRange.value = range.value;
console.log(seriesRange.value)
});
let firstTime = chart.data[0].date.getTime();
let lastTime = chart.data[chart.data.length - 1].date.getTime();
let date = new Date(firstTime + (lastTime - firstTime) / 2);
range.date = date;
seriesRange.date = date;
seriesRange.endDate = chart.data[chart.data.length - 1].date;
this.chart = chart
console.log(this.state.slider);
this.setState({ seriesRange } )
console.log(this.state.slider);
range.value = this.state.slider;
seriesRange.value = this.state.slider;
}
generateChartData() {
let chartData = [];
let firstDate = new Date();
firstDate.setDate(firstDate.getDate() - 200);
let visits = 1200;
for (var i = 0; i < 200; i++) {
let newDate = new Date(firstDate);
newDate.setDate(newDate.getDate() + i);
visits += Math.round(
(Math.random() < 0.5 ? 1 : -1) * Math.random() * 10
);
chartData.push({
date: newDate,
visits: visits,
});
}
return chartData;
}
sound(event){
console.log(event.timeStamp);
//this.setState({slider: 1586025000000 })
}
seek(event){
console.log(event);
}
lis(event){
console.log(event);
}
componentWillUnmount() {
if (this.chart) {
this.chart.dispose();
}
}
render() {
return (
<div>
<div id="chartdiv" style={{ width: "100%", height: "500px" }}></div>
<ReactAudioPlayer src={audio} onPlay={this.sound} onListen={this.lis} onSeeked={this.seek} controls></ReactAudioPlayer>
</div>
);
}
}
export default App;
I am not able to access this.setState({slider: 1586025000000 }) in the sound function. I am quite new to react. Please any suggestion is welcome;
You need to wrap your callbacks with arrow functions that call your methods directly (or call bind: this.sound.bind(this)) so that it resolves to the correct this scope:
<ReactAudioPlayer
src="{audio}"
onPlay={(ev) => this.sound(ev)}
onListen={(ev) => this.lis(ev)}
onSeeked={(ev) => this.seek(ev)}
controls></ReactAudioPlayer>
I have a dat.gui user interface in which I want to run some math calculations using the value of one slider input (numberOne), and then show the result in a message output (resultOne).
I can't figure out how to get the calculation result into the dat.gui message field.
import * as Calc from './components/Calc.js';
function init() {
let groupA = {
valA1: 0,
valA2: 10
};
let groupB = {
valB1: 3,
valB2: 5.6
};
let calc = Calc.SomeCalculations(groupA, groupB); // Invokes a function in another JS file.
const controller = new function() {
this.numberOne = 0;
this.resultOne = calc.resultOne;
}();
const gui = new GUI( {width: 300 });
const f1 = gui.addFolder('My inputs');
f1.add(controller, 'numberOne', 0, 100).onChange( function() {
// What goes here?
} );
f1.open();
const f2 = gui.addFolder('My results');
f2.add(controller, 'resultOne');
f2.open();
gui.open();
}
Nevermind, I just realized that I need to use .setValue()
const gui = new GUI( {width: 300 });
const f1 = gui.addFolder('My inputs');
var gui_numberOne = f1.add(controller, 'numberOne', 0, 100);
f1.open();
const f2 = gui.addFolder('My results');
var gui_resultOne = f2.add(controller, 'resultOne');
gui_numberOne.onChange( function() {
gui_resultOne.setValue( Calc.SomeCalculations(groupA, groupB).myResult );
} );
f2.open();
gui.open();