Angular 7 шрифт AmCharts PDF - PullRequest
       12

Angular 7 шрифт AmCharts PDF

4 голосов
/ 06 февраля 2020

Я использую Angular 7 и AmCharts, и я экспортирую PDF (используя прилагаемую библиотеку pdfmake), но я не могу изменить шрифт по умолчанию для сгенерированного PDF. Я уже создал vfs_fonts.js с нужными мне шрифтами и включил его в мой angular.json в разделе scripts. В компоненте, где я генерирую отчет, я делаю следующее:

let pdfMake = res[0];

pdfMake.fonts = {
    "Open Sans": {
        "normal": 'OpenSans-Regular.ttf',
        "bold": 'OpenSans-Bold.ttf',
        "italics": 'OpenSans-Italic.ttf',
        "bolditalics": 'OpenSans-BoldItalic.ttf'
    }
}

let doc = {
    header: this.drawHeader(),
    footer: this.drawFooter(),
    pageSize: "A4",
    pageOrientation: "portrait",
    pageMargins: [40, 80, 40, 60],
    content: [],
    defaultStyle: {
        font: "Open Sans"
    }
};

Но я получаю следующую ошибку:

ERROR Error: Uncaught (in promise): Error: Font 'Open Sans' in style 'normal' is not defined in the font section of the document definition.
Error: Font 'Open Sans' in style 'normal' is not defined in the font section of the document definition

Как я могу решить? Спасибо

Ответы [ 4 ]

2 голосов
/ 13 февраля 2020

Я столкнулся с той же проблемой при изменении шрифтов библиотеки pdf make. Я решил проблему, используя следующие шаги.

Изначально загрузите файл .ttf необходимых шрифтов.

Шаг 1)
Go на следующий путь -> node_modules -> pdfmake

Шаг 2)
В каталоге pdfmake создайте папку с именем examples . Go до примеров папки и внутри нее создайте имя папки шрифты .

Шаг 3) Теперь вставьте загруженные файлы .ttf в папку шрифтов. (path_to_paste_ttf_File: node_modules / pdfmake / examples / fonts)

После вставки файлов в эту папку. Перейдите к шаг 4

Шаг 4)
Go к пути к папке шрифтов, как указано в терминале. Смотрите ниже изображение пути к терминалу для справки. enter image description here

Шаг 5)
После завершения step4 , ром gulb buildFonts. Ваш вывод будет похож на изображение ниже. enter image description here

Как только вы успешно создадите свои шрифты.
Go в ваш файл .ts вашего компонента, где вы генерируете pdf.

Внутри .ts file

import pdfMake from 'pdfmake/build/pdfmake';
import pdfFonts from 'pdfmake/build/vfs_fonts';
pdfMake.vfs = pdfFonts.pdfMake.vfs;


async generatePdf(value){
        pdfMake.fonts = {
            Roboto: {
                normal: 'Roboto-Regular.ttf',
                bold: 'Roboto-Medium.ttf',
                italics: 'Roboto-Italic.ttf',
                bolditalics: 'Roboto-MediumItalic.ttf'
            }
        };
        const documentDefinition = await this.getDocumentDefinition();
        pdfMake.createPdf(documentDefinition).open();
    }
    getDocumentDefinition(){
        return {
            content: (...),
              defaultStyle: {
                    font: 'yourFontName' //in this case it's roboto.
            }
        }               
    }

Это решило мою проблему и дало мне знать, если у вас возникнут какие-либо трудности.
Для получения дополнительной информации см. официальный документ

0 голосов
/ 15 февраля 2020

Вы можете использовать ту же библиотеку pdfmake, что и amcharts, но вам нужно сгенерировать шрифт, как упомянуто в ответе выше, используя gulp после установки npm, была проблема с обещанием, поэтому я заменил синтаксис просто для быстрого обхода, поэтому вам нужно go в папку pdfmake, расположенную в node_modules вашего проекта root, и запустить npm install, после чего вы создаете папку fonts в каталоге примеров -> examples / fonts и копируете туда свои шрифты ttf, затем запускаете gulp файл, который даст вам vfs_fonts. js файл.

Вам нужно отредактировать этот файл и изменить

this.pdfMake = this.pdfMake || {}; pdfMake.vfs = {}

до

export const pdfMake = {vfs: {}}; как на этом скриншоте: enter image description here

и соответствующим образом завершите ключ следующим образом:

enter image description here вместо этого };

, после этого вы можете проверить мой полный рабочий код со шрифтом montserrat, который я сделал с помощью демонстрационного содержимого amchart ниже:

import { Component, NgZone } from "@angular/core";
import * as am4core from "@amcharts/amcharts4/core";
import * as am4charts from "@amcharts/amcharts4/charts";
import am4themes_animated from "@amcharts/amcharts4/themes/animated";
import am4themes_dataviz from "@amcharts/amcharts4/themes/dataviz";

import * as pdfFontsVfs from "../../node_modules/pdfmake/build/vfs_fonts";
am4core.useTheme(am4themes_animated);

// Themes
am4core.useTheme(am4themes_animated);
am4core.useTheme(am4themes_dataviz);

@Component({
  selector: "app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})

export class AppComponent {
  private chart: am4charts.XYChart;
  private chart2: am4charts.XYChart;
  private chart3: am4charts.XYChart;
  private chart4: am4charts.PieChart;
  constructor(private zone: NgZone) {}

  ngAfterViewInit() {
    this.zone.runOutsideAngular(() => {

    /**
     * Chart 1
     */

    // Create chart instance
    var chart = am4core.create("chartdiv", am4charts.XYChart);

    // Add data
    chart.data = [{
      "date": new Date(2018, 0, 1),
      "value": 450,
      "value2": 362,
      "value3": 699
    }, {
      "date": new Date(2018, 0, 2),
      "value": 269,
      "value2": 450,
      "value3": 841
    }, {
      "date": new Date(2018, 0, 3),
      "value": 700,
      "value2": 358,
      "value3": 699
    }, {
      "date": new Date(2018, 0, 4),
      "value": 490,
      "value2": 367,
      "value3": 500
    }, {
      "date": new Date(2018, 0, 5),
      "value": 500,
      "value2": 485,
      "value3": 369
    }, {
      "date": new Date(2018, 0, 6),
      "value": 550,
      "value2": 354,
      "value3": 250
    }, {
      "date": new Date(2018, 0, 7),
      "value": 420,
      "value2": 350,
      "value3": 600
    }];

    // Create axes
    var categoryAxis = chart.xAxes.push(new am4charts.DateAxis());
    categoryAxis.renderer.grid.template.location = 0;
    categoryAxis.renderer.labels.template.disabled = true;
    categoryAxis.renderer.minGridDistance = 30;

    var valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
    valueAxis.renderer.labels.template.disabled = true;



    this.createSeries("value", "Series #1",chart);
    this.createSeries("value2", "Series #2",chart);
    this.createSeries("value3", "Series #3",chart);
    this.chart = chart;

    /**
     * Chart 2
     */

    // Create chart instance
    var chart2 = am4core.create("chartdiv2", am4charts.XYChart);
    chart2.paddingBottom = 25;

    // Add data
    chart2.data = [{
      "country": "USA",
      "visits": 3025
    }, {
      "country": "China",
      "visits": 1882
    }, {
      "country": "Japan",
      "visits": 1809
    }, {
      "country": "Germany",
      "visits": 1322
    }, {
      "country": "UK",
      "visits": 1122
    }, {
      "country": "France",
      "visits": 1114
    }, {
      "country": "India",
      "visits": 984
    }];

    // Create axes
    var categoryAxis2 = chart2.xAxes.push(new am4charts.CategoryAxis());
    categoryAxis2.dataFields.category = "country";
    categoryAxis2.renderer.grid.template.location = 0;
    categoryAxis2.renderer.minGridDistance = 30;
    categoryAxis2.renderer.labels.template.disabled = true;

    var valueAxis = chart2.yAxes.push(new am4charts.ValueAxis());
    valueAxis.renderer.labels.template.disabled = true;

    // Create series
    var series = chart2.series.push(new am4charts.ColumnSeries());
    series.sequencedInterpolation = true;
    series.dataFields.valueY = "visits";
    series.dataFields.categoryX = "country";
    series.columns.template.strokeWidth = 0;

    series.columns.template.column.cornerRadiusTopLeft = 10;
    series.columns.template.column.cornerRadiusTopRight = 10;

    // on hover, make corner radiuses bigger
    var hoverState = series.columns.template.column.states.create("hover");
    hoverState.properties.cornerRadiusTopLeft = 0;
    hoverState.properties.cornerRadiusTopRight = 0;
    hoverState.properties.fillOpacity = 1;

    series.columns.template.adapter.add("fill", (fill, target)=>{
      return chart.colors.getIndex(target.dataItem.index);
    });
    this.chart2 = chart2;
    /**
     * Chart 3
     */

    // Create chart instance
    var chart3 = am4core.create("chartdiv3", am4charts.XYChart);
    chart3.paddingBottom = 25;

    // Add percent sign to all numbers
    chart3.numberFormatter.numberFormat = "#.3'%'";

    // Add data
    chart3.data = [{
        "country": "USA",
        "year2004": 3.5,
        "year2005": 4.2
    }, {
        "country": "UK",
        "year2004": 1.7,
        "year2005": 3.1
    }, {
        "country": "Canada",
        "year2004": 2.8,
        "year2005": 2.9
    }, {
        "country": "Japan",
        "year2004": 2.6,
        "year2005": 2.3
    }, {
        "country": "France",
        "year2004": 1.4,
        "year2005": 2.1
    }, {
        "country": "Brazil",
        "year2004": 2.6,
        "year2005": 4.9
    }];

    // Create axes
    var categoryAxis3 = chart3.xAxes.push(new am4charts.CategoryAxis());
    categoryAxis3.dataFields.category = "country";
    categoryAxis3.renderer.grid.template.location = 0;
    categoryAxis3.renderer.minGridDistance = 30;
    categoryAxis3.renderer.labels.template.disabled = true;

    var valueAxis3 = chart3.yAxes.push(new am4charts.ValueAxis());
    valueAxis3.renderer.labels.template.disabled = true;

    // Create series
    var series3 = chart3.series.push(new am4charts.ColumnSeries());
    series3.dataFields.valueY = "year2004";
    series3.dataFields.categoryX = "country";
    series3.clustered = false;

    series3.columns.template.column.cornerRadiusTopLeft = 10;
    series3.columns.template.column.cornerRadiusTopRight = 10;

    var series2 = chart3.series.push(new am4charts.ColumnSeries());
    series2.dataFields.valueY = "year2005";
    series2.dataFields.categoryX = "country";
    series2.clustered = false;
    series2.columns.template.width = am4core.percent(50);

    series2.columns.template.column.cornerRadiusTopLeft = 6;
    series2.columns.template.column.cornerRadiusTopRight = 6;
    this.chart3 = chart3;
    /**
     * Chart 4
     */

    // Create chart
    var chart4 = am4core.create("chartdiv4", am4charts.PieChart);
    chart4.padding(0, 0, 0, 0);

    chart4.data = [
      {
        country: "Lithuania",
        value: 260
      },
      {
        country: "Czech Republic",
        value: 230
      },
      {
        country: "Ireland",
        value: 200
      },
      {
        country: "Germany",
        value: 165
      },
      {
        country: "Australia",
        value: 139
      },
      {
        country: "Austria",
        value: 128
      }
    ];

    var series4 = chart4.series.push(new am4charts.PieSeries());
    series4.dataFields.value = "value";
    series4.dataFields.radiusValue = "value";
    series4.dataFields.category = "country";
    series4.slices.template.cornerRadius = 6;
    series4.colors.step = 3;
    series4.radius = am4core.percent(100);
    series4.labels.template.disabled = true;
    series4.ticks.template.disabled = true;
    this.chart4 = chart4;
    });
  }


  /**
   * Function that exports PDF
   */

  async savePDF() {

      var res = [];
      var fonts =  {
        "Montserrat": {
         normal: "Montserrat-Regular.ttf",
         bold: "Montserrat-Bold.ttf",
         italics: "Montserrat-Italic.ttf",
         bolditalics: "Montserrat-BoldItalic.ttf"
       }
     };
      var PdfMake = await this.chart.exporting.pdfmake;





      PdfMake.vfs = pdfFontsVfs.pdfMake.vfs;
      PdfMake.addVirtualFileSystem = pdfFontsVfs;
      PdfMake.fonts = fonts;

      res[1] = await this.chart.exporting.getImage("png");
      res[2] = await this.chart2.exporting.getImage("png");
      res[3] = await this.chart3.exporting.getImage("png");
      res[4] = await this.chart4.exporting.getImage("png");

      // pdfmake is ready

      // Create document template
      var doc = {
        pageSize: "A4",
        pageOrientation: "portrait",
        pageMargins: [30, 30, 30, 30],
        content: [],
        defaultStyle:{
          font: "Montserrat"
        }
      };

      doc.content.push({
        text: "In accumsan velit in orci tempor",
        fontSize: 20,
        bold: true,
        margin: [0, 20, 0, 15]
      });

      doc.content.push({
        text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed sem quam, sodales ac volutpat sed, vestibulum id quam. Sed quis arcu non elit fringilla mattis. Sed auctor mi sed efficitur vehicula. Sed bibendum odio urna, quis lobortis dui luctus ac. Duis eu lacus sodales arcu tincidunt ultrices viverra a risus. Vivamus justo massa, malesuada quis pellentesque ut, placerat in massa. Nunc bibendum diam justo, in consequat ipsum fringilla ac. Praesent porta nibh ac arcu viverra, at scelerisque neque venenatis. Donec aliquam lorem non ultrices ultrices. Aliquam efficitur eros quis tortor condimentum, id pellentesque metus iaculis. Aenean at consequat neque, a posuere lectus. In eu libero magna. Pellentesque molestie tellus nec nisi molestie, eu dignissim lacus tristique. Sed tellus nulla, suscipit a velit non, mattis dictum metus. Curabitur mi mi, convallis nec libero quis, venenatis vestibulum ante.",
        fontSize: 15,
        margin: [0, 0, 0, 15]
      });

      doc.content.push({
        text: "Aliquam lacinia justo",
        fontSize: 20,
        bold: true,
        margin: [0, 20, 0, 15]
      });

      doc.content.push({
        image: res[1],
        width: 530
      });

      doc.content.push({
        text: "Phasellus suscipit in diam a interdum",
        fontSize: 20,
        bold: true,
        margin: [0, 20, 0, 15]
      });

      doc.content.push({
        table: {
          headerRows: 1,
          widths: [ "*", "*", "*", "*" ],
          body: [
            [
              { text: "USA", bold: true },
              { text: "Japan", bold: true },
              { text: "France", bold: true },
              { text: "Mexico", bold: true }
            ],
            [ "2500", "2500", "2200", "1200" ],
            [ "800", "1200", "990", "708" ],
            [ "2100", "2150", "900", "1260" ],
          ]
        }
      });

      doc.content.push({
        text: "Duis sed efficitur mauris",
        fontSize: 20,
        bold: true,
        margin: [0, 20, 0, 15]
      });

      doc.content.push({
        columns: [{
          image: res[2],
          width: 250
        }, {
          image: res[3],
          width: 250
        }],
        columnGap: 30
      });

      doc.content.push({
        text: "Aliquam semper lacinia",
        fontSize: 20,
        bold: true,
        margin: [0, 20, 0, 15]
      });

      doc.content.push({
        columns: [{
          image: res[4],
          width: 150
        }, {
          stack: [{
            text: "Maecenas congue leo vel tortor faucibus, non semper odio viverra. In ac libero rutrum libero elementum blandit vel in orci. Donec sit amet nisl ac eros mollis molestie. Curabitur ut urna vitae turpis bibendum malesuada sit amet imperdiet orci. Etiam pulvinar quam at lorem pellentesque congue. Integer sed odio enim. Maecenas eu nulla justo. Sed quis enim in est sodales facilisis non sed erat. Aenean vel ornare urna. Praesent viverra volutpat ex a aliquet.",
            fontSize: 15,
            margin: [0, 0, 0, 15]
          }, {
       text: "Fusce sed quam pharetra, ornare ligula id, maximus risus. Integer dignissim risus in placerat mattis. Fusce malesuada dui ut lectus ultricies, et sollicitudin nisl placerat. In dignissim elit in pretium lobortis. Fusce ornare enim at metus laoreet, ut convallis elit lacinia. Maecenas pharetra aliquet mi. Nulla orci nunc, egestas id nisi ut, volutpat sollicitudin mi.",
        fontSize: 15,
        margin: [0, 0, 0, 15]
      }],
      width: "*"
    }],
    columnGap: 30
  });

    await  PdfMake.createPdf(doc,null, fonts,pdfFontsVfs.pdfMake.vfs).download("report.pdf");


  }

  // Create series
  createSeries(field, name, chart) {
    console.log(chart);
    var series = chart.series.push(new am4charts.LineSeries());
    series.dataFields.valueY = field;
    series.dataFields.dateX = "date";
    series.name = name;
    series.tooltipText = "{dateX}: [b]{valueY}[/]";
    series.strokeWidth = 3;

    var bullet = series.bullets.push(new am4charts.CircleBullet());
    bullet.circle.stroke = am4core.color("#fff");
    bullet.circle.strokeWidth = 3;
    bullet.circle.radius = 7;
  }
  ngOnDestroy() {
    this.zone.runOutsideAngular(() => {
      if (this.chart) {
        this.chart.dispose();
        ...dispose other charts
      }
    });
  }
}

и конфигурация webpack:

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: './src/main.ts',
    resolve: {
        extensions: ['.ts', '.js']
    },
    module: {
        rules: [
            {
                test: /\.ts$/,
                use: ['ts-loader', 'angular2-template-loader'],
                exclude: [ /node_modules/, /pdfmake.js$/ ]
            },
            {
                test: /\.(html|css)$/,
                use: 'raw-loader'
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({ template: './src/index.html' })
    ],

}

и html:

    <div class="main">

  <input type="button" value="Save as PDF" (click)="savePDF()" />

<h1>In accumsan velit in orci tempor</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed sem quam, sodales ac volutpat sed, vestibulum id quam. Sed quis arcu non elit fringilla mattis. Sed auctor mi sed efficitur vehicula. Sed bibendum odio urna, quis lobortis dui luctus ac. Duis eu lacus sodales arcu tincidunt ultrices viverra a risus. Vivamus justo massa, malesuada quis pellentesque ut, placerat in massa. Nunc bibendum diam justo, in consequat ipsum fringilla ac. Praesent porta nibh ac arcu viverra, at scelerisque neque venenatis. Donec aliquam lorem non ultrices ultrices. Aliquam efficitur eros quis tortor condimentum, id pellentesque metus iaculis. Aenean at consequat neque, a posuere lectus. In eu libero magna. Pellentesque molestie tellus nec nisi molestie, eu dignissim lacus tristique. Sed tellus nulla, suscipit a velit non, mattis dictum metus. Curabitur mi mi, convallis nec libero quis, venenatis vestibulum ante.</p>
<h2>Aliquam lacinia justo</h2>
<div id="chartdiv" class="chart"></div>

<h2>Phasellus suscipit in diam a interdum</h2>
<table>
  <tr>
    <th>USA</th>
    <th>Japan</th>
    <th>France</th>
    <th>Mexico</th>
  </tr>
  <tr>
    <td>2500</td>
    <td>1900</td>
    <td>2200</td>
    <td>1200</td>
  </tr>
  <tr>
    <td>800</td>
    <td>1200</td>
    <td>990</td>
    <td>708</td>
  </tr>
  <tr>
    <td>2100</td>
    <td>2150</td>
    <td>900</td>
    <td>1260</td>
  </tr>
</table>

<h2>Duis sed efficitur mauris</h2>
<div>
  <div class="col">
    <div id="chartdiv2" class="chart"></div>
  </div>
  <div class="col">
    <div id="chartdiv3" class="chart"></div>
  </div>
</div>

<br>
<h2>Aliquam semper lacinia</h2>
<div id="chartdiv4" class="chart"></div>
<p>Maecenas congue leo vel tortor faucibus, non semper odio viverra. In ac libero rutrum libero elementum blandit vel in orci. Donec sit amet nisl ac eros mollis molestie. Curabitur ut urna vitae turpis bibendum malesuada sit amet imperdiet orci. Etiam pulvinar quam at lorem pellentesque congue. Integer sed odio enim. Maecenas eu nulla justo. Sed quis enim in est sodales facilisis non sed erat. Aenean vel ornare urna. Praesent viverra volutpat ex a aliquet.</p>

<p>Fusce sed quam pharetra, ornare ligula id, maximus risus. Integer dignissim risus in placerat mattis. Fusce malesuada dui ut lectus ultricies, et sollicitudin nisl placerat. In dignissim elit in pretium lobortis. Fusce ornare enim at metus laoreet, ut convallis elit lacinia. Maecenas pharetra aliquet mi. Nulla orci nunc, egestas id nisi ut, volutpat sollicitudin mi.</p>

</div>

и пакет. json:

    {
    "name": "angular-7-tutorial",
    "version": "1.0.0",
    "scripts": {
        "start": "webpack-dev-server --mode development --open"
    },
    "dependencies": {
        "@amcharts/amcharts4": "^4.9.1",
        "@angular/common": "^7.2.13",
        "@angular/compiler": "^7.2.13",
        "@angular/core": "^7.2.13",
        "@angular/forms": "^7.2.13",
        "@angular/platform-browser": "^7.2.13",
        "@angular/platform-browser-dynamic": "^7.2.13",
        "@angular/router": "^7.2.13",
        "core-js": "^3.0.1",
        "rxjs": "^6.4.0",
        "zone.js": "^0.9.0"
    },
    "devDependencies": {
        "@types/node": "^11.13.5",
        "angular2-template-loader": "^0.6.2",
        "html-webpack-plugin": "^3.2.0",
        "raw-loader": "^1.0.0",
        "ts-loader": "^5.3.3",
        "typescript": "^3.4.4",
        "webpack": "^4.30.0",
        "webpack-cli": "^3.3.0",
        "webpack-dev-server": "^3.3.1"
    }
}

и структура папок: enter image description here

Что я понял, так это конфликт синтаксиса и обещание неразрешенной ошибки, препятствующей тому, чтобы диаграммы не загружали пользовательские шрифты. Если вы увидите файл amcharts vs_fonts по умолчанию, вы увидите синтаксис экспорта по умолчанию, который мог вызвать проблему. Во-вторых, проверьте файлы шрифтов, если они не повреждены. Надеюсь, что это поможет вам. Спасибо.

0 голосов
/ 14 февраля 2020

Это может быть связано с пробелами в Open Sans. Попробуйте изменить его на OpenSans

0 голосов
/ 12 февраля 2020

Pdfmake документация о том, как использовать пользовательские шрифты на стороне клиента здесь .


Формат файла vfs_fonts. js выглядит примерно так:

this.pdfMake = this.pdfMake || {}; this.pdfMake.vfs = {
  "Roboto-Italic.ttf": "AAEAAAASAQAABAAgR0RFRtRX1"
}

Следовательно, вы должны определить его следующим образом:

window.pdfMake.vfs["OpenSans-Regular.ttf"] = "BASE 64 HERE";
window.pdfMake.vfs["OpenSans-Bold.ttf"] = "BASE 64 HERE";
window.pdfMake.vfs["OpenSans-Italic.ttf"] = "BASE 64 HERE";
window.pdfMake.vfs["OpenSans-BoldItalic.ttf"] = "BASE 64 HERE";

После этого вам все еще нужно назначить pdfMake.fonts:

Открыть без изменений: https://fonts.google.com/specimen/Open+Sans?selection.family=Open+Sans: 400,400i, 600i, 700

pdfMake.fonts = {
    // Make sure you define all 4 components - normal, bold, italics, bolditalics - (even if they all point to the same font file)
    "Open Sans": {  <======================= TRY WITH "OpenSans" too
        "normal": 'OpenSans-Regular.ttf',
        "bold": 'OpenSans-Bold.ttf',
        "italics": 'OpenSans-Italic.ttf',
        "bolditalics": 'OpenSans-BoldItalic.ttf'
    }
};

Другое возможное решение: (не уверен, работает ли оно)

let pdfMake = res[0];
let pdfFonts = require('./vfs_fonts');

pdfMake.addVirtualFileSystem(pdfFonts);
...