File Coverage

blib/lib/MARC/Moose/Formater/UnimarcToMarc21.pm
Criterion Covered Total %
statement 17 45 37.7
branch 0 22 0.0
condition 0 3 0.0
subroutine 6 8 75.0
pod 0 1 0.0
total 23 79 29.1


line stmt bran cond sub pod time code
1             package MARC::Moose::Formater::UnimarcToMarc21;
2             $MARC::Moose::Formater::UnimarcToMarc21::VERSION = '1.0.46';
3             # ABSTRACT: Convert biblio record from UNIMARC to MARC21
4 4     4   29 use Moose;
  4         10  
  4         25  
5              
6 4     4   26120 use 5.010;
  4         15  
7 4     4   24 use utf8;
  4         10  
  4         32  
8              
9             extends 'MARC::Moose::Formater';
10              
11 4     4   205 use List::Util qw/ first /;
  4         9  
  4         299  
12 4     4   27 use MARC::Moose::Field::Control;
  4         10  
  4         115  
13 4     4   22 use MARC::Moose::Field::Std;
  4         10  
  4         38974  
14              
15              
16             # Equivalence UNIMARC author type code > MARC21
17             # Each UNIMARC code points to a array ref which first entry contains MARC21
18             # code and second MARC21 author type description. The second entry isn't used
19             # yet.
20             my %authcode = map { /^(\d*) (\w*) (.*)$/; $1 => [$2, $3] } split /\n/, <<EOS;
21             005 act actor
22             010 adp adapter
23             020 ann annotator
24             030 arr arranger
25             040 art artist
26             050 asg assignee
27             060 asn associated name
28             065 auc auctioneer
29             070 aut author
30             072 aqt author in quotations or text abstract
31             075 aft author of afterword, colophon, etc.
32             080 aui author of introd
33             090 aus author of screenplay
34             100 ant bibl. antecedent
35             110 bnd binder
36             120 bdd binding designer
37             130 bkd book designer
38             140 bjd bkjacket designer
39             150 bpd bkplate designer
40             160 bsl bookseller
41             170 cll calligrapher
42             180 ctg cartographer
43             190 cns censor
44             200 chr choreographer
45             205 clb collaborator
46             210 cmm commentator
47             212 cwt commentator for written text
48             220 com compiler
49             230 cmp composer
50             240 cmt compositor
51             245 ccp conceptor
52             250 cnd conductor
53             255 csp consultant to a project
54             260 cph copyright holder
55             270 crr corrector
56             273 cur curator
57             275 dnc dancer
58             280 dte dedicatee
59             290 dto dedicator
60             295 dgg degree grantor
61             300 drt director
62             305 dis dissertant
63             310 dst distributor
64             320 dnr donor
65             330 dub dubious author
66             340 edt editor
67             350 egr engraver
68             360 etr etcher
69             365 exp expert
70             370 flm film editor
71             380 frg forger
72             390 fmo former owner
73             400 fnd funder
74             410 grt graphic technician
75             420 hnr honoree
76             430 ilu illuminator
77             440 ill illustrator
78             450 ins inscriber
79             460 ive interviewee
80             470 ivr interviewer
81             480 lbt librettist
82             490 lse licensee
83             500 lso licensor
84             510 ltg lithographer
85             520 lyr lyricist
86             530 mte metal engraver
87             540 mon monitor/contractor
88             545 mus musician
89             550 nrt narrator
90             555 opn opponent
91             557 orm organizer of meeting
92             560 org originator
93             570 oth other
94             580 ppm papermaker
95             582 pta patent applicant
96             584 inv inventor
97             587 pth patent holder
98             590 prf performer
99             595 res research
100             600 pht photographer
101             610 prt printer
102             620 pop printer of plates
103             630 pro producer
104             635 prg programmer
105             640 pfr proofreader
106             650 pbl publisher
107             651 pbd publishing director
108             660 rcp recipient
109             670 rce recording engineer
110             673 rth research team head
111             675 rev reviewer
112             677 rtm research team member
113             680 rbr rubricator
114             690 sce scenarist
115             695 sad scientific advisor
116             700 scr scribe
117             705 scl sulptor
118             710 sec secretary
119             720 sgn signer
120             721 sng singer
121             723 spn sponsor
122             725 stn standards body
123             727 ths thesis advisor
124             730 trl translator
125             740 tyd type designer
126             750 tyg typographer
127             755 voc vocalist
128             760 wde wood engraver
129             770 wam writer of accompanying material
130             EOS
131              
132             # UNIMARC 100 Type of pub
133             my %typeofpub = map { /(\w) (\w)/; $1 => $2; } split /\n/, <<EOS;
134             a c
135             b d
136             c u
137             d s
138             e r
139             f q
140             g m
141             h t
142             i p
143             j e
144             EOS
145              
146             # UNIMARC 100 Target Audience Code
147             my %target_audience = map { /(\w|\|) (\w|\|)/; $1 => $2; } split /\n/, <<EOS;
148             b a
149             c b
150             a j
151             d c
152             e d
153             k e
154             m g
155             | |
156             EOS
157              
158             # List of moved fields unchanged
159             my @unchanged;
160             push @unchanged, [$_, 500] for 300..315;
161             push @unchanged, [317, 561],
162             [320, 504],
163             [321, 500],
164             [322, 508],
165             [323, 511],
166             [324, 500],
167             [328, 502],
168             [330, 520],
169             [332, 524],
170             [333, 521],
171             [337, 538],
172             [686, '084'];
173              
174             # Tags with non-filing indicator (pos 1 or 2)
175             my $nonfiling_tags = [
176             [ qw/130 630 730 740 830/ ],
177             [ qw/240 242 243 245 440 830/ ],
178             ];
179              
180             # NSB/NSE characters
181             my $ns_characters = [
182             [ "\x08", "\x09" ],
183             [ "\x88", "\x89" ]
184             ];
185              
186              
187              
188             # Procedure 4 Title
189             sub procedure_title {
190 0     0 0   my ($self, $subf) = @_;
191              
192 0           my @sf;
193 0           my ($h_index) = (-1);
194 0           my @equivals = (
195             [ 'a', 'a' ],
196             [ 'j', 'f' ],
197             [ 'n', 'g' ],
198             [ 'h', 'n', '.' ],
199             [ 'k', 'f', '.' ],
200             [ 'l', 'k', '.' ],
201             [ 'm', 'l', '.' ],
202             [ 'q', 's', '.' ],
203             [ 'r', 'r', ',' ],
204             [ 's', 's', ',' ],
205             [ 't', 'o', ';' ],
206             [ 'u', 'r', ',' ],
207             [ 'x', 'x', ',' ],
208             );
209 0           for ( @$subf ) {
210 0           my ($letter, $value) = @$_;
211 0 0   0     if ( my $equival = first { $_->[0] eq $letter } @equivals ) {
  0            
212 0           my ($from, $to, $sep) = @$equival;
213 0 0 0       if ( $sep && @sf ) {
214 0           my $match = $sep;
215 0 0         $match = '\.' if $match eq '.';
216 0 0         if ( $sf[-1]->[0] !~ /$match$/ ) {
217 0           $sf[-1]->[1] .= $sep;
218             }
219             }
220 0           push @sf, [ $to => $value ];
221             }
222             else {
223 0           for ($letter) {
224 0 0         if ( /e/ ) {
    0          
225 0 0         next unless @sf; #FIXME warning required
226 0 0         if ( $sf[-1][0] =~ /a|n|p/ ) {
227 0           $sf[-1]->[1] .= ' :';
228 0           push @sf, [ b => $value ];
229             }
230             else {
231 0           $sf[-1]->[1] .= " : $value";
232             }
233             }
234             elsif ( /i/ ) {
235 0 0         if ( @sf ) {
236 0 0         if ( $sf[-1]->[0] eq 'h' ) {
237 0 0         $sf[-1]->[1] .= ',' if $sf[-1]->[1] !~ /,$/;
238             }
239             else {
240 0           $sf[-1]->[1] .= '.';
241             }
242             }
243 0           push @sf, [ p => $value ];
244             }
245             }
246             }
247             }
248              
249 0           return \@sf;
250             }
251              
252              
253             override 'format' => sub {
254             my ($self, $unimarc) = @_;
255              
256             my $record = MARC::Moose::Record->new();
257              
258             $record->_leader(" nam a22 7a 4500");
259              
260             my $code008 = '120130t xxu||||| |||| 00| 0 ||| d';
261              
262             my @sf040;
263              
264             # 001 => 001
265             for my $field ( $unimarc->field('001' ) ) {
266             $record->append($field->clone());
267             }
268              
269             # ISBN 010 => 020
270             for my $field ( $unimarc->field('010') ) {
271             my @sf;
272             for ( @{$field->subf} ) {
273             my ($letter, $value) = @$_;
274             for ($letter) {
275             if ( /a|z/ ) {
276             $value =~ s/-//g;
277             push @sf, [ $letter => $value ];
278             }
279             elsif ( /b/ ) {
280             $value = "($value)" unless $value =~ /^\(/;
281             if (@sf) {
282             $sf[-1]->[1] .= " $value";
283             }
284             else {
285             push @sf, [ c => $value ];
286             }
287             }
288             elsif ( /d/ ) {
289             if (@sf) {
290             $sf[-1]->[1] .= " :";
291             }
292             push @sf, [ c => $value ];
293             }
294             }
295             }
296             $record->append( MARC::Moose::Field::Std->new(
297             tag => '020', subf => \@sf ) );
298             }
299              
300             # ISSN 011 => 022
301             # Except 011$b$d => 365
302             for my $field ( $unimarc->field('011') ) {
303             my (@sf, @price);
304             for ( @{$field->subf} ) {
305             my ($letter, $value) = @$_;
306             for ($letter) {
307             if ( /a/ ) {
308             $value =~ s/-//g;
309             push @sf, [ a => $value ];
310             }
311             elsif ( /z/ ) {
312             $value =~ s/-//g;
313             push @sf, [ y => $value ];
314             }
315             elsif ( /b|d/ ) {
316             $value = "($value)" unless $value =~ /^\(/;
317             my $newlet = $letter eq 'b' ? 'b' : 'd';
318             push @price, [ $newlet => $value ];
319             }
320             }
321             }
322             $record->append( MARC::Moose::Field::Std->new(
323             tag => '022', subf => \@sf ) ) if @sf;
324             $record->append(MARC::Moose::Field::Std->new(
325             tag => '365', subf => \@price ) ) if @price;
326             }
327              
328             # EAN 076 => 024. Get only $a subfield
329             for my $field ( $unimarc->field('073') ) {
330             my $value = $field->subfield('a');
331             next unless $value;
332             $record->append( MARC::Moose::Field::Std->new(
333             tag => '024', subf => [ [ a => $value ] ] ) );
334             }
335              
336             # 100 => 008
337             if ( my $field = $unimarc->field('100') ) {
338             my $code100 = $field->subfield('a');
339             if ( $code100 && length($code100) > 20 ) {
340             # Date entered on file
341             substr $code008, 0, 6, substr($code100, 2, 6);
342              
343             # Type of publication date
344             my $value = substr($code100, 8, 1);
345             $value = $typeofpub{$value} || ' ';
346             substr $code008, 6, 1, $value;
347              
348             # Date 1
349             $value = substr($code100, 9, 4);
350             if ( 1 ) { #FIXME Determine if it's a serials
351             # Not serials
352             my $count = 0;
353             for ( split //, $value ) { $count++ if / /; }
354             $value =~ s/ /0/g if $count <= 3;
355             }
356             else {
357             # A serials
358             $value =~ s/ /u/g;
359             }
360             substr $code008, 7, 4, $value;
361              
362             # Date 2
363             $value = substr($code100, 13, 4);
364             if ( 1 ) { #FIXME Determine if it's a serials
365             # Not serials
366             my $count = 0;
367             for ( split //, $value ) { $count++ if / /; }
368             $value =~ s/ /0/g if $count <= 3;
369             }
370             else {
371             # A serials
372             $value =~ s/ /u/g;
373             }
374             substr $code008, 11, 4, $value;
375              
376             # 3 positions for target audience
377             $value = substr($code100, 17, 3);
378             for (my $i=0; $i < 3; $i++) {
379             $value = substr($code100, 17+$i, 1);
380             $value = $target_audience{$value} || ' ';
381             substr $code008, 17+$i, 1, $value;
382             }
383            
384             # Language of cataloging
385             push @sf040, [ b => substr($code100, 22, 3) ];
386              
387             # Alphabet of title, converted if serials
388             # FIXME
389             if ( 0 ) {
390             substrr $code008, 33, 1, substr($code100,34,1);
391             }
392             }
393             }
394              
395             # Language 101 => 041 and 008
396             if ( my $field = $unimarc->field('101') ) {
397             # FIXME: à virer
398             if ( ref($field) eq 'MARC::Moose::Field::Control' ) {
399             say $unimarc->as('Text');
400             exit;
401             }
402             my @all = @{$field->subf};
403             my $count_a = 0;
404             my (@sf, @sf_b);
405             for (@all) {
406             my ($letter, $value) = @$_;
407             for ($letter) {
408             if ( /a/ ) {
409             next if $count_a >= 6;
410             $count_a++;
411             if ( $count_a == 1 ) {
412             $value .= ' ';
413             $value = substr($value, 0, 3);
414             substr $code008, 35, 3, $value;
415             }
416             push @sf, [ a => $value];
417             }
418             elsif ( /c/ ) { push @sf, [ h => $value ]; }
419             elsif ( /b/ ) { push @sf_b, $value; }
420             elsif ( /d/ ) { push @sf, [ b => $value ]; }
421             elsif ( /e/ ) { push @sf, [ f => $value ]; }
422             elsif ( /f|g/ ) { }
423             elsif ( /j/ ) { push @sf, [ b => $value ]; }
424             elsif ( /h/ ) { push @sf, [ e => $value ]; }
425             elsif ( /i/ ) { push @sf, [ g => $value ]; }
426             }
427             }
428             if ( @sf_b ) {
429             for ( @sf ) {
430             if ($_->[0] eq 'h') {
431             $_->[1] .= ' ' . join(' ', @sf_b);
432             last;
433             }
434             }
435             }
436             my $ind1 = $field->ind1;
437             $ind1 = '0' if $ind1 eq ' ';
438             $ind1 = '1' if $ind1 eq '2';
439             $record->append( MARC::Moose::Field::Std->new(
440             tag => '041',
441             ind1 => $ind1,
442             subf => \@sf ) );
443             }
444             else {
445             substr($code008, 35, 3) = '|||';
446             }
447              
448             # 125 => 008
449             # FIXME: 125$b isn't handled at all
450             if ( my $field = $unimarc->field('125') ) {
451             my $value = $field->subfield('a');
452             my ($pos0, $pos1);
453             $pos0 = substr($value, 0, 1) if $value && length($value) >= 1;
454             $pos1 = substr($value, 1, 1) if $value && length($value) >= 2;
455             $pos0 ||= '|';
456             $pos0 = 'n' if $pos0 eq 'x';
457             $pos1 ||= '|';
458             $pos1 = 'n' if $pos1 eq 'x';
459             $pos1 = ' ' if $pos1 eq 'y';
460             substr($code008, 20, 2) = $pos0 . $pos1;
461             }
462              
463             $record->append( MARC::Moose::Field::Control->new(
464             tag => '008', value => $code008 ) );
465              
466             # Title
467             for my $field ( $unimarc->field('200') ) {
468             my @sf;
469             my ($a_index, $h_index) = (-1, -1);
470             SUBFIELD200:
471             for ( @{$field->subf} ) {
472             my ($letter, $value) = @$_;
473             for ($letter) {
474             if ( /a/ ) {
475             if ( $a_index == -1 ) {
476             push @sf, [ a => $value ];
477             $a_index = $#sf;
478             }
479             else {
480             $sf[$a_index]->[1] .= " ; $value";
481             }
482             }
483             elsif ( /b/) {
484             if ( $h_index == -1 ) {
485             push @sf, [ h => $value ];
486             $h_index = $#sf;
487             }
488             else {
489             if ( $#sf == $h_index ) {
490             $sf[$h_index]->[1] .= " + $value";
491             }
492             else {
493             $sf[-1]->[1] .= " ($value)";
494             }
495             }
496             }
497             elsif ( /c/ ) {
498             next SUBFIELD200 unless @sf; #FIXME warning required
499             $sf[-1]->[1] .= ". $value";
500             }
501             elsif ( /d/ ) {
502             next SUBFIELD200 unless @sf; #FIXME warning required
503             if ( $sf[-1]->[0] =~ /a|n|p/ ) {
504             $sf[-1]->[1] .= ' =';
505             $value =~ s/^= //;
506             push @sf, [ b => $value ];
507             }
508             else {
509             $sf[-1]->[1] .= " = $value";
510             }
511             }
512             elsif ( /e/ ) {
513             next SUBFIELD200 unless @sf; #FIXME warning required
514             if ( $sf[-1]->[0] =~ /a|n|p/ ) {
515             $sf[-1]->[1] .= ' :';
516             push @sf, [ b => $value ];
517             }
518             else {
519             $sf[-1]->[1] .= " : $value";
520             }
521             }
522             elsif ( /f/) {
523             next SUBFIELD200 unless @sf; #FIXME warning required
524             if ( $sf[-1]->[0] =~ /a|b|n|p/ ) {
525             $sf[-1]->[1] .= ' /';
526             push @sf, [ c => $value ];
527             }
528             else {
529             $sf[-1]->[1] .= " / $value";
530             }
531             }
532             elsif ( /g/) {
533             next SUBFIELD200 unless @sf; #FIXME warning required
534             $sf[-1]->[1] .= " ; $value";
535             }
536             elsif ( /h/ ) {
537             next SUBFIELD200 unless @sf; #FIXME warning required
538             if ( $sf[-1]->[0] =~ /a|n|p/ ) {
539             $sf[-1]->[1] .= '.';
540             push @sf, [ n => $value ];
541             }
542             else {
543             #$sf[-1]->[1] .= ". $value";
544             push @sf, [ n => $value ];
545             }
546             }
547             elsif ( /i/ ) {
548             next SUBFIELD200 unless @sf; #FIXME warning required
549             if ( @sf && $sf[-1]->[0] =~ /a|n|p/ ) {
550             $sf[-1]->[1] .= ',';
551             push @sf, [ p => $value ];
552             }
553             else {
554             $sf[-1]->[1] .= ". $value";
555             }
556             }
557             elsif ( /v|z|5|6|7/ ) { next SUBFIELD200 }
558             }
559             }
560             next unless @sf;
561             $sf[$h_index]->[1] = '[' . $sf[$h_index]->[1] . ']' unless $h_index == -1;
562             # Point final
563             if (@sf) {
564             my $last_value = $sf[-1][1];
565             my $last_char = substr($last_value, length($last_value)-1);
566             $sf[-1][1] = "$last_value." if $last_char !~ /[.?,;:]/;
567             }
568              
569             # Indicators
570             my ($ind1, $ind2) = ($field->ind1, 0);
571             for ($ind1) {
572             if ( /0/ ) { }
573             elsif ( /1/ ) {
574             #FIXME Test marc21 100/110/111/130 presence
575             $ind1 = $unimarc->field('700|710' ) ? 1 : 0;
576             }
577             else { $ind1 = 1; }
578             }
579             $record->append( MARC::Moose::Field::Std->new(
580             tag => '245', ind1 => $ind1, ind2 => $ind2,
581             subf => \@sf ) );
582             }
583            
584             # TODO 204
585              
586             # 205 => 250
587             for my $field ($unimarc->field('205') ) {
588             my @sf;
589             my ($a_index, $b_index) = (-1, -1);
590             for ( @{$field->subf} ) {
591             my ($letter, $value) = @$_;
592             for ($letter) {
593             if ( /a/ ) {
594             if ( $a_index == -1 ) {
595             push @sf, [ a => $value ];
596             $a_index = $#sf;
597             }
598             else {
599             $sf[$a_index]->[1] .= ", $value";
600             }
601             }
602             elsif ( /b/ ) {
603             if ( @sf ) {
604             $sf[-1]->[1] .= ", $value";
605             }
606             else {
607             push @sf, [ a => $value ];
608             $a_index = $#sf;
609             }
610             }
611             elsif ( /d/ ) {
612             if ( $b_index == -1 ) {
613             push @sf, [ b => $value];
614             $b_index = $#sf;
615             }
616             else {
617             $sf[-1]->[1] .= " $value";
618             }
619             }
620             elsif ( /f/ ) {
621             if ( $b_index == -1 ) {
622             $sf[-1]->[1] .= " / " if @sf;
623             push @sf, [ b => $value];
624             $b_index = $#sf;
625             }
626             else {
627             $sf[-1]->[1] .= " / $value";
628             }
629             }
630             elsif ( /g/ ) {
631             if ( @sf ) { $sf[-1]->[1] .= " / $value"; }
632             else { push @sf, [ a => $value ] }
633             }
634             }
635             }
636             next unless @sf;
637             if ( $b_index >= 1 ) {
638             my $value = $sf[$b_index]->[1];
639             if ( $value =~ /= $/ ) {
640             $value =~ s/= $//;
641             $sf[$b_index]->[1] = $value;
642             $sf[$b_index-1]->[1] .= '= ';
643             }
644             }
645             # Point final
646             $sf[-1][1] = $sf[-1][1] . '.' if @sf && $sf[-1][1] !~ /\.$/;
647             $record->append( MARC::Moose::Field::Std->new(
648             tag => '250', ind1 => $field->ind1, ind2 => $field->ind2,
649             subf => \@sf ) );
650             }
651              
652             # TODO 206
653              
654             # 207 => 362
655             for my $field ($unimarc->field('207') ) {
656             my @sf;
657             my $a_index = -1;
658             for ( @{$field->subf} ) {
659             my ($letter, $value) = @$_;
660             for ($letter) {
661             if ( /a/ ) {
662             if ( $a_index == -1 ) {
663             push @sf, [ a => $value ];
664             $a_index = $#sf;
665             }
666             else {
667             my $prev = $sf[$a_index]->[1];
668             $prev =~ s/ *$//;
669             $prev =~ s/;$//;
670             $prev =~ s/ *$//;
671             $sf[$a_index]->[1] = "$prev ; $value";
672             }
673             }
674             elsif ( /v/ ) {
675             push @sf, [ z => $value ];
676             }
677             }
678             }
679             next unless @sf;
680             # Point at the end
681             $sf[-1][1] = $sf[-1][1] . '.' if @sf && $sf[-1][1] !~ /\.$/;
682             $record->append( MARC::Moose::Field::Std->new(
683             tag => '362', ind2 => $field->ind1,
684             subf => \@sf ) );
685             }
686              
687             #TODO 208
688              
689             # 210/214 => 260
690             for my $field ( $unimarc->field('210|214') ) {
691             my @sf;
692             for ( @{$field->subf} ) {
693             my ($letter, $value) = @$_;
694             $value =~ s/^ *//, $value =~ s/ *$//;
695             my %found;
696             for ($letter) {
697             if ( /a/ ) {
698             push @sf, [ a => $value ];
699             }
700             elsif ( /b/ ) {
701             $value = "($value)" if $value !~ /^\(/;
702             if ( @sf ) {
703             $sf[-1]->[1] .= " $value";
704             }
705             else {
706             push @sf, [ a => $value ];
707             }
708             }
709             elsif ( /c/ ) {
710             push @sf, [ b => $value ];
711             }
712             elsif ( /d/ ) {
713             push @sf, [ c => $value ];
714             }
715             elsif ( /e/ ) {
716             push @sf, [ e => $value ];
717             }
718             elsif ( /f/ ) {
719             unless ( $found{$letter} ) {
720             $found{$letter} = 1;
721             $sf[-1]->[1] .= ", $value" if @sf;
722             }
723             }
724             elsif ( /g/ ) {
725             unless ( $found{$letter} ) {
726             $found{$letter} = 1;
727             push @sf, [ f => $value ];
728             }
729             }
730             elsif ( /h/ ) {
731             unless ( $found{$letter} ) {
732             $found{$letter} = 1;
733             push @sf, [ g => $value ];
734             }
735             }
736             elsif ( /j/ ) {
737             $record->append( MARC::Moose::Field::Std->new(
738             tag => '265', subf => [ a => $value ] ) );
739             }
740             elsif ( /k/ ) {
741             $record->append( MARC::Moose::Field::Std->new(
742             tag => '265', ind1 => '0', ind2 => '0',
743             subf => [ a => $value ] ) );
744             }
745             elsif ( /l/ ) {
746             $record->append( MARC::Moose::Field::Std->new(
747             tag => '265', ind1 => '1', ind2 => '0',
748             subf => [ [ a => $value ] ] ) );
749             }
750             elsif ( /m/ ) {
751             $record->append( MARC::Moose::Field::Std->new(
752             tag => '265', ind1 => '2', ind2 => '0',
753             subf => [ a => $value ] ) );
754             }
755             }
756             }
757             next unless @sf;
758             # Ponctuation
759             for (my $i=0; $i < @sf; $i++) {
760             my ($letter, $value) = @{$sf[$i]};
761             for ($letter) {
762             if ( /a/ ) {
763             $sf[$i-1]->[1] .= ' ;' if $i;
764             }
765             elsif ( /b|f/ ) {
766             $sf[$i-1]->[1] .= ' :' if $i;
767             }
768             elsif ( /c|g/ ) {
769             $sf[$i-1]->[1] .= ',' if $i;
770             }
771             }
772             $value = "($value)" if $letter =~ /e|f|g/;
773             if ( $value =~ /^= / ) {
774             $value =~ s/^= //;
775             $sf[$i-1]->[1] .= ' =' if $i;
776             }
777             $sf[$i]->[1] = $value;
778             }
779             $sf[-1][1] = $sf[-1][1] . '.' if @sf && $sf[-1][1] !~ /\.$/;
780             $record->append( MARC::Moose::Field::Std->new( tag => '260', subf => \@sf ) );
781             }
782              
783             # TODO 211 => 263
784              
785             # 215 => 300
786             for my $field ( $unimarc->field('215') ) {
787             my @sf;
788             SUBFIELD215:
789             for ( @{$field->subf} ) {
790             my ($letter, $value) = @$_;
791             $value =~ s/^ *//, $value =~ s/ *$//;
792             for ($letter) {
793             if ( /c/ ) { $letter = 'b'; }
794             elsif ( /d/ ) { $letter = 'c'; }
795             elsif ( /6|7/ ) { next SUBFIELD215; }
796             }
797             push @sf, [ $letter => $value ];
798             }
799             next unless @sf;
800             # Ponctuation
801             for (my $i=1; $i < @sf; $i++) {
802             my ($letter, $value) = @{$sf[$i]};
803             for ($letter) {
804             if ( /b/ ) { $sf[$i-1]->[1] .= ' :'; }
805             elsif ( /c/ ) { $sf[$i-1]->[1] .= ' ;'; }
806             elsif ( /e/ ) { $sf[$i-1]->[1] .= ' + '; }
807             }
808             }
809             $sf[-1][1] = $sf[-1][1] . '.' if $sf[-1][1] !~ /\.$/;
810             $record->append( MARC::Moose::Field::Std->new( tag => '300', subf => \@sf ) );
811             }
812              
813             # 225 => 490
814             for my $field ( $unimarc->field('225') ) {
815             my (@sf, @a, @vx);
816             my $prev_letter = '';
817             for ( @{$field->subf} ) {
818             my ($letter, $value) = @$_;
819             $value =~ s/^ *//, $value =~ s/ *$//;
820             $value =~ s/\x88//g, $value =~ s/\x89//;
821             for ($letter) {
822             if ( /a/ ) { push @a, $value; }
823             elsif ( /d/ ) { push @a, " = $value" }
824             elsif ( /e/ ) { push @a, " : $value" }
825             elsif ( /f/ ) { push @a, " / $value" }
826             elsif ( /h/ ) { push @a, ". $value" }
827             elsif ( /i/ ) {
828             push @a, $prev_letter eq 'h' ? ", $value " : ". $value";
829             }
830             elsif ( /v|x/ ) { push @vx, [ $letter => $value ] }
831             }
832             $prev_letter = $letter;
833             }
834             next unless @a;
835             push @sf, [ a => join('', @a) ];
836             push @sf, @vx;
837             $record->append( MARC::Moose::Field::Std->new(
838             tag => '490',
839             ind1 => $field->ind1 =~ /0|2/ ? 1 : 0,
840             subf => \@sf ) );
841             }
842              
843             # 230 => 256
844             for my $field ( $unimarc->field('230') ) {
845             $record->append($field->clone('256'));
846             }
847              
848             # Unchanged fields
849             for my $fromto ( @unchanged ) {
850             my ($from, $to) = @$fromto;
851             for my $field ( $unimarc->field($from) ) {
852             $record->append($field->clone($to));
853             }
854             }
855              
856             # 325 => 533
857             for my $field ( $unimarc->field('325') ) {
858             $record->append( MARC::Moose::Field::Std->new(
859             tag => '533',
860             subf => [ [ n => $field->subfield('a') ] ] ) );
861             }
862              
863             # 326 => 533
864             for my $field ( $unimarc->field('326') ) {
865             # FIXME Should be done depending on biblio record type:
866             # MAP, SERIALS
867             my $type = 'SERIALS';
868             my $new_field;
869             if ( $type =~ /SERIALS/ ) {
870             $new_field = $field->clone('310');
871             }
872             $record->append($new_field);
873             }
874              
875             # 327 => 505
876             for my $field ( $unimarc->field('327') ) {
877             my $ind1 = $field->ind1;
878             $ind1 = 0 if $ind1 =~ /1/;
879             $ind1 = 1 if $ind1 =~ /0/;
880             my @a = map { $_->[1] } @{$field->subf};
881             $record->append( MARC::Moose::Field::Std->new(
882             tag => '505', ind1 => $ind1,
883             subf => [ [ a => join(' ', @a) ] ] ) );
884             }
885              
886             # 329 => 505
887             # This is French (CCfr) specific field without equivalent in MARC21
888             # Concatained into 505 field
889             for my $field ( $unimarc->field('359') ) {
890             my @a = map { $_->[1] } @{$field->subf};
891             $record->append( MARC::Moose::Field::Std->new(
892             tag => '505', ind1 => '0',
893             subf => [ [ a => join(' -- ', @a) ] ] ) );
894             }
895              
896              
897             # 336 => 500
898             for my $field ( $unimarc->field('336') ) {
899             $record->append( MARC::Moose::Field::Std->new(
900             tag => '500',
901             subf => [ [ a => 'Type of computer file: ' . $field->subfield('a') ] ] ) );
902             }
903              
904             # 345 => 037
905             for my $field ( $unimarc->field('345') ) {
906             my @sf;
907             for ( @{$field->subf} ) {
908             my ($letter, $value) = @$_;
909             $letter = $letter eq 'a' ? 'b' :
910             $letter eq 'b' ? 'a' :
911             $letter eq 'c' ? 'f' :
912             $letter eq 'd' ? 'c' : $letter;
913             push @sf, [ $letter => $value ];
914             }
915             $record->append( MARC::Moose::Field::Std->new(
916             tag => '037', subf => \@sf ) );
917             }
918              
919             # TODO 410 411 421 422 423 430 431 432 433 434 435 436 437 440 441 442 443
920             # 444 445 446 447 448 451 452 453
921              
922             # 454 => 765
923             for my $ft ( (
924             [410, 760],
925             [411, 762],
926             [421, 770],
927             [422, 772],
928             [423, 777],
929             [430, 780, 0],
930             [431, 780, 1],
931             [432, 780, 2],
932             [433, 780, 3],
933             [434, 780, 5],
934             [435, 780, 6],
935             [436, 780, 4],
936             [437, 780, 7],
937             [440, 785, 0],
938             [441, 785, 1],
939             [442, 785, 2],
940             [443, 785, 3],
941             [444, 785, 4],
942             [445, 785, 5],
943             [446, 785, 6],
944             [447, 785, 7],
945             [448, 785, 8],
946             [451, 775],
947             [452, 776],
948             [453, 767],
949             [454, 765],
950             [455, 787, 8, 'Reproduction of:'],
951             [456, 787, 8, 'Reproduced as:'],
952             [461, 773],
953             [462, 774],
954             [463, 773],
955             [464, 774],
956             [470, 787, 8, 'Item reviewed:'],
957             [488, 787, 8, 'Reproduced as:'],
958             [491, 774],
959             [492, 774],
960             [493, 773],
961             [494, 773],
962             ) ) {
963             my ($from, $to, $ind2, $text) = @$ft;
964             $ind2 = ' ' unless $ind2;
965             for my $field ( $unimarc->field($from) ) {
966             my @sf;
967             push @sf, [ i => $text ] if $text;
968             for ( @{$field->subf} ) {
969             my ($letter, $value) = @$_;
970             if ( $letter eq 't') {
971             $value =~ s/\x{0088}//g;
972             $value =~ s/\x{0089}//g;
973             }
974             $letter = $letter eq '1' ? 'a' :
975             $letter eq '3' ? 'w' :
976             $letter eq 'v' ? 'g' :
977             $letter eq 'y' ? 'z' : $letter;
978             push @sf, [ $letter => $value ];
979             }
980             my $ind1 = $field->ind2 =~ /0/ ? 1 : 0;
981             $record->append( MARC::Moose::Field::Std->new(
982             tag => $to, ind1 => $ind1, ind2 =>$ind2, subf => \@sf ) );
983             }
984             }
985              
986             # 500 => 240 or 130
987             for my $field ( $unimarc->field('500|503|517|540|541') ) {
988             my ($ind1, $ind2) = ($field->ind1, $field->ind2);
989             my $tag = '240';
990             if ( $ind2 eq '0' ) {
991             $ind2 = 0;
992             }
993             elsif ( $ind2 eq '1' ) {
994             $tag = '130';
995             ($ind1, $ind2) = (0, ' ');
996             }
997             else {
998             ($ind1, $ind2) = (1, 0);
999             }
1000             $record->append( MARC::Moose::Field::Std->new(
1001             tag => $tag, ind1 => $ind1, ind2 => $ind2,
1002             subf => $self->procedure_title($field->subf) ) );
1003             }
1004              
1005             # 545 => 773, on passe t en a
1006             for my $field ( $unimarc->field('545') ) {
1007             $field->tag('773');
1008             $field->subf( [ grep { $_->[0] = 't' if $_->[0] eq 'a'; $_ } @{$field->subf} ] );
1009             $record->append( $field );
1010             }
1011              
1012             # 600 => 600
1013             # Suppr 6 et 7. f => d
1014             for my $field ( $unimarc->field('600') ) {
1015             my @names;
1016             my $date;
1017             # Skip $6 and $7
1018             my @sf;
1019             my $date_available = 0;
1020             SUBFIELD600:
1021             for ( @{$field->subf} ) {
1022             my ($letter, $value) = @$_;
1023             $value =~ s/^ *//; $value =~ s/ *$//;
1024             next unless $value;
1025             for ($letter) {
1026             if ( /6|7/ ) { next SUBFIELD600; }
1027             elsif ( /a|b/ ) { push @names, $value; next; }
1028             elsif ( /f/ ) { $date_available = 1; $letter = 'd'; }
1029             elsif ( /y/ ) { $letter = 'z'; }
1030             elsif ( /z/ ) { $letter = 'y'; }
1031             push @sf, [ $letter => $value ];
1032             }
1033             }
1034             my @sf_complete;
1035             my $notpushed = 1;
1036             for (@sf) {
1037             my ($letter, $value) = @$_;
1038             if ($letter gt 'a' && $notpushed) {
1039             push @sf_complete, [ a => join(', ', @names) . ($date_available ? ',' : '') ];
1040             $notpushed = 0;
1041             }
1042             push @sf_complete, $_;
1043             }
1044             $record->append( MARC::Moose::Field::Std->new(
1045             tag => '600', subf => \@sf_complete ) );
1046             }
1047              
1048             # 605 => 630 - 606 => 650 - 607 => 651 - 608 => 650
1049             # On conserve à leur place les lettres a x j (subdivision de forme)
1050             # On inverse y et z. et déplacée en v.
1051             # On suppr les $3
1052             for my $fromto ( ( [601, 650], [604, 600], [605, 630], [606, 650], [607, 651], [608, 650] ) ) {
1053             my ($from, $to) = @$fromto;
1054             for my $field ( $unimarc->field($from) ) {
1055             my @sf;
1056             for ( @{$field->subf} ) {
1057             my ($letter, $value) = @$_;
1058             $value =~ s/^ *//, $value =~ s/ *$//;
1059             next if $letter =~ /3/;
1060             if ( $letter eq 'j' ) {
1061             $letter = 'v';
1062             }
1063             elsif ( $letter eq 'y' ) {
1064             $letter = 'z';
1065             }
1066             elsif ( $letter eq 'z' ) {
1067             $letter = 'y';
1068             }
1069             push @sf, [ $letter => $value ];
1070             }
1071             next unless @sf;
1072             $sf[-1][1] = $sf[-1][1] . '.' if $sf[-1][1] !~ /\.$/;
1073             $record->append( MARC::Moose::Field::Std->new(
1074             tag => $to, subf => \@sf ) );
1075             }
1076             }
1077              
1078             # 675 => 080, $v and $z aren't converted
1079             for my $field ( $unimarc->field('675') ) {
1080             my @sf = grep { $_->[0] !~ /v|z/; } @{$field->subf};
1081             $record->append( MARC::Moose::Field::Std->new(
1082             tag => '080', subf => \@sf ) );
1083             }
1084              
1085             # 676 => 082, $v => $2
1086             for my $field ( $unimarc->field('676') ) {
1087             my @sf = map { $_->[0] = '2' if $_->[0] eq 'v'; $_; } @{$field->subf};
1088             $record->append( MARC::Moose::Field::Std->new(
1089             tag => '082', subf => \@sf ) );
1090             }
1091              
1092             # Les auteurs 700 => 100,
1093             # Suppr sous $3, $6 et $7 $9
1094             for my $fromto ( ( [700, 100], [701, 700], [702, 700] ) ) {
1095             my ($from, $to) = @$fromto;
1096             for my $field ( $unimarc->field($from) ) {
1097             my $ind1 = $field->ind2;
1098             my @sf;
1099             my @codes;
1100             for ( @{$field->subf} ) {
1101             my ($letter, $value) = @$_;
1102             for ($letter) {
1103             if ( /a/ ) {
1104             push @sf, [ a => $value ];
1105             }
1106             elsif ( /b/ ) {
1107             if ( @sf ) {
1108             $sf[-1]->[1] .= ", $value";
1109             }
1110             else {
1111             push @sf, [ a => $value ];
1112             }
1113             }
1114             elsif ( /c/ ) {
1115             $sf[-1]->[1] .= ',';
1116             push @sf, [ c => $value ];
1117             }
1118             elsif ( /d/ ) {
1119             push @sf, [ b => $value ];
1120             }
1121             elsif ( /f/ ) {
1122             $sf[-1]->[1] .= ',' if @sf;
1123             push @sf, [ d => $value ];
1124             }
1125             elsif ( /g/ ) {
1126             $sf[-1]->[1] .= '(';
1127             push @sf, [ q => "$value)" ];
1128             }
1129             elsif ( /4/ ) {
1130             next if $from eq '700' && $value eq '070';
1131             my $code = $authcode{$value};
1132             next unless $code;
1133             push @codes, $code->[0];
1134             }
1135             }
1136             }
1137             next unless @sf;
1138             my $value = $sf[-1]->[1];
1139             $value =~ s/ *$//;
1140             $value =~ s/\.*$//;
1141             $value .= '.' if $value !~ /[-\?]$/;
1142             $sf[-1]->[1] = $value;
1143             push @sf, [ 4 => $_ ] for @codes;
1144             $record->append( MARC::Moose::Field::Std->new(
1145             tag => $to, ind1 => $field->ind2, subf => \@sf ) );
1146             }
1147             }
1148              
1149             # Les collectivités
1150             # Suppr sous $3, $6 et $7 $9
1151             SUBFIELD_CORPORATE:
1152             for my $fromto ( ( [710, 110, 111], [711, 710, 711], [712, 710, 711] ) ) {
1153             my ($from, $to_corporate, $to_meeting) = @$fromto;
1154             for my $field ( $unimarc->field($from) ) {
1155             my @sf;
1156             my @codes;
1157             for ( @{$field->subf} ) {
1158             my ($letter, $value) = @$_;
1159             for ($letter) {
1160             if ( /a/ ) {
1161             push @sf, [ a => $value ];
1162             }
1163             elsif ( /g/ ) {
1164             $value = "($value)" unless $value =~ /^\(/;
1165             $sf[-1]->[1] .= " $value" if @sf;
1166             }
1167             elsif ( /h/ ) {
1168             $sf[-1]->[1] .= " $value";
1169             }
1170             elsif ( /g/ ) {
1171             $sf[-1]->[1] .= " ($value)";
1172             }
1173             elsif ( /b/ ) {
1174             if ( @sf ) {
1175             $sf[-1]->[1] .= '.' unless $sf[-1]->[1] =~ /\.$/;
1176             }
1177             push @sf, [ b => $value ];
1178             }
1179             elsif ( /d/ ) {
1180             $value = "($value" unless $value =~ /^\(/;
1181             push @sf, [ n => $value ];
1182             }
1183             elsif ( /e/ ) {
1184             $value = " :$value)";
1185             push @sf, [ c => $value ];
1186             }
1187             elsif ( /f/ ) {
1188             $value = $sf[-1]->[0] eq 'n'
1189             ? " :$value"
1190             : "($value" if @sf;
1191             push @sf, [ d => $value ];
1192             }
1193             elsif ( /4/ ) {
1194             next SUBFIELD_CORPORATE if $from eq '700' && $value eq '070';
1195             my $code = $authcode{$value};
1196             next SUBFIELD_CORPORATE unless $code;
1197             push @codes, $code->[0];
1198             }
1199             }
1200             }
1201             next unless @sf;
1202             my $value = $sf[-1]->[1];
1203             $value =~ s/ *$//;
1204             $value =~ s/\.*$//;
1205             $value .= '.';
1206             $sf[-1]->[1] = $value;
1207             push @sf, [ 4 => $_ ] for @codes;
1208             my $to = $field->ind1 eq '1' ? $to_meeting : $to_corporate;
1209             $record->append( MARC::Moose::Field::Std->new(
1210             tag => $to, ind1 => $field->ind2, subf => \@sf ) );
1211             }
1212             }
1213              
1214             # Populate non-filing indicator based on UNIMARC NSB/NSE
1215             {
1216             my $first = 1;
1217             for my $tags (@$nonfiling_tags) {
1218             for my $tag (@$tags) {
1219             for my $field ($record->field($tag)) {
1220             for (@{$field->subf}) {
1221             next if $_->[0] ne 'a';
1222             # Found Main title
1223             my $title = $_->[1];
1224             next unless $title;
1225             for my $ns (@$ns_characters) {
1226             my ($nsb, $nse) = @$ns;
1227             next if $title !~ /^$nsb(.*)$nse(.)/;
1228             my $len = length($1);
1229             $len++ if $2 eq ' ';
1230             $len = 0 if $len >= 10;
1231             $title =~ s/$nsb//g;
1232             $title =~ s/$nse//g;
1233             $_->[1] = $title;
1234             if ($first) { $field->ind1($len); }
1235             else { $field->ind2($len); }
1236             last;
1237             }
1238             last;
1239             }
1240             }
1241             }
1242             $first = 0;
1243             }
1244             }
1245              
1246             # Some fields are kept, as they are: 856, 801, 9xx
1247             if ( my @fields = $unimarc->field('801|856|9..') ) {
1248             $record->append(@fields)
1249             }
1250              
1251             # Clean non-filing characters in all fields
1252             for my $field (@{$record->fields}) {
1253             next if ref $field eq 'MARC::Moose::Field::Control';
1254             for (@{$field->subf} ) {
1255             next if $_->[0] !~ /[a-z0-9]/;
1256             $_->[1] =~ s/\x08|\x09//g;
1257             }
1258             }
1259              
1260             return $record;
1261             };
1262              
1263             __PACKAGE__->meta->make_immutable;
1264              
1265             1;
1266              
1267             __END__
1268              
1269             =pod
1270              
1271             =encoding UTF-8
1272              
1273             =head1 NAME
1274              
1275             MARC::Moose::Formater::UnimarcToMarc21 - Convert biblio record from UNIMARC to MARC21
1276              
1277             =head1 VERSION
1278              
1279             version 1.0.46
1280              
1281             =head1 SYNOPSYS
1282              
1283             Read a UNIMARC ISO2709 file and dump it to STDOUT in text transformed into
1284             MARC21:
1285              
1286             my $reader = MARC::Moose::Reader::File::Iso2709->new(
1287             file => 'biblio-unimarc.iso' );
1288             my $formater = MARC::Moose::Formater::UnimarcToMarc21->new();
1289             while ( my $unimarc = $reader->read() ) {
1290             my $marc21 = $formater->format($unimarc);
1291             print $marc21->as('Text');
1292             }
1293              
1294             Same with shortcut:
1295              
1296             my $reader = MARC::Moose::Reader::File::Iso2709->new(
1297             file => 'biblio-unimarc.iso' );
1298             while ( my $unimarc = $reader->read() ) {
1299             print $unimarc->as('UnimarcToMarc21')->as('Text');
1300             }
1301              
1302             Read a UNIMARC ISO2709 file and dump it to another ISO2709 file transformed
1303             into MARC21:
1304              
1305             my $reader = MARC::Moose::Reader::File::Iso2709->new(
1306             file => 'biblio-unimarc.iso' );
1307             my $writer = MARC::Moose::Writer->new(
1308             fh => IO::File->new('koha.mrc', '>:encoding(utf8)'),
1309             formater => MARC::Moose::Formater::Iso2709->new() )
1310             );
1311             my $tomarc21 = MARC::Moose::Formater::UnimarcToMarc21->new();
1312             while ( my $unimarc = $reader->read() ) {
1313             $writer->write( $tomarc21->format($unimarc) );
1314             }
1315              
1316             =head1 COMMAND LINE
1317              
1318             If you don't want to write a Perl script, you can use the L<marcmoose> command.
1319             This way, you can for example convert a ISO 2709 UNIMARC file named
1320             C<unimarc.iso> into a ISO 2709 MARC21 file named C<marc.iso>:
1321              
1322             marcmoose --parser iso2709 --formater iso2709 --converter unimarctomarc21
1323             --output marc.iso unimarc.iso
1324              
1325             =head1 AUTHOR
1326              
1327             Frédéric Demians <f.demians@tamil.fr>
1328              
1329             =head1 COPYRIGHT AND LICENSE
1330              
1331             This software is copyright (c) 2022 by Frédéric Demians.
1332              
1333             This is free software; you can redistribute it and/or modify it under
1334             the same terms as the Perl 5 programming language system itself.
1335              
1336             =cut