-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhershey-text.ulp
898 lines (824 loc) · 32.1 KB
/
hershey-text.ulp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
#usage "<b>Render Text in a Hershey Typeface Using Wires</b>\n"
"<p>"
"In a board or package, group the text to render then run this ULP. \n"
"A menu dialog box lets you set the Hershey typeface file, wire (stroke) "
"width, slope and character width adjustment, the output layer or "
"layer offset and whether to save this as a package in a local library."
"<p>"
"See Help in the ULP for more information."
"<p>"
"Edit the ULP file to preset your preferred ULP dialog box options"
"<p>"
"Get the latest version of ULP and fonts from https://github.com/nallison/hershey-text-eagle"
"<p>"
"Version 1.4.0 (2014-06-05)"
"<p>"
"<author>Author: neil@avon-tech-solutions.co.nz</author>"
/* This is free software: you are free to change and redistribute it.
* THIS PROGRAM IS PROVIDED AS IS AND WITHOUT WARRANTY OF ANY KIND,
* EXPRESSED OR IMPLIED */
/* The data in this package is covered by the following licenses:
* - GNU GENERAL PUBLIC LICENSE Version 2
* - Hershey fonts (hershey-fonts/*.jhf) license
*
* Refer to the file COPYING in the hershey-fonts directory
*/
/*****************************************************************************
* Preset adjustable preferences - adjust these to pre-load the menu defaults
* Use 0 = no/false and 1 = yes/true
*****************************************************************************/
string face_filename = filedir(argv[0]) + "hershey-fonts/eagles.jhf";
/* Typeface file location: hershey-fonts sub-directory is normally
* in the ULP directory containing hershey-text.ulp
*/
int is_use_ratios = 1; /* Use the ratio of each text separately
* rather than a common stroke width */
real stroke_width_user = 0.2; /* Stroke width in mm if not using ratios */
real slope_degrees = 0.0;
real width_factor = 1.0; /* Factor for narrowing / expanding font */
int is_fixed_output_layer = 0; /* Put all output on the same layer if 1 */
int fixed_output_layer = 21; /* tPlace is a common default suggestion */
int layer_offset = 0; /* 0 = output layer is same as input layer */
int is_make_package = 1; /* 0 = render in current pac or pcb,
* 1 = make package in library */
int is_edit_script = 0; /* View/edit script before it is executed */
int is_undo_log_off = 1;
char unknown_character = '?'; /* Printed if glyph is not defined in font */
/******************************************************************************/
string HelpText()
{ /* Only in a function so text editor folding can hide it ;) */
return
"<html>\n"
"<h1>Hershey Text for Eagle</h1>"
"<h3>Function</h3>"
"<blockquote>"
"Renders grouped text in a Hershey typeface using wires."
"<br>"
"Runs in a board or package."
"</blockquote>"
"<p>"
"<h3>Syntax</h3>"
"<p>"
"<blockquote><tt>RUN hershey-text [font] [width] [layer] [no_pac]</tt></blockquote>"
"<p>"
"<blockquote>"
"<tt>font </tt> font filename without path or extension, e.g. <tt>eagles</tt> "
" or <tt>rowmanc</tt>.<br>"
"<tt>width </tt> wire (stroke) width in mm. Put 0 to use text ratio to set wire stroke."
" Put 0.0 for a zero wire width.<br>"
"<tt>layer </tt> the layer the text is rendered in<br>"
"<tt>no_pac </tt> any value for this argument means the text is rendered directly in the editor."
"</blockquote>"
"<blockquote>"
"<b>Command Line Options</b><br>"
"If any arguments are supplied to the command line then the ULP runs without "
"displaying the menu and uses the remaining default settings."
"</blockquote>"
"<p>"
"<h2>Menu Options</h2>"
"When run from a <i>board editor</i>, the default options will add a library "
"package with the text rendered using a wire width calculated from the text height "
"and ratio on the same layer as the text. It will have the same "
"mirroring, spin and rotation as the text and is ADDed to the board at the text "
"origin. A copy of the text is placed in the package on layer 48 Document."
"<p>"
"When run from a <i>package editor</i>, the default options create the rendered "
"wires in the current package on the same layer and location as the text, "
"and preserves spin, mirroring and rotation that the text has."
"<p>"
"The default values for the menu are set by variables at the start of the "
"ULP code and may be adjusted to suit the values your prefer to be loaded "
"into the dialog box when the ULP is run."
"<p>"
"<h3>Font rendering characteristics</h3>"
"<p>"
"<ul>"
"<li> Choose the font data file to use. The <tt>/hershey-fonts/</tt> "
"directory is normally installed in the ULP folder and this is "
"where it will commence browsing for other files."
"</li>"
"<li> Use ratio to calculate wire width for each text instance rather than "
"using the fixed wire width for everything. This is intended for "
"when there are several different size and ratio text objects grouped "
"together."
"</li>"
"<li> Choose the wire width units of mm or mil. This setting is ignored if "
"the Use ratio check box is set."
"</li>"
"<li> Set the wire width in mm or mil. Values in the range 4% to 15% of the text "
"height often give acceptable results. This setting is ignored if "
"the Use ratio check box is set."
"</li>"
"<li> Set the slope of the text in degrees. A negative slope makes "
"the text lean backwards (to the left) while a positive slope of "
"15° imitates <i>italicised</i> fonts."
"</li>"
"<li> The width factor provides for condensed (<1) and expanded (>1) "
"type."
"</li>"
"</ul>"
"<h3>Output Layer(s)</h3>"
"<p>"
"<ul>"
"<li> Render all group texted on this layer forces the rendered wires to "
"be placed on this layer - the layer(s) of the input text are ignored."
"</li>"
"<li> Render text on separate layers using offset reads the layer of each "
"text item in the group and places the wires on that layer plus "
"the offset provided. Choosing 0 puts it on the same layer while "
"using 100 puts the Hershey text in a user layer (Text.layer + 100)."
"</li>"
"</ul>"
"<h3>Options and Information</h3>"
"<p>"
"<ul>"
"<li> Setting \"Make a Package in library\" renders the text in a library "
"saved in the same directory as the board. The package name is the "
"first 8 characters of the text followed by sequence, date and time "
"information to help reduce \"package already exists\" errors. "
"This setting is ignored if the ULP is run from a package editor."
"</li>"
"<li> View and edit script before running opens a dialog so you can review "
"and optionally edit the script before it is executed."
"</li>"
"<li> Turn UNDO Log off while script is running helps improve the speed at "
"which the script is executed."
"</li>"
"<li> Use the each text's ratio to calculate the wire width separately "
"for each text instance selected in the group."
"</li>"
"<li> For information, the number of text items found in the group is "
"displayed."
"</li>"
"</ul>"
"<hr>"
"<h2>Why use Hershey Text for Eagle</h2>"
"Have you prepared your pcb <i>artwork</i> and wanted to have some text in a "
"typeface that stands out or is more <i>artistic</i> than the default vector "
"font and yet still WYSIWYG in the CAM outputs?"
"<p>"
"This ULP renders text in typefaces originally created by Dr A. V. Hershey "
"while working at the U. S. National Bureau of Standards. The format of the "
"font data in this distribution was originally created by James Hurt and "
"modified by Kamal Mostafa <a href=\"http://www.whence.com/hershey-fonts/\"> "
"http://www.whence.com/hershey-fonts/</a>."
"<p>"
"The plain text .jhf font data files may be created or modified to create new "
"character <i>glyphs</i>. For example the \"Eagle Simplex\" "
"<tt>eagles.jhf</tt> font is based on <tt>futural.jhf</tt> with °, "
"±, µ, Ø and <i>omega</i> (ASCII character codes 176, 177, 181, "
" 216, 219 respectively) added."
"<p>"
"<p>"
"<hr>"
"<h3>Latest Verision</h3>"
"The latest version is available from <a href=https://github.com/nallison/hershey-text-eagle>"
"https://github.com/nallison/hershey-text-eagle</a>"
"<p>"
"<h3>More Information</h3>"
"More information, including example board files demonstrating some output, "
"is at <a href=\"http://www.avon-tech-solutions.co.nz/hershey.html\"> "
"Avon Technical Solutions Ltd</a>."
"<p>"
"© 2014 Neil Allison, Avon Technical Solutions Limited"
"<p>"
"</html>";
}
/* Global variables */
string version = "1.4.0"; /* 2014-06-05 */
string h;
string cmd;
string cmd_insert; /* Hold sequence of script commands to insert library packages */
string script_filename; /* brd or lib .name + "_Hershey.scr in brd or lib directory */
string library_filename; /* brd.name + "_Hershey.lbr" in brd directory */
string lib_package;
int layer_output = fixed_output_layer;
int defined_brd_layer[];
int defined_pac_layer[];
int text_count = 0; /* Used as part of package name to help provide unique names */
int MAX_CHARS_IN_PAC_NAME = 8; /* First n chars to use in package name */
int MAX_GLYPHS = 224; /* Max number of glyphs a jhf file can contain */
int MIN_CHARS_IN_GLYPH = 10; /* Valid glyphs are at least 10 chars long */
int FIRST_CHAR = 32; /* First printable character, offset into glyph data array*/
string hershey_data_lines[]; /* .jhf Hershey font file data, one glyph per line */
int glyph_count = 0;
int GLYPH_SCALE_FACTOR = 21; /* 21 units from base to top of capital letter */
int glyph_y_adjust = 9; /* TODO Get base and top from letter 'E' and calculate this */
real shear_factor = 0.0; /* 0 is 0° (vertical), 0.268 is 15°, 1 is 45°, tan(angle°) */
int is_apply_spin = 0;
int is_apply_mirror = 0;
real hershey_length = 0;
enum {UNIT_MM, UNIT_MIL};
int stroke_units = UNIT_MM;
real MIL_TO_MM = 0.0254;
/* Choose correct internal units: 1/320000 mm for v6, 0.1um for <v6 */
int MM_TO_UNITS;
if (EAGLE_VERSION < 6) {
MM_TO_UNITS = 10000;
}
else {
MM_TO_UNITS = 320000;
}
void output_header(string source)
{
sprintf(h, "# Script created: %s\n"
+ "# Exported from : %s\n"
+ "# ULP Name : %s\n"
+ "# ULP Version : %s\n"
+ "# CAD Version : %s \n\n",
t2string(time(),"yyyy-MM-dd hh:mm:ss"), source, argv[0],
version, EAGLE_SIGNATURE);
cmd += h;
if (is_undo_log_off) cmd += "SET UNDO_LOG OFF;\n";
cmd += "set wire_bend 2;\n";
cmd += "GRID mm;\n";
}
void output_footer(void)
{
cmd += "SET UNDO_LOG ON;\n";
cmd += "GRID LAST;\n";
}
string package_text_abbrev(string s)
{
int len = min(strlen(s), MAX_CHARS_IN_PAC_NAME);
string temp;
temp = strsub(s, 0, len);
for (int pos = 0; pos < len; pos++) {
if (!isalnum(temp[pos])) {
temp[pos] = '_';
}
}
return temp;
}
void set_default_layers_as_defined(void)
{
for (int lay = 1; lay <=52; lay++) {
defined_brd_layer[lay] = 1;
defined_pac_layer[lay] = 1;
}
defined_brd_layer[50] = 0;
defined_pac_layer[50] = 0;
}
void update_used_layers(UL_LAYER L)
{
/* Add definitions of additional used layers so don't rename these */
if (L.used) {
if (board) defined_brd_layer[L.number] = 1;
if (package) defined_pac_layer[L.number] = 1;
}
}
void ensure_defined_layer(int lay)
{
/* There is currently no (easy) mechanism to check and add layers in a library
* when creating a package from a board.
* So user will have to create layers in response to undefined layer error
* dialog boxes.
* A kludge could have the HT font package library have ALL layers 1-255 and
* then after adding to the board (and therefore adding all layers to the
* board) run a loop with "LAYERS ?? -n" to delete the empty layers.
*/
/* This works for when not making a package from the board (see above)
* and rendering Hershey Text in a package.
*/
if ((board && !defined_brd_layer[lay])
|| (package && !defined_pac_layer[lay])) {
sprintf(h, "Layer %d %d_Hershey_Text_Defined;\n", lay, lay);
cmd += h;
if (board) defined_brd_layer[lay] = 1;
if (package) defined_pac_layer[lay] = 1;
}
}
int import_hershey_data()
{
/* Reads a modified Hershey Glyph data file into a global array.
* Returns the number of lines read as integer.
* No checking is performed, jhf files are assumed correct. This requires:
* - .jhf files to be modified so that there are no linebreaks in a glyph.
* See, for example, http://www.whence.com/hershey-fonts/
* https://github.com/kamalmostafa/hershey-fonts
* - First character (=first array element) defined is ASCII 32 (space).
* - Maximum of 224 glyphs (MAX_GLYPHS) per data file (usually only 95, up to
* ASCII 127 (~). Any text beyond line 224 in the .jhf file is ignored.
* - Unused glyphs prior to the last "real" glyph must be present e.g. as a
* valid >=10 char "dummy" glyph to make the ANSI ASCII indexing work.
* - Lines between the last "real" glyph and line 224 may be blank
* - No comment or other text lines prior to line 224.
*/
string raw_data_lines[];
int nlines = 0;
int nglyphs = 0;
int max_data_rows = 0;
nlines = fileread(raw_data_lines, face_filename);
max_data_rows = min(nlines, MAX_GLYPHS);
for (int i = 0; i < max_data_rows; i++) {
if (strlen(raw_data_lines[i]) >= MIN_CHARS_IN_GLYPH) {
hershey_data_lines[i] = raw_data_lines[i];
nglyphs++;
}
}
sprintf(h, "# Font file : %s\n# Raw data lines: %u\n# Glyphs : %u\n",
face_filename, nlines, nglyphs);
cmd += h;
return nglyphs;
}
int jhf_grid(char c)
{
/* When Hershey Font glpyhs are described in the James Hurt font data format
* they use a [-49, 49] grid with 'R' as origin (i.e. corresponds to ASCII
* characters [33, 131] or '!' through ''.
* x increases to right, y increases DOWN
* The "Normal" Hershey font height is 21 with (TODO) base at 'F' and top at '['
* Convert the grid cooridnate to integer numbers
*/
return (c - 'R');
}
char check_printable_char(char c)
{
/* Ensure character is a formally printable (i.e. ASCII 32-127) or
* is in the extended range and there is a glyph for it in the jhf file
* otherwise set it to the predefined "unknown character"
*/
char print_this_char;
if (isprint(c) || ((c > 127) && (c <= glyph_count + FIRST_CHAR))) {
print_this_char = c;
}
else {
print_this_char = unknown_character;
}
return print_this_char;
}
real get_hershey_string_length(UL_TEXT Tx)
{
/* Traverse the text string and accumulate the Hershey Font width for each
* character.
*/
int char_index;
real len = 0;
char measure_char;
int left_width = 0;
int right_width = 0;
string char_path;
string text_string = Tx.value;
for (int i=0; text_string[i]; i++) {
measure_char = check_printable_char(text_string[i]);
char_index = measure_char - FIRST_CHAR; /* JHF data starts at ASCII 32 */
char_path = hershey_data_lines[char_index];
left_width = jhf_grid(char_path[8]);
right_width = jhf_grid(char_path[9]);
len += right_width - left_width;
}
return len * width_factor * Tx.size / GLYPH_SCALE_FACTOR;
}
int draw_hershey_character(int draw_char, int x_offset, int vertoffset, UL_TEXT Txt, int wirewidth)
{
/* Draws a single glyph and returns the updated x-offset value
* Inspired by Hershey Text Inkscape extension function
* def draw_svg_text(char, face, offset, vertoffset, parent):
*
* ref OpenCV http://docs.opencv.org/modules/core/doc/drawing_functions.html#initfont
*
* Normal Hershey font height is 21 with origin at centre of grid but glyphs
* are not necessarily centred on the origin
* Grid picture at http://home.comcast.net/~urbanjost/CLONE/LIBS/LIBRARY/hershey/
*/
int num_vertices;
int txt_x = Txt.x;
int txt_y = Txt.y;
int xi = 0;
int yi = 0;
int xi_delta = 0; /* Distance from local Text insertion origin */
int yi_delta = 0;
int xi_rot = 0; /* Rotated co-ordinates */
int yi_rot = 0;
real angle_radians = 0;
int left_width = 0;
int right_width = 0;
int char_index = draw_char - FIRST_CHAR; /* Data starts at ASCII 32 */
int text_size_adjusted;
string char_path;
text_size_adjusted = (Txt.size - wirewidth) / GLYPH_SCALE_FACTOR;
angle_radians = Txt.angle * PI / 180.0;
char_path = hershey_data_lines[char_index];
sprintf(h, " # Writing character: %c Path string: %s\n", draw_char, char_path);
cmd += h;
num_vertices = strtol(strsub(char_path, 5, 3)) - 1; /* Data incl LR pair */
left_width = jhf_grid(char_path[8]);
right_width = jhf_grid(char_path[9]);
if (is_make_package) {
/* Set "local text origin" to be (0 0) i.e. package insertion point */
txt_x = 0;
txt_y = 0;
}
/* Begin a wire segment */
sprintf(h, "WIRE %f ", u2mm(wirewidth));
cmd += h;
/* Parse data pairs starting at character 10 through EOL */
for (int i = 10; i < 2*num_vertices+10; i += 2) {
if (char_path[i] == ' ' && char_path[i+1] == 'R') {
/* "PEN UP" so finish current wire and start a new one */
sprintf(h, ";\nWIRE %f ", u2mm(wirewidth));
cmd += h;
}
else {
/* Get point from glyph and scale to correct size */
yi = (-jhf_grid(char_path[i+1]) + glyph_y_adjust) * text_size_adjusted + vertoffset;
xi = (jhf_grid(char_path[i]) - left_width) * width_factor * Txt.size/GLYPH_SCALE_FACTOR
+ x_offset + shear_factor * (yi - vertoffset);
/* Spin, if needed, is applied relative to local text origin */
if (is_apply_spin) {
xi = -(xi - txt_x) + txt_x + hershey_length;
yi = -(yi - txt_y) + txt_y + Txt.size;
}
/* Rotation needs local text origin as centre of rotation */
xi_delta = xi - txt_x;
yi_delta = yi - txt_y;
xi_rot = xi_delta * cos(angle_radians) - yi_delta * sin(angle_radians);
yi_rot = xi_delta * sin(angle_radians) + yi_delta * cos(angle_radians);
xi = xi_rot + txt_x;
yi = yi_rot + txt_y;
/* Mirror, if required, across a Y axis through the local text origin */
if (is_apply_mirror) {
xi = -(xi - txt_x) + txt_x ;
}
sprintf(h, "(%5.3f %5.3f) ", u2mm(int(xi)), u2mm(int(yi)));
cmd += h;
}
}
sprintf(h, ";\n"); /* Finish wire statement */
cmd += h;
x_offset += (right_width - left_width) * (width_factor * Txt.size / GLYPH_SCALE_FACTOR);
return x_offset;
}
void render_text_in_group(UL_TEXT Tx)
{
char char_to_render;
string tx_to_render = Tx.value;
int text_x = 0;
int text_y_adjusted;
int stroke_width;
is_apply_spin = ((Tx.angle > 90.0) && (Tx.angle <= 270.0) && (0 == Tx.spin));
is_apply_mirror = (Tx.mirror != 0);
hershey_length = get_hershey_string_length(Tx);
/* Adjust stroke width according to ratio or fixed user setting*/
if (is_use_ratios) {
stroke_width = int(Tx.size * Tx.ratio / 100.0);
}
else {
if (UNIT_MIL == stroke_units) {
stroke_width_user = stroke_width_user * MIL_TO_MM;
}
stroke_width = int(stroke_width_user * MM_TO_UNITS);
}
/* Update output layer, otherwise use the layer_output set by offset */
if (is_fixed_output_layer) {
layer_output = fixed_output_layer;
}
else {
layer_output = Tx.layer + layer_offset;
}
ensure_defined_layer(layer_output);
sprintf(h, "Change Layer %d;\n", layer_output);
cmd += h;
if (is_make_package) {
text_x = 0;
text_y_adjusted = stroke_width/2;
}
else {
text_x = Tx.x;
text_y_adjusted = Tx.y + stroke_width/2;
}
for (int i = 0; tx_to_render[i]; ++i) {
/* Only render printable characters OR those above ASCII 127 which have
* glyphs present in the current datafile
*/
if (isprint(tx_to_render[i])
|| ((tx_to_render[i] > 127) && (tx_to_render[i] <= glyph_count + FIRST_CHAR))) {
char_to_render = tx_to_render[i];
}
else {
char_to_render = unknown_character;
}
text_x = draw_hershey_character(int(char_to_render), text_x, text_y_adjusted,
/*Tx.size*/ Tx, stroke_width);
}
}
string make_ht_package(UL_TEXT Tx)
{
/* Open library and get package ready to write script in.
* Put rendered text at origin of package, and put copy of text in
* layer 48 Document.
* Package name example is in form ABCDEFGH-1-140317-165014.pac
* Use first 8 letters of text string with underscore replacing
* non-alphanumeric characters.
*/
string lib_pac;
string spin_mirror_cmd = "";
int time_now = time();
/* v1.4.0 changes to open library once */
// sprintf(h, "OPEN '%s';\n", library_filename);
// cmd += h;
sprintf(lib_pac, "%s-%u-%s", package_text_abbrev(Tx.value),
text_count, t2string(time_now, "yyMMdd-hhmmss"));
sprintf(h, "EDIT %s.PAC;\nGRID MM;\nSET WIRE_BEND 2;\n", lib_pac);
cmd += h;
/* Use \\n to make the description text have correct line breaks */
/* TODO: Parse the text string in case it has chars misinterpreted as html tags
* Consider request permissions for #include awtools.inc
*/
sprintf(h, "DESCRIPTION '<b>Hershey Text:</b> %s \\n"
+ "<p> \\n"
+ "Typeface: %s \\n"
+ "<br> \\n"
+ "Height: %5.3f mm \\n"
+ "<br> \\n"
+ "Ratio: %u \\n"
+ "<br> \\n"
+ "Created at: %s';\n",
Tx.value, face_filename, u2mm(Tx.size), Tx.ratio,
t2string(time_now, "yyyy-MM-dd hh:mm:ss"));
cmd += h;
/* Put a copy of the text in layer 48 Document to facilitate updates.
* V1.2.0 matches the spin, mirror and rotation of source text */
sprintf(h, "CHANGE LAYER 48;\n");
cmd += h;
sprintf(h, "CHANGE SIZE %5.3f;\n", u2mm(Tx.size));
cmd += h;
sprintf(h, "CHANGE RATIO %u;\n", Tx.ratio);
cmd += h;
sprintf(h, "CHANGE FONT VECTOR;\n");
cmd += h;
if (Tx.spin) {
spin_mirror_cmd = "S";
}
else {
spin_mirror_cmd = "";
}
if (Tx.mirror) {
spin_mirror_cmd += "M";
}
sprintf(h, "TEXT '%s' %sR%3.1f (0 0);\n", Tx.value, spin_mirror_cmd, Tx.angle);
cmd += h;
return lib_pac;
}
void insert_ht_package(string t_pac, string t_lib, UL_TEXT Tx)
{
/* Insert this new package at text origin with any rotation
* The name of this inserted Element will be autogenerated as E$..
*/
/* Library is still open from rendering the text so save and close it to
* return to board
* TODO Assuming always go back to last editing window so don't need
* to reopen the board we were working on
*/
/* v1.4.0 changes to open and close library once */
// sprintf(h, "WRITE;\nCLOSE;\n");
// cmd += h;
/* v1.2.0 change to package having 0 angle and added at same angle as source
* text to package containing rotation and inserted with zero rotation
*/
sprintf(h, "ADD '%s@%s' R0 (%5.3f %5.3f);\n",
t_pac, t_lib, u2mm(Tx.x), u2mm(Tx.y));
//cmd += h;
cmd_insert += h;
}
void process_argv(void)
{
if (argc > 1) {
face_filename = filedir(argv[0]) + "hershey-fonts/" + argv[1] + ".jhf";
}
if (argc > 2) {
/* Add use ratio for stroke width if string is "0", otherwise use this as
* the stroke width. Using 0.0 forces width of 0.0mm
*/
if ("0" == argv[2]) {
is_use_ratios = 1;
stroke_width_user = 0;
}
else {
is_use_ratios = 0;
stroke_width_user = strtod(argv[2]);
}
}
if (argc > 3) {
is_fixed_output_layer = 1;
fixed_output_layer = strtol(argv[3]);
}
if (argc >4) {
is_make_package = 0;
}
}
/* Menu - English */
int menue(void)
{
string s = "Render Hershey Text v" + version;
int d = dlgDialog(s) {
dlgHBoxLayout {
dlgSpacing(600);
}
dlgTabWidget {
dlgTabPage("S&ettings"){ /* Tab 1 */
dlgGridLayout{
dlgCell(0,0,3,4)
dlgGroup("Font rendering characteristics"){
dlgGridLayout {
dlgCell(0, 0) dlgLabel("Hershey font file:");
dlgCell(0, 1, 0, 3) dlgStringEdit(face_filename);
dlgCell(0,4) dlgPushButton("&Browse") {
face_filename = dlgFileOpen("Select the Hershey Font file to use:",
face_filename, "Hershey Font (*.jhf);; All Files (*.*)");
}
if (is_use_ratios) stroke_width_user = 0;
dlgCell(1, 0, 1, 3) dlgCheckBox("Use &ratio to calculate wire width for each text instance", is_use_ratios);
dlgCell(1, 4) {
dlgGroup("Wire Width Units") {
dlgHBoxLayout {
dlgRadioButton("&mm", stroke_units)
{stroke_width_user = stroke_width_user * MIL_TO_MM; dlgRedisplay(); }
dlgRadioButton("m&il", stroke_units)
{stroke_width_user = stroke_width_user / MIL_TO_MM; dlgRedisplay(); }
}
}
}
dlgCell(2, 0, 2, 3) dlgLabel("Wire &width (mm) <i>Ignored if ''Use ratio to calculate wire width'' is set</i>:");
dlgCell(2, 1) dlgStretch(1);
dlgCell(2, 2) dlgStretch(1);
dlgCell(2, 3) dlgStretch(1);
dlgCell(2, 4) dlgRealEdit(stroke_width_user);
dlgCell(3, 0) dlgLabel("&Slope (°)");
dlgCell(3, 4) dlgRealEdit(slope_degrees);
dlgCell(4, 0) dlgLabel("Width &Factor");
dlgCell(4, 4) dlgRealEdit(width_factor);
};
}
dlgCell(4,0,5,4)
dlgGroup("Output Layer(s)") {
dlgGridLayout{
dlgCell(5, 0) dlgRadioButton("Render text on separate layers using offse&t",
is_fixed_output_layer);
dlgCell(5, 4) dlgIntEdit(layer_offset);
dlgCell(4, 0) dlgRadioButton("Render all grouped text on this &Layer", is_fixed_output_layer);
dlgCell(4, 4) dlgIntEdit(fixed_output_layer);
dlgCell(4, 1) dlgStretch(2);
dlgCell(4, 2) dlgStretch(2);
dlgCell(4, 3) dlgStretch(2);
}
}
dlgCell(6,0,7,4)
dlgGroup("Options and Information") {
if (board) dlgCheckBox("Make a &Package in library (only available in a Board)", is_make_package);
if (package) dlgLabel("<i> Already in a Package, cannot make another</i>", is_make_package);
dlgCheckBox("&View and edit script before running", is_edit_script);
dlgCheckBox("Turn &UNDO Log off while script is running (advisable for speed reasons)", is_undo_log_off);
sprintf(s, "<i>Number of text items in group: %d</i>", text_count);
dlgLabel(s);
}
}
}
dlgTabPage("&Help") { /**Tab 2 */
dlgTextView(HelpText());
}
}
dlgHBoxLayout {
dlgStretch(3);
dlgPushButton("+&OK") dlgAccept();
dlgPushButton("-&Cancel") dlgReject();
}
};
return d;
}
void edit_script(void)
{
/* View and edit the script before executing it */
int Result = dlgDialog("Edit Script to Generate Hershey Text") {
dlgHBoxLayout {
dlgTextEdit(cmd);
dlgVBoxLayout {
dlgSpacing(300);
}
}
dlgHBoxLayout {
dlgSpacing(500);
}
dlgHBoxLayout {
dlgPushButton("+&Execute") dlgAccept();
dlgPushButton("-&Cancel") dlgReject();
dlgStretch(1);
}
};
if (Result == 0) exit(0);
}
/****************************************************************************
* Main
****************************************************************************/
/* Check some text is grouped in board or package before starting */
if (board) board(B) {
B.texts(T) {
if (ingroup(T)) text_count++;
}
B.elements(E) {
E.texts(T) {
if (ingroup(T)) text_count++;
}
}
}
else if (package) package(P) {
P.texts(T) {
if (ingroup(T)) text_count++;
}
}
else {
dlgMessageBox("!Start this ULP in a board or library package.", "OK");
exit(0);
}
if ( 0 == text_count ) {
dlgMessageBox(";No text found in group.\nPlease group the text you wish to render.", "OK");
exit(0);
}
else {
sprintf(h, "# Texts in group: %u\n", text_count);
cmd += h;
}
if (package) {
/* In Lib so set menu checkbox and variable to process text as we find it */
is_make_package = 0;
}
/* Now Display and act upon menu dialog */
process_argv();
if (argc <= 1) {
if (!menue()) exit (0);
}
shear_factor = tan(slope_degrees * PI / 180);
glyph_count = import_hershey_data();
text_count = 0;
set_default_layers_as_defined();
if (library) library(LIB) {
script_filename = filesetext(LIB.name + "_Hershey", ".scr");
output_header(LIB.name);
LIB.layers(L) update_used_layers(L);
if (package) package(P) {
P.texts(T) {
if (ingroup(T)) {
text_count++;
render_text_in_group(T);
}
}
}
}
else board(B) {
script_filename = filesetext(B.name + "_Hershey", ".scr");
output_header(B.name);
if (is_make_package) {
if (!library_filename) {
library_filename = filesetext(B.name + "_Hershey", ".lbr");
}
}
/* Add definitions of additional used layers so don't rename these */
B.layers(L) update_used_layers(L);
if (is_make_package) {
/* v1.4.0 changes to open library once */
sprintf(h, "OPEN '%s';\n", library_filename);
cmd += h;
}
B.texts(T) {
/* Choose to process only texts in board & elements selected in the group */
if (ingroup(T)) {
text_count++;
if (is_make_package) {
lib_package = make_ht_package(T);
}
render_text_in_group(T);
if (is_make_package) {
insert_ht_package(lib_package, library_filename, T);
}
}
}
B.elements(E) {
E.texts(T) {
/* Choose to process only texts in board & elements selected in the group */
if (ingroup(T)) {
text_count++;
if (is_make_package) {
lib_package = make_ht_package(T);
}
render_text_in_group(T);
if (is_make_package) {
insert_ht_package(lib_package, library_filename, T);
}
}
}
}
if (is_make_package) {
/* v1.4.0 changes to open library once and insert all packages at end */
/* Save library and switch to board */
cmd += "WRITE;\n\n"; //CLOSE;\n";
cmd += "# Switch to board and insert the library packages created earlier. \n";
sprintf(h, "EDIT '%s';\n", B.name);
cmd += h;
cmd += cmd_insert;
}
}
output_footer();
if (is_edit_script) edit_script();
output(script_filename, "wt") printf("%s", cmd);
/* exit("script '" + script_filename + "';\nREMOVE '" + script_filename + "';\n"); */
exit("script '" + script_filename + "';\n");