How to use PDF.js in Angular 2/4/5? - javascript

I'm trying to develop PDfViewer Application using Mozilla's PDF.js (example here). It would be great if there is any github project as a reference.
Thanks inadvance!!

PDF.js with typings in Angualr 10
ng2-pdf-viewer is a great solution. I needed to use PDF.js directly as for the requirement to generate thumbnail in a service without creating a component.
This works as of Angular 10:
npm i pdfjs-dist
npm i #types/pdfjs-dist
Import and Usage. Note the GlobalWorkerOptions.workerSrc = pdfWorkerSrc;:
import { getDocument, GlobalWorkerOptions, PDFDocumentProxy, PDFRenderParams, version, ViewportParameters } from 'pdfjs-dist';
export class MyPdfService {
private document: Document;
constructor(#Inject(DOCUMENT) document) {
this.document = document;
const pdfWorkerSrc = `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/${version}/pdf.worker.min.js`;
GlobalWorkerOptions.workerSrc = pdfWorkerSrc;
}
// My use case demonstrating strongly typed usage.
public async pdfToImageDataURLAsync(pdfFile: File): Promise<string> {
const arrayBuffer = await new Response(pdfFile).arrayBuffer();
const canvas = this.document.createElement('canvas'),
ctx = canvas.getContext('2d') as CanvasRenderingContext2D,
data = arrayBuffer;
const pdf: PDFDocumentProxy = await getDocument(data).promise;
const page = await pdf.getPage(1);
const viewPortParams: ViewportParameters = { scale: 2 };
const viewport = page.getViewport(viewPortParams);
canvas.height = viewport.height;
canvas.width = viewport.width;
const renderContext: PDFRenderParams = {
canvasContext: ctx,
viewport: viewport
};
const renderedPage = await page.render(renderContext).promise;
const res = canvas.toDataURL();
if (pdf != null) pdf.destroy();
return res;
}
}

If it is not a must to use Mozilla's PDF.js then you can use ng2-pdf-viewer npm module which uses PDF.js in background. You can start of it with following steps
Install
npm install ng2-pdf-viewer --save
Note: For angular 4 or less use version 3.0.8
Then, import the module in app.module.js
import { PdfViewerModule } from 'ng2-pdf-viewer';
#NgModule({
imports: [BrowserModule, PdfViewerModule],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
And then use it in your component
#Component({
selector: 'example-app',
template: `
<pdf-viewer [src]="pdfSrc"
[render-text]="true"
style="display: block;">
</pdf-viewer>
})
For more details, refer the below git URL and the demo URL.
https://github.com/VadimDez/ng2-pdf-viewer
https://vadimdez.github.io/ng2-pdf-viewer/
Hope this helps you.

If the requirement is to use pdfjs directly on angular, here is the relevant code
copy the 'web' and 'build' folders from https://github.com/mozilla/pdfjs-dist under your application's assets folder.
Get/Create your `Blob() object
this.http.get(url, { responseType: ResponseContentType.Blob }).map(
(res) => {
return new Blob([res.blob()], { type: fileType });
});
3.Create url using blob and supply to viewer.html
// myBlob object is created over http call response. See item 2.
const fileUrl = URL.createObjectURL(myBlobObject);
let myFileName = "sample";
var viewerUrl = `assets/pdfjs/web/viewer.html?file=${encodeURIComponent(fileUrl)}&fileName=${sample}.pdf`;
window.open(viewerUrl);
You may have to manually upgrade pdfjs if you follow this steps.
If you are looking to use an easier solution without all these manual steps, install https://www.npmjs.com/package/ng2-pdfjs-viewer and follow the instructions.
The usage would be as easy as
<ng2-pdfjs-viewer pdfSrc="report.pdf"></ng2-pdfjs-viewer>

Related

React download an image from uri and put in static folder? [duplicate]

I am building an app with React Native, for Android and iOS. I am trying to let the user download a PDF file when clicking on a button.
react-native-file-download does not support Android
react-native-fs does nothing when I trigger downloadFile (nothing shows up on the notification bar), and I am not able to find the file after that. I added android.permission.WRITE_EXTERNAL_STORAGE to the Android Manifest file. I double-checked that the file I am trying to download exists (when it does not, the library throws an error)
I do not find other solutions for this problem. I have found libraries for viewing a PDF, but I would like to let the user download the PDF.
Just implemented the download feature an hour ago :p
Follow these steps:
a) npm install rn-fetch-blob
b) follow the installation instructions.
b2) if you want to manually install the package without using rnpm, go to their wiki.
c) Finally, that's how I made it possible to download files within my app:
const { config, fs } = RNFetchBlob
let PictureDir = fs.dirs.PictureDir // this is the pictures directory. You can check the available directories in the wiki.
let options = {
fileCache: true,
addAndroidDownloads : {
useDownloadManager : true, // setting it to true will use the device's native download manager and will be shown in the notification bar.
notification : false,
path: PictureDir + "/me_"+Math.floor(date.getTime() + date.getSeconds() / 2), // this is the path where your downloaded file will live in
description : 'Downloading image.'
}
}
config(options).fetch('GET', "http://www.example.com/example.pdf").then((res) => {
// do some magic here
})
If you're using Expo, react-native-fetch-blob won't work. Use FileSystem.
Here's a working example:
const { uri: localUri } = await FileSystem.downloadAsync(remoteUri, FileSystem.documentDirectory + 'name.ext');
Now you have localUri with the path to the downloaded file. Feel free to set your own filename instead of name.ext.
I Followed the solution from Jonathan Simonney, above on this post. But I had to change it a little:
const { config, fs } = RNFetchBlob;
const date = new Date();
const { DownloadDir } = fs.dirs; // You can check the available directories in the wiki.
const options = {
fileCache: true,
addAndroidDownloads: {
useDownloadManager: true, // true will use native manager and be shown on notification bar.
notification: true,
path: `${DownloadDir}/me_${Math.floor(date.getTime() + date.getSeconds() / 2)}.pdf`,
description: 'Downloading.',
},
};
config(options).fetch('GET', 'http://www.africau.edu/images/default/sample.pdf').then((res) => {
console.log('do some magic in here');
});
GetItem_downloadbtn = (item, itemname) => {
console.log("fiel url comiugn jdd " + item);
console.log("item name checkoing " + itemname);
const android = RNFetchBlob.android;
const filename = itemname;
const filepath = RNFetchBlob.fs.dirs.DownloadDir + '/foldernamae/' + filename;
const downloadAppUrl = item;
RNFetchBlob.config({
addAndroidDownloads: {
useDownloadManager: true,
title: 'great, download success',
description:'an apk that will be download',
mime: 'application/vnd.android.package-archive',
// mime: 'image/jpeg',
// mediaScannable: true,
notification: true,
path: filepath
}
})
.fetch('GET', downloadAppUrl)
.then((res) => {
// console.log('res.path ', res.path());
alert('res.path ', res.path());
android.actionViewIntent(res.path(), 'application/vnd.android.package-archive');
})
.catch((err) => {
alert('download error, err is', JSON.stringify(err));
});
}
I had the same issue, got it working using Expo WebBrowser Module
// install module
npm install react-native-webview
// import the module
import * as WebBrowser from 'expo-web-browser';
// then in your function you can call this function
await WebBrowser.openBrowserAsync(file_ur);
it will open preview of the file and then user can download using share button.

NextJS import images in MDX

I tried a official NextJS MDX-Blog example.
https://github.com/mdx-js/mdx/tree/master/examples/next
But what I'm not able to figure out is how do I setup the NextJS config to load images via webpack?
import img from "./image.jpg"
## Hallo Blogwelt
![My own Image]({img})
You can also use the /public directory to hold your images. For example, if you add an image at /public/image.jpg, you can reference the image in your blog post like this:
![Alt Text](/image.jpg)
Edit: https://nextjs.org/docs/basic-features/image-optimization#local-images
Imagine your next.config.js as something to append to an existing webpack.config behind the scenes. You won't have direct access to the webpack but you can extend it.
So in order to load images you'll need an appropriate image loader.
I found next-images easiest to use:
const withImages = require('next-images')
module.exports = withImages({
webpack(config, options) {
return config
}
})
then you can import:
import Img from "./image.jpg"
Hey thanks for the tip!
It's been a while since June and a gave it another try today and now it's working like expected from me.
I took the MDX/Next Example
Edited the next.config.js like so:
const withPlugins = require('next-compose-plugins');
const images = require('remark-images');
const emoji = require('remark-emoji');
const optimizedImages = require('next-optimized-images');
const withMDX = require('#zeit/next-mdx')({
extension: /\.mdx?$/,
options: {
mdPlugins: [images, emoji]
}
});
module.exports = withPlugins([
[
withMDX,
{
pageExtensions: ['js', 'jsx', 'md', 'mdx']
}
],
[optimizedImages]
]);
Now it works exactly like expected and in a Markdown file within the pages folder I'm able to do something like this:
import Layout from '../../components/Layout'
import Image from './catimage.jpg'
# Hey guys this is the heading of my post!
<img src={Image} alt="Image of a cat" />
Sorry I'm late, but with Next v11 you can directly import images.
That being said, you can add custom loaders for Webpack to modify your mdx files and use a custom component to process the image. e.g.:
// save this somewhere such as mdxLoader and
// add it to your webpack configuration
const replaceAll = require("string.prototype.replaceall");
module.exports = function (content, map, meta) {
return replaceAll(
map
content,
/\!\[(.*)\]\((.+)\)/g,
`<NextImage alt="$1" src={require('$2').default} />`
);
};
and process it:
// and reference this in your MDX provider
const components = {
NextImage: (props: any) => {
return <Image alt={props.alt || "Image"} {...props} />;
},
};
Now you can use markdown flavored images in your posts!
![my image](./image.png)
Include the ./ relative prefix, however.
I'm building a Next.js blog with MDX-Bundler. It allows you to use a remark plugin called remark-mdx-images which converts markdown flavored images into JSX images.
Below is an example configuration to get it to work
const {code} = await bundleMDX(source, {
cwd: '/posts/directory/on/disk',
xdmOptions: options => {
options.remarkPlugins = [...(options.remarkPlugins ?? []), remarkMdxImages]
return options
},
esbuildOptions: options => {
options.outdir = '/public/directory/on/disk/img'
options.loader = {
...options.loader,
'.jpg': 'file'
}
options.publicPath = '/img/'
options.write = true
return options
}
})
You can check out the following resources for detailed explanation on how to do it.
Images with MDX-Bundler
How I built the new notjust.dev platform with NextJS
If you installed the #next/mdx package you can use the <Image /> component Next.js provides:
// pages/cute-cat.mdx
import Image from "next/image";
import cuteCat from "./cute-cat.jpg";
# Cute cat
This is a picture of a cute cat
<Image src={cuteCat} />

Angular 4 - How to Simulate Mock Data for Prototyping and Development

