% -------------------------------------------------------------------------- % the SUBSTANCES package % % A Chemical Database % % -------------------------------------------------------------------------- % Clemens Niederberger % Web: https://bitbucket.org/cgnieder/substances/ % E-Mail: contact@mychemistry.eu % -------------------------------------------------------------------------- % Copyright 2012--2016 Clemens Niederberger % % This work may be distributed and/or modified under the % conditions of the LaTeX Project Public License, either version 1.3 % of this license or (at your option) any later version. % The latest version of this license is in % http://www.latex-project.org/lppl.txt % and version 1.3 or later is part of all distributions of LaTeX % version 2005/12/01 or later. % % This work has the LPPL maintenance status `maintained'. % % The Current Maintainer of this work is Clemens Niederberger. % -------------------------------------------------------------------------- % The substances package consists of the files % - substances.sty, substances-default.def, substances-examples.sub, % substances_en.tex, substances_en.pdf, README % -------------------------------------------------------------------------- % If you have any ideas, questions, suggestions or bugs to report, please % feel free to contact me. % -------------------------------------------------------------------------- \RequirePackage{ expl3 , xparse , l3keys2e , xtemplate } \ProvidesExplPackage {substances} {2016/01/07} {0.2a} {A Chemical Database} \RequirePackage{chemmacros,chemfig,ghsystem} \usechemmodule{nomenclature,units} % ---------------------------------------------------------------------------- % variables: \tl_new:N \l__substances_tmpa_tl \tl_new:N \l__substances_tmpb_tl \prop_new:N \l__substances_properties_pre_prop \prop_new:N \l__substances_properties_post_prop \seq_new:N \l__substances_required_seq \seq_new:N \l_substances_chemicals_seq \clist_new:N \l_substances_chemicals_clist \bool_new:N \l__substances_strict_bool \bool_new:N \l__substances_index_entry_bool \bool_new:N \l__substances_imakeidx_bool \bool_new:N \l__substances_multind_bool \bool_new:N \l__substances_splitidx_bool \bool_new:N \l__substances_single_name_bool \bool_new:N \l__substances_single_alt_bool \bool_set_true:N \l__substances_single_alt_bool \tl_new:N \l__substances_style_tl \tl_set:Nn \l__substances_style_tl {default} \cs_generate_variant:Nn \prop_get:NnN { c } \AtBeginDocument { \@ifpackageloaded {imakeidx} { \bool_set_true:N \l__substances_imakeidx_bool } { } \@ifpackageloaded {splitidx} { \bool_set_true:N \l__substances_splitidx_bool } { } \@ifpackageloaded {multind} { \bool_set_true:N \l__substances_multind_bool } { } } % ---------------------------------------------------------------------------- % messages: \cs_new_protected:Npn \substances_msg:nnn #1#2#3 { \bool_if:NTF \l__substances_strict_bool { \msg_error:nnnn {substances} {#1} {#2} {#3} } { \msg_warning:nnnn {substances} {#1} {#2} {#3} } } \cs_new_protected:Npn \substances_msg:nn #1#2 { \bool_if:NTF \l__substances_strict_bool { \msg_error:nnn {substances} {#1} {#2} } { \msg_warning:nnn {substances} {#1} {#2} } } \msg_new:nnnn {substances} {property-missing} { Property~`#2'~not~defined~for~substance~`#1'~\msg_line_context:. } { You~called~property~`#2'~for~substance~`#1'.~It~seems~though~that~you~ haven't~defined~it,~yet. } \msg_new:nnnn {substances} {field-missing} { Property~`#2'~is~not~defined~\msg_line_context:. } { You~called~property~`#2'~for~substance~`#1'.~This~property~has~not~been~ declared,~though.~Perhaps~you've~mispelled? } \msg_new:nnnn {substances} {database-missing} { I~ can't~ find~ the~ database~ file~ `\__substances_database:n{#1}'~ \msg_line_context: . } { You~ requested~ the~ database~ file~ `\__substances_database:n{#1}',~ but~ apparently~ it~ is~ missing.~ Maybe~ you've~ mispelled? } \msg_new:nnnn {substances} {required-field} { The~field~`#1'~is~missing~for~substance~`#2'~\msg_line_context:. } { You~declared~the~the~substance~`#2'~but~forgot~to~add~the~required~ property~`#1'. } \msg_new:nnnn {substances} {style-missing} { I~can't~find~the~file~`substances_#1.def'. } { You~specified~the~style~`#1'~which~means~you~want~me~to~load~the~file~ `substances_#1.def'~but~I~can't~find~it.~Perhaps~you've~mispelled?~ Anyway~I'm~loading~the~`default'~style~instead. } % ---------------------------------------------------------------------------- % options \keys_define:nn { substances / options } { strict .bool_set:N = \l__substances_strict_bool , draft .code:n = \bool_set_true:N \l__substances_strict_bool , final .code:n = \bool_set_false:N \l__substances_strict_bool , index .bool_set:N = \l__substances_index_entry_bool , style .tl_set:N = \l__substances_style_tl } \ProcessKeysOptions { substances / options } % ---------------------------------------------------------------------------- % define new property fields: \cs_new_protected:Npn \substances_define_property_field:nnnn #1#2#3#4 { \bool_if:nT {#1} { \seq_put_right:Nn \l__substances_required_seq {#2} } \prop_if_exist:cF { l__substances_#2_prop } { \prop_new:c { l__substances_#2_prop } } \tl_if_blank:nTF {#3} { \prop_put:Nnn \l__substances_properties_pre_prop {#2} { } } { \prop_put:Nnn \l__substances_properties_pre_prop {#2} {#3} } \tl_if_blank:nTF {#4} { \prop_put:Nnn \l__substances_properties_post_prop {#2} { } } { \prop_put:Nnn \l__substances_properties_post_prop {#2} {#4} } } \NewDocumentCommand \DeclareSubstanceProperty { smO{}O{} } { \substances_define_property_field:nnnn {#1} {#2} {#3} {#4} } \@onlypreamble\DeclareSubstanceProperty % ---------------------------------------------------------------------------- % declare new substance entry: \cs_new_protected:Npn \substances_declare_substance:nn #1#2 { \seq_put_right:Nn \l_substances_chemicals_seq {#1} \clist_put_right:Nn \l_substances_chemicals_clist {#1} \prop_map_inline:Nn \l__substances_properties_pre_prop { \tl_set:Nn \l__substances_tmpa_tl {##2} \prop_get:NnN \l__substances_properties_post_prop {##1} \l__substances_tmpb_tl \substances_add_property:nnVV {#1} {##1} \l__substances_tmpa_tl \l__substances_tmpb_tl } \keys_set:nn { substances / #1 } {#2} \seq_map_inline:Nn \l__substances_required_seq { \group_begin: \bool_set_true:N \l__substances_strict_bool \prop_if_in:cnF { l__substances_##1_prop } {#1} { \substances_msg:nnn {required-field} {##1} {#1} } \group_end: } \prop_if_in:NnTF \l__substances_sort_prop {#1} { \substances_remove_braces_set:xN { \prop_item:Nn \l__substances_sort_prop {#1} } \l__substances_tmpa_tl } { \prop_get:NnN \l__substances_properties_pre_prop {name} \l__substances_tmpa_tl \substances_remove_str_unbrace_set:VnnN \l__substances_tmpa_tl {#1} {name} \l__substances_tmpa_tl } \prop_put:NnV \l__substances_sort_prop {#1} \l__substances_tmpa_tl \prop_if_in:NnT \l__substances_alt_prop {#1} { \prop_if_in:NnTF \l__substances_altsort_prop {#1} { \substances_remove_braces_set:xN { \prop_item:Nn \l__substances_altsort_prop {#1} } \l__substances_tmpa_tl } { \prop_get:NnN \l__substances_properties_pre_prop {alt} \l__substances_tmpa_tl \substances_remove_str_unbrace_set:VnnN \l__substances_tmpa_tl {#1} {alt} \l__substances_tmpa_tl } \prop_put:NnV \l__substances_altsort_prop {#1} \l__substances_tmpa_tl } \bool_new:c { g_substances_#1_alt_index_entry_bool } \bool_new:c { g_substances_#1_name_index_entry_bool } } \cs_new_protected:Npn \substances_add_property:nnnn #1#2#3#4 { \keys_define:nn { substances / #1 } { #2 .code:n = \prop_put:cnn { l__substances_#2_prop } {#1} { #3{##1}#4 } } } \cs_generate_variant:Nn \substances_add_property:nnnn { nnVV } \NewDocumentCommand \DeclareSubstance { mm } { \substances_declare_substance:nn {#1} {#2} } \@onlypreamble\DeclareSubstance % ---------------------------------------------------------------------------- % recover values: \cs_new_protected:Npn \substances_get_property:nn #1#2 { \IfSubstanceFieldTF {#2} { \IfSubstancePropertyTF {#1} {#2} { \prop_item:cn { l__substances_#2_prop } {#1} } { {??} \substances_msg:nnn {property-missing} {#1} {#2} } } { {??} \substances_msg:nnn {field-missing} {#1} {#2} } } % ---------------------------------------------------------------------------- % some commands for creating user macros or whatever: \NewDocumentCommand \GetSubstanceProperty { mm } { \substances_get_property:nn {#1} {#2} } \DeclareExpandableDocumentCommand \RetrieveSubstanceProperty { mm } { \substances_get_property:nn {#1} {#2} } \DeclareExpandableDocumentCommand \IfSubstanceFieldTF { mmm } { \cs_if_exist:cTF { l__substances_#1_prop } {#2} {#3} } \DeclareExpandableDocumentCommand \IfSubstanceFieldT { mm } { \cs_if_exist:cT { l__substances_#1_prop } {#2} } \DeclareExpandableDocumentCommand \IfSubstanceFieldF { mm } { \cs_if_exist:cF { l__substances_#1_prop } {#2} } \DeclareExpandableDocumentCommand \IfSubstanceExistTF { mmm } { \seq_if_in:NnTF \l_substances_chemicals_seq {#1} {#2} {#3} } \DeclareExpandableDocumentCommand \IfSubstanceExistT { mm } { \seq_if_in:NnT \l_substances_chemicals_seq {#1} {#2} } \DeclareExpandableDocumentCommand \IfSubstanceExistF { mm } { \seq_if_in:NnF \l_substances_chemicals_seq {#1} {#2} } \DeclareExpandableDocumentCommand \IfSubstancePropertyTF { mmmm } { \cs_if_exist:cTF { l__substances_#2_prop } { \prop_if_in:cnTF { l__substances_#2_prop } {#1} {#3} {#4} } {#4} } \DeclareExpandableDocumentCommand \IfSubstancePropertyT { mmm } { \cs_if_exist:cT { l__substances_#2_prop } { \prop_if_in:cnT { l__substances_#2_prop } {#1} {#3} } } \DeclareExpandableDocumentCommand \IfSubstancePropertyF { mmm } { \cs_if_exist:cTF { l__substances_#2_prop } { \prop_if_in:cnF { l__substances_#2_prop } {#1} {#3} } {#3} } \DeclareExpandableDocumentCommand \ForAllSubstancesDo {} { \seq_map_inline:Nn \l_substances_chemicals_seq } \AtBeginDocument { \tl_new:N \AllSubstancesSequence \seq_map_inline:Nn \l_substances_chemicals_seq { \tl_put_right:Nn \AllSubstancesSequence { {#1} } } \cs_new_eq:NN \AllSubstancesClist \l_substances_chemicals_clist } % ---------------------------------------------------------------------------- % user command: \NewDocumentCommand \chem { soomo } { \IfNoValueF {#2} {#2} \IfNoValueTF {#5} { \IfBooleanTF {#1} { \IfSubstancePropertyTF {#4} {alt} { \RetrieveSubstanceProperty {#4} {alt} } { \RetrieveSubstanceProperty {#4} {name} } } { \RetrieveSubstanceProperty {#4} {name} } } { \RetrieveSubstanceProperty {#4} {#5} } \IfNoValueF {#3} {#3} \SubstanceIndex {#4} } % ---------------------------------------------------------------------------- % index command to add an entry to the chemicals list if the option % `index=true' is used: \cs_new_protected:Npn \substances_index:nn #1#2 { \bool_if:NTF \l__substances_imakeidx_bool { \index [ #1 ] {#2} } { \bool_if:NTF \l__substances_splitidx_bool { \sindex [ #1 ] {#2} } { \bool_if:NTF \l__substances_multind_bool { \index {#1} {#2} } { \index {#2} } } } } \cs_generate_variant:Nn \substances_index:nn { no,nx } \cs_new_protected:Npn \substances_index_entry:nn #1#2 { \UseInstance {substances-index} {#1} {#2} } \NewDocumentCommand \SubstanceIndex {O{default}m} { \bool_if:NT \l__substances_index_entry_bool { \substances_index_entry:nn {#1} {#2} } } \cs_new_protected:Npn \substances_remove_braces_set:nN #1#2 { \tl_set:Nn #2 #1 } \cs_generate_variant:Nn \substances_remove_braces_set:nN {x} \cs_new_protected:Npn \substances_remove_str_unbrace_set:nnnN #1#2#3#4 { \prop_get:cnN { l__substances_#3_prop } {#2} #4 \tl_remove_all:Nn #4 {#1} \substances_remove_braces_set:xN {#4} #4 } \cs_generate_variant:Nn \substances_remove_str_unbrace_set:nnnN { V } % let's use xtemplate's features for possible customization later: \DeclareObjectType {substances-index} {1} \DeclareTemplateInterface {substances-index} {standard} {1} { alternative-entry : boolean = true , alternative-name : boolean = true } \DeclareTemplateCode {substances-index} {standard} {1} { alternative-entry = \l__substances_index_alternative_entry_bool , alternative-name = \l__substances_index_alternative_name_bool , } { \AssignTemplateKeys \bool_if:nTF { \l__substances_index_alternative_name_bool && \prop_if_in_p:Nn \l__substances_alt_prop {#1} } { \bool_if:cF { g_substances_#1_name_index_entry_bool } { \substances_index:nx { \c_job_name_tl -chem } { \SubstanceIndexNameAltEntry { \prop_item:Nn \l__substances_sort_prop {#1} } { \GetSubstanceProperty {#1} {name} } { \GetSubstanceProperty {#1} {alt} } } } \bool_if:NT \l__substances_single_name_bool { \bool_gset_true:c { g_substances_#1_name_index_entry_bool } } } { \bool_if:cF { g_substances_#1_name_index_entry_bool } { \substances_index:nx { \c_job_name_tl -chem } { \SubstanceIndexNameEntry { \prop_item:Nn \l__substances_sort_prop {#1} } { \GetSubstanceProperty {#1} {name} } } } \bool_if:NT \l__substances_single_name_bool { \bool_gset_true:c { g_substances_#1_name_index_entry_bool } } } \bool_if:nT { \l__substances_index_alternative_entry_bool && \l__substances_index_alternative_name_bool && \prop_if_in_p:Nn \l__substances_alt_prop {#1} } { \bool_if:cF { g_substances_#1_alt_index_entry_bool } { \substances_index:nx { \c_job_name_tl -chem } { \SubstanceIndexAltEntry { \prop_item:Nn \l__substances_altsort_prop {#1} } { \GetSubstanceProperty {#1} {name} } { \GetSubstanceProperty {#1} {alt} } } \substances_contains_see:NT \SubstanceIndexAltEntry { \bool_gset_true:c { g_substances_#1_alt_index_entry_bool } } } } } \DeclareInstance {substances-index} {default} {standard} { } \cs_new:Npn \SubstanceIndexNameEntry #1#2 { #1@#2 } \cs_new:Npn \SubstanceIndexNameAltEntry #1#2#3 { #1@#2~(#3) } \cs_new:Npn \SubstanceIndexAltEntry #1#2#3 { #1@#3|see{#2} } \cs_new_protected:Npn \substances_contains_see:NT #1#2 { \tl_set_rescan:Nnx \l__substances_tmpa_tl {} {\cs_meaning:N #1 } \tl_if_in:VnT \l__substances_tmpa_tl { |see } {#2} } % ---------------------------------------------------------------------------- % define some default fields: \cs_new_nopar:Npn \@CAS #1-#2-#3\relax { \iupac{#1-#2-#3} } \NewDocumentCommand \CAS { m } { \@CAS #1 \relax } \DeclareSubstanceProperty* {name} [\iupac] \DeclareSubstanceProperty {sort} \DeclareSubstanceProperty {alt} [\iupac] \DeclareSubstanceProperty {altsort} \DeclareSubstanceProperty {CAS} [\CAS] \DeclareSubstanceProperty {PubChem} % ---------------------------------------------------------------------------- % load style file \tl_const:Nn \c__substances_style_extension_tl {def} \tl_const:Nn \c__substances_style_prefix_tl {substances-} \cs_new:Npn \__substances_style:n #1 { \c__substances_style_prefix_tl#1.\c__substances_style_extension_tl } \cs_new_protected:Npn \substances_load_style:n #1 { \tl_set:Nx \l__substances_tmpa_tl { \tl_trim_spaces:n {#1} } \__substances_load_style:V \l__substances_tmpa_tl } \cs_generate_variant:Nn \substances_load_style:n { V } \cs_new_protected:Npn \__substances_load_style:n #1 { \substances_if_style_exist:nTF {#1} { % \msg_log:nnn {substances} {loading-style} {#1} \@onefilewithoptions {\c__substances_style_prefix_tl#1}[][] \c__substances_style_extension_tl } { \substances_msg:nn {style-missing} {#1} } } \cs_generate_variant:Nn \__substances_load_style:n { V } \prg_new_conditional:Npnn \substances_if_style_exist:n #1 {T,F,TF} { \file_if_exist:nTF { \__substances_style:n {#1} } { \prg_return_true: } { \prg_return_false: } } \cs_new_protected:Npn \__substances_style:nn #1#2 { \ProvidesFile { \__substances_style:n {#2} } \bool_if:nT {#1} { \ExplSyntaxOn } } \bool_new:N \l__substances_inside_style_file_bool \NewDocumentCommand \SubstancesStyle {sm} { \__substances_style:nn {#1} {#2} \bool_set_true:N \l__substances_inside_style_file_bool \AtEndOfPackage { \bool_set_false:N \l__substances_inside_style_file_bool } } \NewDocumentCommand \LoadSubstancesStyle {m} { \bool_if:NTF \l__substances_inside_style_file_bool { \substances_load_style:n {#1} } {} } \substances_load_style:V \l__substances_style_tl % ---------------------------------------------------------------------------- % load database file \tl_const:Nn \c__substances_database_extension_tl {sub} \tl_const:Nn \c__substances_database_prefix_tl {} \cs_new:Npn \__substances_database:n #1 { \c__substances_database_prefix_tl#1.\c__substances_database_extension_tl } \prg_new_conditional:Npnn \substances_if_database_exist:n #1 {T,F,TF} { \file_if_exist:nTF { \__substances_database:n {#1} } { \prg_return_true: } { \prg_return_false: } } \cs_new_protected:Npn \__substances_load_database:n #1 { \substances_if_database_exist:nTF {#1} { \@onefilewithoptions {\c__substances_database_prefix_tl#1}[][] \c__substances_database_extension_tl } { \substances_msg:nn {database-missing} {#1} } } \cs_generate_variant:Nn \__substances_load_database:n { V } \cs_new_protected:Npn \substances_load_database:n #1 { \tl_set:Nx \l__substances_tmpa_tl { \tl_trim_spaces:n {#1} } \__substances_load_database:V \l__substances_tmpa_tl } \NewDocumentCommand \LoadSubstances {m} { \substances_load_database:n {#1} } \@onlypreamble\LoadSubstances \cs_new_protected:Npn \__substances_database:nn #1#2 { \ProvidesFile { \__substances_database:n {#2} } \bool_if:nF {#1} { } } \NewDocumentCommand \SubstancesDatabase {sm} { \__substances_database:nn {#1} {#2} } \tex_endinput:D % ---------------------------------------------------------------------------- % HISTORY: 2012/07/22 v0.1 - first release to CTAN 2012/09/02 v0.1a - small fix due to updated l3kernel 2015/10/21 v0.2 - maintenance: minor fixes, adapt to chemmacros v5 - change implementation of how style files are loaded - change implementation of how database files are loaded 2016/01/07 v0.2a - \prop_get:Nn => \prop_item:Nn % TODO: