David PHAM-VAN

Add google fonts

... ... @@ -56,3 +56,4 @@ package.json
GeneratedPluginRegistrant.*
test/social_preview.png
test/fonts.json
... ...
# Changelog
## 5.4.0
- Add Google Fonts support
## 5.3.0
- Fix raster crash on all OS.
... ...
... ... @@ -7,7 +7,9 @@ import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:printing/printing.dart';
void main() => runApp(const MyApp('Printing Demo'));
Future<void> main() async {
runApp(const MyApp('Printing Demo'));
}
class MyApp extends StatelessWidget {
const MyApp(this.title);
... ... @@ -27,14 +29,24 @@ class MyApp extends StatelessWidget {
}
Future<Uint8List> _generatePdf(PdfPageFormat format, String title) async {
final pdf = pw.Document();
final pdf = pw.Document(version: PdfVersion.pdf_1_5, compress: true);
final font = await PdfGoogleFonts.nunitoExtraLight();
pdf.addPage(
pw.Page(
pageFormat: format,
build: (context) {
return pw.Center(
child: pw.Text(title),
return pw.Column(
children: [
pw.SizedBox(
width: double.infinity,
child: pw.FittedBox(
child: pw.Text(title, style: pw.TextStyle(font: font)),
),
),
pw.SizedBox(height: 20),
pw.Flexible(child: pw.FlutterLogo())
],
);
},
),
... ...
... ... @@ -15,7 +15,9 @@
*/
export 'src/asset_utils.dart';
export 'src/cache.dart';
export 'src/callback.dart';
export 'src/fonts/gfonts.dart';
export 'src/preview/pdf_preview.dart';
export 'src/preview/pdf_preview_action.dart';
export 'src/printer.dart';
... ...
... ... @@ -15,8 +15,6 @@
*/
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/rendering.dart' as rdr;
... ... @@ -24,6 +22,8 @@ import 'package:flutter/services.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart';
import 'cache.dart';
/// Loads an image from a Flutter [ImageProvider]
/// into an [ImageProvider] instance
Future<ImageProvider> flutterImageProvider(
... ... @@ -66,77 +66,35 @@ Future<ImageProvider> flutterImageProvider(
/// Loads a font from an asset bundle key. If used multiple times with the same font name,
/// it will be included multiple times in the pdf file
Future<TtfFont> fontFromAssetBundle(String key, [AssetBundle? bundle]) async {
Future<TtfFont> fontFromAssetBundle(
String key, {
AssetBundle? bundle,
bool cache = true,
PdfBaseCache? pdfCache,
bool protect = false,
}) async {
bundle ??= rootBundle;
final data = await bundle.load(key);
return TtfFont(data);
final bytes = await bundle.load(key);
return TtfFont(bytes, protect: protect);
}
/// Load an image from an asset bundle key.
Future<ImageProvider> imageFromAssetBundle(String key,
[AssetBundle? bundle]) async {
Future<ImageProvider> imageFromAssetBundle(
String key, {
AssetBundle? bundle,
bool cache = true,
PdfImageOrientation? orientation,
double? dpi,
PdfBaseCache? pdfCache,
}) async {
bundle ??= rootBundle;
final data = await bundle.load(key);
return MemoryImage(data.buffer.asUint8List());
}
final HttpClient _sharedHttpClient = HttpClient();
/// Store network images in a cache
abstract class PdfBaseImageCache {
/// Create a network image cache
const PdfBaseImageCache();
/// The default cache used when none specified
static final defaultCache = PdfMemoryImageCache();
/// Add an image to the cache
Future<void> add(String key, Uint8List bytes);
/// Retrieve an image from the cache
Future<Uint8List?> get(String key);
/// Does the cache contains this image?
Future<bool> contains(String key);
final bytes = await bundle.load(key);
/// Remove an image from the cache
Future<void> remove(String key);
/// Clear the cache
Future<void> clear();
}
/// Memory image cache
class PdfMemoryImageCache extends PdfBaseImageCache {
/// Create a memory image cache
PdfMemoryImageCache();
final _imageCache = <String, Uint8List>{};
@override
Future<void> add(String key, Uint8List bytes) async {
_imageCache[key] = bytes;
}
@override
Future<Uint8List?> get(String key) async {
return _imageCache[key];
}
@override
Future<void> clear() async {
_imageCache.clear();
}
@override
Future<bool> contains(String key) async {
return _imageCache.containsKey(key);
}
@override
Future<void> remove(String key) async {
_imageCache.remove(key);
}
return MemoryImage(
bytes.buffer.asUint8List(bytes.offsetInBytes, bytes.lengthInBytes),
orientation: orientation,
dpi: dpi,
);
}
/// Download an image from the network.
... ... @@ -146,28 +104,15 @@ Future<ImageProvider> networkImage(
Map<String, String>? headers,
PdfImageOrientation? orientation,
double? dpi,
PdfBaseImageCache? imageCache,
PdfBaseCache? pdfCache,
}) async {
imageCache ??= PdfBaseImageCache.defaultCache;
if (cache && await imageCache.contains(url)) {
return MemoryImage((await imageCache.get(url))!,
orientation: orientation, dpi: dpi);
}
final request = await _sharedHttpClient.getUrl(Uri.parse(url));
headers?.forEach((String name, String value) {
request.headers.add(name, value);
});
final response = await request.close();
final builder = await response.fold(
BytesBuilder(), (BytesBuilder b, List<int> d) => b..add(d));
final List<int> data = builder.takeBytes();
final bytes = Uint8List.fromList(data);
if (cache) {
await imageCache.add(url, bytes);
}
pdfCache ??= PdfBaseCache.defaultCache;
final bytes = await pdfCache.resolve(
name: url,
uri: Uri.parse(url),
cache: cache,
headers: headers,
);
return MemoryImage(bytes, orientation: orientation, dpi: dpi);
}
... ...
/*
* Copyright (C) 2017, David PHAM-VAN <dev.nfet.net@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
/// Store data in a cache
abstract class PdfBaseCache {
/// Create a cache
const PdfBaseCache();
/// The default cache used when none specified
static final defaultCache = PdfMemoryCache();
/// Add some data to the cache
Future<void> add(String key, Uint8List bytes);
/// Retrieve some data from the cache
Future<Uint8List?> get(String key);
/// Does the cache contains this data?
Future<bool> contains(String key);
/// Remove some data from the cache
Future<void> remove(String key);
/// Clear the cache
Future<void> clear();
/// Download the font
Future<Uint8List?> _download(
Uri uri, {
Map<String, String>? headers,
}) async {
final response = await http.get(uri, headers: headers);
if (response.statusCode != 200) {
return null;
}
return response.bodyBytes;
}
/// Resolve the data
Future<Uint8List> resolve({
required String name,
required Uri uri,
bool cache = true,
Map<String, String>? headers,
}) async {
if (cache && await contains(name)) {
return (await get(name))!;
}
final bytes = await _download(uri, headers: headers);
if (bytes == null) {
throw FlutterError('Unable to find the asset $name');
}
if (cache) {
await add(name, bytes);
}
return bytes;
}
}
/// Memory cache
class PdfMemoryCache extends PdfBaseCache {
/// Create a memory cache
PdfMemoryCache();
final _imageCache = <String, Uint8List>{};
Timer? _timer;
void _resetTimer() {
_timer?.cancel();
_timer = Timer(const Duration(minutes: 20), () {
clear();
});
}
@override
Future<void> add(String key, Uint8List bytes) async {
_imageCache[key] = bytes;
_resetTimer();
}
@override
Future<Uint8List?> get(String key) async {
_resetTimer();
return _imageCache[key];
}
@override
Future<void> clear() async {
_imageCache.clear();
}
@override
Future<bool> contains(String key) async {
return _imageCache.containsKey(key);
}
@override
Future<void> remove(String key) async {
_imageCache.remove(key);
}
}
/// Store network images in a cache
@Deprecated('Use PdfBaseCache instead')
abstract class PdfBaseImageCache extends PdfBaseCache {
/// Create a network image cache
const PdfBaseImageCache();
/// The default cache used when none specified
static final defaultCache = PdfBaseCache.defaultCache;
}
/// Memory image cache
@Deprecated('Use PdfMemoryCache instead')
class PdfMemoryImageCache extends PdfMemoryCache {
/// Create a memory image cache
PdfMemoryImageCache();
}
... ...
import 'package:flutter/services.dart';
import 'package:pdf/widgets.dart';
import '../cache.dart';
import 'manifest.dart';
/// Downloadable font object
class DownloadbleFont {
/// Create a downloadable font object
const DownloadbleFont(this.url, this.name);
/// The Url to get the font from
final String url;
/// The Font filename
final String name;
/// The cache to use
static var cache = PdfBaseCache.defaultCache;
/// Get the font to use in a Pdf document
Future<TtfFont> getFont({
PdfBaseCache? pdfCache,
bool protect = false,
Map<String, String>? headers,
String assetPrefix = 'google_fonts/',
AssetBundle? bundle,
bool cache = true,
}) async {
final asset = '$assetPrefix$name.ttf';
if (await AssetManifest.contains(asset)) {
bundle ??= rootBundle;
final data = await bundle.load(asset);
return TtfFont(
data,
protect: protect,
);
}
pdfCache ??= PdfBaseCache.defaultCache;
final bytes = await pdfCache.resolve(
name: name,
uri: Uri.parse(url),
headers: headers,
cache: cache,
);
return TtfFont(
bytes.buffer.asByteData(bytes.offsetInBytes, bytes.lengthInBytes),
protect: protect,
);
}
}
... ...
This diff could not be displayed because it is too large.
import 'dart:async';
import 'dart:convert';
import 'package:flutter/services.dart';
/// Application asset manifest.
mixin AssetManifest {
static final _assets = <String>[];
static final _mutex = Mutex();
static bool _ready = false;
static bool _failed = false;
/// Does is contains this key?
static Future<bool> contains(String key) async {
if (_failed) {
return false;
}
await _mutex.acquire();
try {
if (!_ready) {
try {
final jsonString = await rootBundle.loadString('AssetManifest.json');
final jsonData = json.decode(jsonString) as Map<String, dynamic>;
_assets.addAll(jsonData.keys);
} catch (e) {
print('Error loading AssetManifest.json $e');
rootBundle.evict('AssetManifest.json');
_failed = true;
_ready = true;
return false;
}
_ready = true;
}
} finally {
_mutex.release();
}
return _assets.contains(key);
}
}
/// Simple Mutex
class Mutex {
final _waiting = <Completer>[];
bool _locked = false;
/// Lock the mutex
Future<void> acquire() async {
if (_locked) {
final c = Completer<void>();
_waiting.add(c);
await c.future;
}
_locked = true;
}
/// Release the mutex
void release() {
_locked = false;
for (final e in _waiting) {
e.complete();
}
}
}
... ...
... ... @@ -7,7 +7,7 @@ description: >
homepage: https://github.com/DavBfr/dart_pdf/tree/master/printing
repository: https://github.com/DavBfr/dart_pdf
issue_tracker: https://github.com/DavBfr/dart_pdf/issues
version: 5.3.0
version: 5.4.0
environment:
sdk: ">=2.12.0 <3.0.0"
... ... @@ -19,6 +19,7 @@ dependencies:
sdk: flutter
flutter_web_plugins:
sdk: flutter
http: ^0.13.0
image: ^3.0.1
js: ^0.6.3
meta: ^1.3.0
... ...
import 'dart:convert';
import 'dart:io';
String _capitalize(String s) {
if (s.isEmpty) return s;
return '${s[0].toUpperCase()}${s.substring(1)}';
}
String _uncapitalize(String s) {
if (s.isEmpty) return s;
return '${s[0].toLowerCase()}${s.substring(1)}';
}
void main(List<String> args) async {
final f = File('fonts.json');
final d = StringBuffer();
if (f.existsSync()) {
d.write(await f.readAsString());
} else {
final key = args[0];
final http = HttpClient();
print('Downloading...');
final q = await http.getUrl(Uri.parse(
'https://content-webfonts.googleapis.com/v1/webfonts?key=$key'));
final r = await q.close();
await for (final c in r.transform(utf8.decoder)) {
d.write(c);
}
await f.writeAsString(d.toString());
}
print('Converting...');
final Map m = json.decode(d.toString());
final output =
await File('../printing/lib/src/fonts/gfonts.dart').openWrite();
output.writeln('import \'package:pdf/widgets.dart\';');
output.writeln('');
output.writeln('import \'font.dart\';');
output.writeln('');
output.writeln('/// Google Fonts');
output.writeln('class PdfGoogleFonts extends DownloadbleFont {');
output.writeln('');
output.writeln('/// Create a Google Font');
output.writeln(
'const PdfGoogleFonts._(String url, String name) : super(url, name);');
for (final f in m['items']) {
final family = _uncapitalize(f['family'].replaceAll(' ', ''));
for (final s in f['files'].entries) {
var sub = _capitalize(s.key);
sub = sub.replaceAll('100', 'Thin ');
sub = sub.replaceAll('200', 'ExtraLight ');
sub = sub.replaceAll('300', 'Light ');
sub = sub.replaceAll('400', 'Regular ');
sub = sub.replaceAll('500', 'Medium ');
sub = sub.replaceAll('600', 'SemiBold ');
sub = sub.replaceAll('700', 'Bold ');
sub = sub.replaceAll('800', 'ExtraBold ');
sub = sub.replaceAll('900', 'Black ');
sub = sub.split(' ').map<String>((String e) => _capitalize(e)).join('');
final name = _capitalize(family) + '-' + sub;
output.writeln('');
output.writeln('/// ${f['family']} ${s.key}');
output.writeln('static Future<Font> $family$sub() {');
output.writeln(
'const font = PdfGoogleFonts._(\'${s.value}\', \'$name\',);');
output.writeln('return font.getFont();');
output.writeln('}');
}
}
for (final entry in <String, String>{
'MaterialIcons':
'https://fonts.gstatic.com/s/materialicons/v90/flUhRq6tzZclQEJ-Vdg-IuiaDsNZ.ttf'
}.entries) {
output.writeln('');
output.writeln('/// ${entry.key}');
output.writeln('static Future<Font> ${_uncapitalize(entry.key)}() {');
output.writeln(
'const font = PdfGoogleFonts._(\'${entry.value}\', \'${entry.key}\',);');
output.writeln('return font.getFont();');
output.writeln('}');
}
output.writeln('}');
await output.close();
print('Done');
}
... ...