Skip to content

Commit

Permalink
Desktop: Resolves #10334: Support URLs in plugin API imaging.createFr…
Browse files Browse the repository at this point in the history
…omPath
  • Loading branch information
laurent22 committed Apr 27, 2024
1 parent a0faca0 commit 034e568
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 28 deletions.
37 changes: 37 additions & 0 deletions packages/app-cli/tests/support/plugins/imaging/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,42 @@ const registerMakeThumbnailCommand = async () => {
await joplin.views.toolbarButtons.create('makeThumbnailButton', 'makeThumbnail', ToolbarButtonLocation.EditorToolbar);
};

const registerMakeThumbnailFromUrlCommand = async () => {
await joplin.commands.register({
name: 'makeThumbnailFromUrl',
execute: async () => {
const urls = [
'https://github.com/laurent22/joplin/blob/dev/Assets/ImageSources/RoundedCornersMac_1024x1024.png?raw=true',
'https://github.com/laurent22/joplin/blob/dev/packages/app-cli/tests/ocr_samples/multi_page__embedded_text.pdf?raw=true',
]

for (const url of urls) {
// ---------------------------------------------------------------
// Create an image from URLs
// ---------------------------------------------------------------

const imageHandle = await joplin.imaging.createFromPath(url);
const resizedImageHandle = await joplin.imaging.resize(imageHandle, { width: 100 });

// ---------------------------------------------------------------
// Convert the image to a resource and add it to the note
// ---------------------------------------------------------------

const newResource = await joplin.imaging.toJpgResource(resizedImageHandle, { title: "Thumbnail" });
await joplin.commands.execute('insertText', '\n![](:/' + newResource.id + ')');

// ---------------------------------------------------------------
// Free up the image objects at the end
// ---------------------------------------------------------------

await joplin.imaging.free(imageHandle);
await joplin.imaging.free(resizedImageHandle);
}
},
});

await joplin.views.toolbarButtons.create('makeThumbnailFromUrlButton', 'makeThumbnailFromUrl', ToolbarButtonLocation.EditorToolbar);
};