I'm in the process of upgrading an AngularJS v1.5 project to Angular 4.x. During development of the original AngularJS application, we would use the ngMocks package to simulate actual web service API responses, and display the data accordingly on the page. This was incredibly helpful during development as I didn't have to hard-code values for removal later on. Best of all, we configured Webpack to only include the mock data during development, and ignore those mock data files when building our application for production use. The mock data was configured like this:
/* app-login.mock.js */
import angular from 'angular';
import 'angular-mocks';
angular.module('app').run(function ($httpBackend) {
$httpBackend
.whenPOST('./api/auth')
.respond(function(method, url, data) {
var credentials = angular.fromJson(data);
if (credentials.username == 'gooduser') {
return [200, {'token': createToken(credentials.username)}];
} else {
return [401, {'errorMsg': 'Mock login only allows username "gooduser"'}];
}
});
});
function createToken(username) {
// Create a token, which is valid enough for testing purposes.
// This token is based on the actual token generated by the web service.
let currentTime = new Date();
let futureTime = new Date(currentTime.getTime() + ((currentTime.getHours() + 8) * 60 * 60 * 1000));
let header = {
alg: 'HS512'
};
let payload = {
exp: futureTime.getTime() / 1000,
sub: username,
roles: 'SOME_APPLICATION_ROLES',
iat: currentTime.getTime() / 1000
};
return `${btoa(angular.toJson(header))}.${btoa(angular.toJson(payload))}`;
}
Webpack was then configured to include all "mock" files into the built bundle, which could then be displayed as if it were a real HTTP response.
/* webpack.config.js */
const isProd = process.env.NODE_ENV === 'production';
const entry = {
app: (() => {
let app = [
'babel-polyfill',
path.join(PATHS.app, 'pollyfills.ts'),
path.join(PATHS.app, 'main.ts')
];
if (isProd) {
app.push(path.join(PATHS.app, 'app.prod.js'));
} else {
app.push(path.join(PATHS.app, 'app.mock.js'));
}
return app;
})()
};
module.exports = {
entry,
// ...other exports
};
And then the app.mock.js file:
/* app.mock.js */
var mockContext = require.context(".", true, /\.mock$/);
mockContext.keys().forEach(mockContext);
I've scoured the internet looking for a solution that works just as well as our old one, though I haven't come up with any good answers. Best I've found are tutorials on how to set up Unit Tests that return mock data, and while that's useful for testing functionality it doesn't help me test the application during the development process.
I also have seen some documentation on setting up Interceptors using the new HttpClient class found within Angular 4, but I'm not sure how to add it to our Webpack configuration under the condition of only being allowed during development. Does anyone have any advice on what to do?
I use the angular-in-memory-web-api. You can find it here: https://github.com/angular/in-memory-web-api
UPDATE: The repo was moved here, within the angular/angular repo: https://github.com/angular/angular/tree/e0dfa42d6e656124f3c3d78e178b1bf091b38e79/packages/misc/angular-in-memory-web-api
It intercepts all of your http calls and works with sample data you provide.
To change from dev to production, you need to remove the imports. Or you could possibly write two different modules, one with the dev imports and one with the production imports and include one or the other with webpack similar to what you do now. (But I have not tried this.)
You set up your data like this:
import { InMemoryDbService } from 'angular-in-memory-web-api';
import { IProduct } from './product';
export class ProductData implements InMemoryDbService {
createDb() {
let products: IProduct[] = [
{
'id': 1,
'productName': 'Leaf Rake',
'productCode': 'GDN-0011',
'releaseDate': 'March 19, 2016',
'description': 'Leaf rake with 48-inch wooden handle.',
'price': 19.95,
'starRating': 3.2,
'imageUrl': 'http://openclipart.org/image/300px/svg_to_png/26215/Anonymous_Leaf_Rake.png',
'tags': ['rake', 'leaf', 'yard', 'home']
},
// ...
];
return { products };
}
}
And you build your data access service using the normal Http or HttpClient.
I have a full example with all CRUD operations here: https://github.com/DeborahK/Angular2-ReactiveForms

Vue.js exclude settings file from being bundled

I am using the vue-webpack template and I have created a settings.json file to store environment variables that should be changed when installing the script.
My settings.json (just store the absolute path to the API server):
{
"apiURL": "//localhost/app/server/API"
}
How can I keep the file from being minified/bundled in the production version such that I can change it and the updated file will be used next time the app is accessed (without having to build it again) ?
In my app I use this file via require:
const SETTINGS = require('../settings.json');
I understand that by requireing it webpack will bundle it as a module, but how can I include it in my app such that the settings file will still be a separated file in the production build that I can edit.
Is there a better format/way to store those settings (so that they can be edited in production without re-building) ?
You can define those settings in an object that can be referenced in the externals configuration in webpack.config.js.
The externals configuration option provides a way of excluding
dependencies from the output bundles. Instead, the created bundle
relies on that dependency to be present in the consumer's environment.
Example:
externals: {
appSettings: "appSettings",
"window.appSettings": "appSettings"
}
Where appSettings is a global variable containing the environment variables you want to manipulate.
Alternatively, if you do not like that method that exposes the settings in the global object, you can do the following:
Export a variable with the default settings, which will be included in webpack bundle.
export var appSettings = {
currentSettings: "",
settings: {},
getString: function(strName) {
var sett = this.currentSettings ?
this.settings[this.currentSettings] :
appDefaultStrings;
if (!sett || !sett[strName]) sett = appDefaultStrings;
return sett[strName];
},
getSettings: function() { //Gets all available settings
var res = [];
res.push("");
for (var key in this.settings) {
res.push(key);
}
res.sort();
return res;
}
};
export var appDefaultStrings = {
apiURL: "//localhost/app/server/API"
//...
}
appSettings.settings["default"] = appDefaultStrings;
You can then require or import this variable and use it like so:
import appSettings from "../path/to/appSettings";
appSettings.getString("apiURL"); //"//localhost/app/server/API"
Now that you have your default settings up and running, we will create another file containing the custom settings.
import appSettings from "../path/to/appSettings";
export var appProductionSettings = {
apiUrl: "http://example.com"
//...
}
appSettings.settings["production"] = appProductionSettings;
The last thing you need to do is handle which settings you want to use. I have not used vue.js yet, but hopefully this will lead you in the right direction:
import appSettings from "../path/to/appSettings";
export class MyApp {
constructor() {
this.settingsValue = "";
}
get settings() {
return this.settingsValue;
}
set settings(value) {
this.settingsValue = value;
appSettings.currentSettings = value;
}
}
Change the settings:
import "../path/to/productionSettings";
var app = new MyApp();
app.settings = "production";
With this method you can create and use as many settings files as you want.