const registerInlinePdfCommand = async () => {
await joplin.commands.register({
Expand Down Expand Up @@ -106,5 +142,6 @@ joplin.plugins.register({
onStart: async function() {
await registerMakeThumbnailCommand();
await registerInlinePdfCommand();
await registerMakeThumbnailFromUrlCommand();
},
});
43 changes: 27 additions & 16 deletions packages/app-desktop/services/plugins/PlatformImplementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import BasePlatformImplementation, { Joplin } from '@joplin/lib/services/plugins
import { CreateFromPdfOptions, Implementation as ImagingImplementation } from '@joplin/lib/services/plugins/api/JoplinImaging';
import shim from '@joplin/lib/shim';
import { join } from 'path';
import uuid from '@joplin/lib/uuid';
import uuid, { uuidgen } from '@joplin/lib/uuid';
import { hasProtocol } from '@joplin/utils/url';
import { fileExtension } from '@joplin/utils/path';
const { clipboard, nativeImage } = require('electron');
const packageInfo = require('../../packageInfo');

Expand Down Expand Up @@ -97,24 +99,33 @@ export default class PlatformImplementation extends BasePlatformImplementation {
await shim.fsDriver().remove(tempDir);
}
};

return {
nativeImage: {
async createFromPath(path: string) {
if (path.toLowerCase().endsWith('.pdf')) {
const images = await createFromPdf(path, { minPage: 1, maxPage: 1 });

if (images.length === 0) {
// Match the behavior or Electron's nativeImage when reading an invalid image.
return nativeImage.createEmpty();
}

return images[0];
} else {
return nativeImage.createFromPath(path);
createFromPath: async (path: string) => {
let pathToProcess = path;
let ext = fileExtension(path).toLowerCase();

if (hasProtocol(path, ['http', 'https', 'file'])) {
ext = fileExtension((new URL(path)).pathname);
const tempFilePath = `${Setting.value('tempDir')}/${uuidgen()}${ext ? `.${ext}` : ''}`;
await shim.fetchBlob(path, { path: tempFilePath });
pathToProcess = tempFilePath;
}

if (ext === 'pdf') {
const images = await createFromPdf(pathToProcess, { minPage: 1, maxPage: 1 });

if (images.length === 0) {
// Match the behavior or Electron's nativeImage when reading an invalid image.
return nativeImage.createEmpty();
}
},
createFromPdf,

return images[0];
} else {
return nativeImage.createFromPath(pathToProcess);
}
},
createFromPdf,
getPdfInfo(path: string) {
return shim.pdfInfo(path);
},
Expand Down
4 changes: 2 additions & 2 deletions packages/app-desktop/testPluginDemo.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
TEMP_PATH=~/src/plugin-tests
NEED_COMPILING=1
PLUGIN_PATH=~/src/plugin-abc
PLUGIN_PATH=~/src/joplin/packages/app-cli/tests/support/plugins/imaging

if [[ $NEED_COMPILING == 1 ]]; then
mkdir -p "$TEMP_PATH"
Expand All @@ -18,7 +18,7 @@ if [[ $NEED_COMPILING == 1 ]]; then

rsync -a --delete "$PLUGIN_PATH/" "$TEMP_PLUGIN_PATH/"

npm install --prefix="$TEMP_PLUGIN_PATH" && yarn start --dev-plugins "$TEMP_PLUGIN_PATH"
NODE_OPTIONS=--openssl-legacy-provider npm install --prefix="$TEMP_PLUGIN_PATH" && yarn start --dev-plugins "$TEMP_PLUGIN_PATH"
else
yarn start --dev-plugins "$PLUGIN_PATH"
fi
Expand Down
9 changes: 7 additions & 2 deletions packages/app-mobile/plugins/PlatformImplementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Implementation as WindowImplementation } from '@joplin/lib/services/plu
import Setting from '@joplin/lib/models/Setting';
import { reg } from '@joplin/lib/registry';
import BasePlatformImplementation, { Joplin } from '@joplin/lib/services/plugins/BasePlatformImplementation';
import { Implementation as ImagingImplementation } from '@joplin/lib/services/plugins/api/JoplinImaging';
import { CreateFromPdfOptions, Implementation as ImagingImplementation } from '@joplin/lib/services/plugins/api/JoplinImaging';
import RNVersionInfo from 'react-native-version-info';
import { _ } from '@joplin/lib/locale';
import shim from '@joplin/lib/shim';
Expand Down Expand Up @@ -76,7 +76,12 @@ export default class PlatformImplementation extends BasePlatformImplementation {

public get imaging(): ImagingImplementation {
return {
nativeImage: null,
createFromPath: async (_path: string) => {
throw new Error('Not implemented: createFromPath');
},
createFromPdf: (_path: string, _options: CreateFromPdfOptions) => {
throw new Error('Not implemented: createFromPdf');
},
getPdfInfo: async () => {
throw new Error('Not implemented: getPdfInfo');
},
Expand Down
17 changes: 9 additions & 8 deletions packages/lib/services/plugins/api/JoplinImaging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,8 @@ export interface PdfInfo {
}

export interface Implementation {
nativeImage: {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
createFromPath: (path: string)=> Promise<any>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
createFromPdf: (path: string, options: CreateFromPdfOptions)=> Promise<any[]>;
};
createFromPath: (path: string)=> Promise<unknown>;
createFromPdf: (path: string, options: CreateFromPdfOptions)=> Promise<unknown[]>;
getPdfInfo: (path: string)=> Promise<PdfInfo>;
}

Expand Down Expand Up @@ -118,16 +114,21 @@ export default class JoplinImaging {
// return this.cacheImage(this.implementation_.nativeImage.createFromBuffer(buffer, options));
// }

/**
* Creates an image from the provided path. Note that images and PDFs are supported. If you
* provide a URL instead of a local path, the file will be downloaded first then converted to an
* image.
*/
public async createFromPath(filePath: string): Promise<Handle> {
return this.cacheImage(await this.implementation_.nativeImage.createFromPath(filePath));
return this.cacheImage(await this.implementation_.createFromPath(filePath));
}

public async createFromResource(resourceId: string): Promise<Handle> {
return this.createFromPath(await getResourcePath(resourceId));
}

public async createFromPdfPath(path: string, options?: CreateFromPdfOptions): Promise<Handle[]> {
const images = await this.implementation_.nativeImage.createFromPdf(path, options);
const images = await this.implementation_.createFromPdf(path, options);
return images.map(image => this.cacheImage(image));
}

Expand Down

0 comments on commit 034e568

Please sign in to comment.