Angular2 / Electron application using electron API within the angular2 ts files

I have setup an angular2 / Electron app similar to the explanation in this video : https://www.youtube.com/watch?v=pLPCuFFeKOU. The project I am basing my code on can be found here : https://github.com/rajayogan/angular2-desktop
I am getting the error:
app.ts:16Uncaught TypeError: Cannot read property 'on' of undefined
When I try to run this code:
import { bootstrap } from '#angular/platform-browser-dynamic';
import { Component } from '#angular/core';
import { MenuComponent} from './menu';
import { ConfigEditorComponent } from './config-editor';
import { remote, ipcRenderer} from 'electron';
let {dialog} = remote;
//Functions used for select server xml callbacks.
const ipc = require('electron').ipcMain
const xml2js = require('xml2js')
const fs = require('fs')
var parser = new xml2js.Parser();
ipc.on('open-file-dialog', function (event) {
dialog.showOpenDialog({
title:"Select zOS Connect server.xml",
properties: ['openFile', 'openDirectory'],
filters: [
{name: 'XML', extensions: ['xml']},
{name: 'All Files', extensions: ['*']}
]
}, function (files) {
if (files){
fs.readFile(files[0], function(err, data) {
parser.parseString(data, function (err, result) {
console.dir(result);
process_server_xml(event,result);
})
})
}
})
})
function process_server_xml(event,json){
console.log("oh hello!")
event.sender.send('selected-directory', json)
console.log("oh im done!")
}
#Component({
selector: 'connect-toolkit',
templateUrl: 'app.component.html',
directives: [ MenuComponent, ConfigEditorComponent ]
})
export class AppComponent {
constructor() {
var menu = remote.Menu.buildFromTemplate([{
label: 'Raja',
submenu: [
{
label: 'open',
click: function(){
dialog.showOpenDialog((cb) => {
})
}
},
{
label: 'opencustom',
click: function(){
ipcRenderer.send('open-custom');
let notification = new Notification('Customdialog', {
body: 'This is a custom window created by us'
})
}
}
]
}])
remote.Menu.setApplicationMenu(menu);
}
}
bootstrap(AppComponent);
I think the problem may be:
const ipc = require('electron').ipcMain
const xml2js = require('xml2js')
const fs = require('fs')
var parser = new xml2js.Parser();
Is it possible require doesn't work here, and somehow I need to use import statements instead from my ts files? If this is the case how do I use the import in order to get the ipcMain object and my xml2js etc?
Why would that be the case? How can I make require work within the ts files if this is the problem.
Note that if I remove the require lines, and all the ipc.on code everything runs as expected and works fine (other than the fact that the ipc event is never received ;)
Calling ipcMain doesn't work because you're not on main (i.e., the electron side code, which is on electron index.js file), your are on renderer (web page). Therefore you must use ipcRenderer instead, which is already imported using es6 import syntax on top of your app.ts file. And if you want to make something using electron ipcMain, it have to be done from the electron code side.
import {remote, ipcRenderer} from 'electron';
Electron ipc notes:
ipcMain Communicate asynchronously from the main process to renderer processes.
ipcRenderer Communicate asynchronously from a renderer process to the main process.

Categories

Resources